arceos-userprivilege
A standalone monolithic kernel application running on ArceOS, demonstrating user-privilege mode execution: loading a minimal user-space binary, switching the CPU to unprivileged mode, and handling syscalls when the user program requests kernel services. All dependencies are sourced from crates.io. Supports four processor architectures.
What It Does
This application demonstrates the fundamental OS mechanism of privilege separation -- running code in unprivileged (user) mode and trapping back to the kernel on syscalls:
- Address space creation (
main.rs): Creates an isolated user address space withAddrSpace::new_empty(), then copies the kernel page table entries so kernel code remains accessible during traps. - Binary loading (
loader.rs): Reads a raw binary (/sbin/origin) from a FAT32 virtual disk and copies it to a fixed user-space address (0x1000). - User stack allocation (
main.rs): Allocates a 64 KiB user stack at the top of the user address space withSharedPagesbackend. - User-mode execution (
task.rs): Spawns a kernel task that creates aUserContext, switches to the user page table, and enters user mode viaUserContext::run(). A trap dispatch loop handlesReturnReason::Syscalland other events. - Syscall handling (
syscall.rs): InterceptsSYS_EXIT(syscall 93) from user space, prints a message, and terminates the task with the provided exit code.
The User-Space Payload
The payload is a minimal no_std Rust binary that simply invokes the SYS_EXIT syscall with exit code 0:
unsafe extern "C" !
Each architecture uses its own inline assembly to issue the syscall (ecall on riscv64, svc #0 on aarch64, syscall on x86_64, syscall 0 on loongarch64). The payload is compiled for the target bare-metal architecture, converted to a raw binary with rust-objcopy, and packaged into a FAT32 disk image as /sbin/origin.
Relationship to other crates in this series
| Crate | Key Feature | Builds On |
|---|---|---|
arceos-userprivilege (this) |
User/kernel privilege separation, basic syscall | -- |
arceos-lazymapping |
Demand paging (lazy page fault handling) | userprivilege |
arceos-runlinuxapp |
Run real Linux ELF binaries (musl libc) | lazymapping |
Supported Architectures
| Architecture | Rust Target | QEMU Machine | Platform |
|---|---|---|---|
| riscv64 | riscv64gc-unknown-none-elf |
qemu-system-riscv64 -machine virt |
riscv64-qemu-virt |
| aarch64 | aarch64-unknown-none-softfloat |
qemu-system-aarch64 -machine virt |
aarch64-qemu-virt |
| x86_64 | x86_64-unknown-none |
qemu-system-x86_64 -machine q35 |
x86-pc |
| loongarch64 | loongarch64-unknown-none |
qemu-system-loongarch64 -machine virt |
loongarch64-qemu-virt |
Prerequisites
1. Rust nightly toolchain (edition 2024)
2. Bare-metal targets (install the ones you need)
3. rust-objcopy (from cargo-binutils, required for non-x86_64 targets)
4. QEMU (install the emulators for your target architectures)
# Ubuntu 24.04
# macOS (Homebrew)
Note: On Ubuntu,
qemu-system-aarch64is provided byqemu-system-arm, andqemu-system-loongarch64is provided byqemu-system-misc.
Summary of required packages (Ubuntu 24.04)
# All-in-one install for Ubuntu 24.04
# Rust toolchain
|
&&
Quick Start
# Install cargo-clone sub-command
# Get source code of arceos-userprivilege crate from crates.io
# Enter crate directory
# Build and run on RISC-V 64 QEMU (default)
# Build and run on other architectures
# Build only (no QEMU)
What cargo xtask run does
The xtask command automates the full workflow:
- Install config -- copies
configs/<arch>.tomlto.axconfig.toml - Build payload -- compiles
payload/Rust crate for the bare-metal target, thenrust-objcopyconverts the ELF to a raw binary - Create disk image -- builds a 64 MB FAT32 image containing
/sbin/origin - Build kernel --
cargo build --release --target <target> --features axstd - Objcopy -- converts kernel ELF to raw binary (non-x86_64 only)
- Run QEMU -- launches the emulator with VirtIO block device attached
Expected output
handle_syscall ...
[SYS_EXIT]: process is exiting ..
monolithic kernel exit [0] normally!
QEMU will automatically exit after the kernel prints the final message.
Project Structure
app-userprivilege/
├── .cargo/
│ └── config.toml # cargo xtask alias & AX_CONFIG_PATH
├── xtask/
│ └── src/
│ └── main.rs # Build/run tool: payload compilation, disk image, QEMU
├── configs/
│ ├── riscv64.toml # Platform config (MMIO, memory layout, etc.)
│ ├── aarch64.toml
│ ├── x86_64.toml
│ └── loongarch64.toml
├── payload/
│ ├── Cargo.toml # Minimal no_std binary crate
│ ├── linker.ld # Linker script (entry at 0x1000)
│ └── src/
│ └── main.rs # User-space: SYS_EXIT(0) via inline assembly
├── src/
│ ├── main.rs # Kernel entry: create address space, load app, spawn task
│ ├── loader.rs # Raw binary loader (read from FAT32, copy to 0x1000)
│ ├── syscall.rs # Syscall handler (SYS_EXIT only)
│ └── task.rs # User task spawning & trap dispatch loop
├── build.rs # Linker script path setup (auto-detects arch)
├── Cargo.toml # Dependencies from crates.io
├── rust-toolchain.toml # Nightly toolchain & bare-metal targets
└── README.md
Key Components
| Component | Role |
|---|---|
axstd |
ArceOS standard library (replaces Rust's std in no_std environment) |
axhal |
Hardware Abstraction Layer -- UserContext, ReturnReason, trap handling |
axmm |
Memory management -- user address spaces, page mapping with SharedPages backend |
axtask |
Task scheduler -- kernel task spawning, CFS scheduling, context switching |
axfs / axfeat |
Filesystem -- FAT32 virtual disk access for loading the user binary |
axio |
I/O traits (Read) for file operations |
axlog |
Kernel logging (ax_println!) |
memory_addr |
Virtual/physical address types and alignment utilities |
How the Privilege Transition Works
Kernel (supervisor/ring 0)
1. Create AddrSpace::new_empty()
2. copy_mappings_from(kernel_aspace)
3. Load /sbin/origin at VA 0x1000
4. Map user stack
5. UserContext::new(entry=0x1000, sp=stack_top, 0)
6. uctx.run() ─────────────────────────────┐
│
8. ReturnReason::Syscall ◄───────┤
9. handle_syscall -> SYS_EXIT(0) │
10. axtask::exit(0) │
▼
┌─────────────────────────┐
│ User mode (ring 3) │
│ │
│ 7. ecall / svc / │
│ syscall │
└─────────────────────────┘
License
GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0