入门

We recommend you developing the kernel in Debian-12.0+ or Ubuntu-24.04+ environment to get the best tooling support.

准备基本的开发环境

repo

我们使用repo来管理内核项目。请按照https://mirrors.tuna.tsinghua.edu.cn/help/git-repo/安装repo

gn

我们使用GN来组织和构建BlueOS项目,而不是Rust生态系统的官方包管理器cargogn提供比cargo更好的多语言支持和更快的构建速度。你可以从https://gn.googlesource.com/gn/#getting-a-binary下载预构建的gn二进制文件。将下载的二进制文件放入一个目录,并确保该目录在你的${PATH}中。

在Linux上安装包

安装由发行版提供的软件包。

sudo apt install build-essential cmake ninja-build pkg-config \
                 libssl-dev gdb-multiarch curl git wget \
                 libslirp-dev python3 python3-pip meson \
                 libglib2.0-dev flex bison libfdt-dev \
                 gcc-riscv64-unknown-elf clang llvm lld \
                 python3-kconfiglib python3-tomli

此外,下载并安装arm toolchains

wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz
tar xvf arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz -C <install-path>
wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf.tar.xz
tar xvf arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf.tar.xz -C <install-path>

<install-path>/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf/bin<install-path>/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi/bin 添加到你的 $PATH 中。

Build and install QEMU on Linux

下载QEMU源代码tarball,

wget https://download.qemu.org/qemu-10.0.2.tar.xz
tar xvf qemu-10.0.2.tar.xz
cd qemu-10.0.2
mkdir build && cd build
../configure --prefix=<install-path> --enable-slirp && \
    make -j$(nproc) install

<install-path>/bin 添加到你的 $PATH 中。

在macOS上安装包

brew install coreutils llvm@19 lld@19 gcc-arm-embedded cmake ninja qemu
brew tap riscv-software-src/riscv
brew install riscv-tools riscv64-elf-gcc riscv64-elf-binutils riscv64-elf-gdb
python3 -m pip install --user --break-system-packages --upgrade kconfiglib

For aarch64 toolchain, please refer to arm-gnu-toolchain. It’s recommended to download tarballs rather than pkgs. For RISC-V toolchain on macOS, please refer to homebrew-riscv.

代码格式化工具

我们使用相应编程语言的代码格式化工具来保持代码风格一致。这些格式化工具可以通过

# 在Linux上sudo apt install clang-format yapf3
# 在 macOS 上brew install clang-format yapf

以下是格式命令及其对应编程语言的表格。

语言格式化命令
Rustrustfmt
C/C++clang-format
Pythonyapf3
GNgn format

初始化和同步项目

使用以下命令初始化项目。

mkdir blueos-dev
cd blueos-dev

If you have configured public ssh key on github, please use following commands

repo init -u git@github.com:vivoblueos/manifests.git -b main -m manifest.xml

otherwise, please try

repo init -u https://github.com/vivoblueos/manifests.git -b main -m manifest.xml

然后同步项目中的所有仓库。

repo sync

您可以通过在上面的命令后添加-j$(nproc)来加速同步。

Building the Rust toolchain for BlueOS kernel

We have forked upstream Rust compiler to support BlueOS kernel targeted to *-vivo-blueos-* and BlueOS kernel’s Rust-std.

我们最终将我们的更改贡献给上游仓库,并使*-vivo-blueos-*成为Rust的tier目标。

克隆下游仓库

Run If you have configured public ssh key on github, please use following commands,

git clone git@github.com:vivoblueos/rust.git  
git clone git@github.com:vivoblueos/cc-rs.git  
git clone git@github.com:vivoblueos/libc.git

otherwise, please try

git clone https://github.com/vivoblueos/rust.git  
git clone https://github.com/vivoblueos/cc-rs.git  
git clone https://github.com/vivoblueos/libc.git 

blueos-dev 分支已设置为默认,因此无需手动切换分支。

设置Rust镜像站点

在中国,我们推荐你使用 crates.iorustup 的镜像站点。将以下行添加到你的 ~/.bashrc

export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup

然后输入

source ~/.bashrc

通过 x 脚本安装

在您的bash shell中运行以下命令。这些指令适用于Linux和macOS平台:

export CARGO_NET_GIT_FETCH_WITH_CLI=true
export DESTDIR=<选择您的安装前缀>
cd rust
cp config.blueos.toml config.toml
./x.py install -i --stage 1 compiler/rustc
./x.py install -i --stage 1 library/std --target aarch64-vivo-blueos-newlib
./x.py install -i --stage 1 library/std --target thumbv7m-vivo-blueos-newlibeabi
./x.py install -i --stage 1 library/std --target thumbv8m.main-vivo-blueos-newlibeabihf
./x.py install -i --stage 1 library/std --target riscv64-vivo-blueos
./x.py install -i --stage 1 library/std --target riscv32-vivo-blueos
./x.py install -i --stage 1 library/std --target riscv32imc-vivo-blueos
./x.py install -i --stage 0 rustfmt
./x.py install -i --stage 0 rust-analyzer
./x.py install -i --stage 0 clippy

您还必须安装主机的标准库和LLVM工具。

对于Linux:

./x.py install -i --stage 1 library/std --target x86_64-unknown-linux-gnu
cp -rav build/x86_64-unknown-linux-gnu/llvm/{bin,lib} ${DESTDIR}/usr/local

对于macOS:

./x.py install -i --stage 1 library/std --target aarch64-apple-darwin
cp -av build/aarch64-apple-darwin/llvm/{bin,lib} ${DESTDIR}/usr/local

要使用内核工具链,请将以下内容添加到您的环境中:

export PATH=${DESTDIR}/usr/local/bin:${PATH}

Or if you want to mange the blueos toolchain using rustup, you can try:

ln -s ${DESTDIR}/usr/local ~/.rustup/toolchains/blueos-dev
rustup default blueos-dev

构建内核镜像

内核目前支持多种板子。

  • qemu_mps2_an385
  • qemu_mps3_an547
  • qemu_virt64_aarch64
  • qemu_riscv64

为指定板(如 qemu_mps2_an385)构建内核镜像,使用命令

gn gen out/qemu_mps2_an385.release/ --args='build_type="release" board="qemu_mps2_an385"'
ninja -C out/qemu_mps2_an385.release

要运行测试,输入

ninja -C out/qemu_mps2_an385.release check_all

参数及其语义。

参数语义
构建类型构建使用的配置
开发板目标开发板名称

在VSCode中使用rust-analyzer

We recommend you to use rust-analyzer extension in VSCode to support development of BlueOS kernel.

然而,由于我们使用gn而非Cargo.toml来管理项目,VSCode中的rust-analyzer扩展可能无法开箱即用。你应该使用以下命令1来使扩展正常工作

gn gen out/qemu_mps2_an385 --export-rust-project
ln -sfn out/qemu_mps2_an385/rust-project.json

  1. https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/rust.md#使用-vscode

QEMU检查器

大多数内核代码通过QEMU进行测试。我们引入QEMU检查器以协助测试内核。

QEMU检查器必须提供一个QEMU运行脚本和一个包含检查指令的输入文件。QEMU检查器运行该脚本并捕获其输出行。如果输出行匹配检查指令中指定的条件或正则表达式,检查器将采取相应操作。指令应写在输入文件的头部。

指令动作
// 检查-失败: <regex>记录此行并在退出时报告。
// 检查-成功: <regex>记录此行并在退出时报告。
// 断言失败:<regex>退出 runner 并报告失败。
// 断言-成功: <regex>退出 runner 并报告成功。
// 新建行超时: <数字>检查器读取下一行时超时。如果发生超时,报告失败。
// 总超时: <数字>本次运行的总超时时间。

例子

假设我们有一个用于测试的内核镜像,名为blueos_foo_test。首先我们需要为它生成一个qemu运行脚本。

gen_qemu_runner("runner_for_blueos_foo_test") {
  img = ":blueos_foo_test"
  qemu = "$qemu_exe"
  board = "$board"
}

然后,我们为上述runner制作一个checker。

run_qemu_checker("check_blueos_foo_test") {
  img = ":blueos_foo_test"
  runner = ":runner_for_blueos_foo_test"
  checker = "//kernel/kernel/tests/integration_test.rs"
}

必须指出,上述两个目标必须放在同一个 BUILD.gn 中。检查指令应放在 integration_test.rs 的头部。

// NEWLINE-TIMEOUT: 15  
// ASSERT-SUCC: Kernel test end.
// ASSERT-FAIL: Backtrace in Panic.*

在CI中集成检查

CI runner 仅运行两个顶层目标,defaultcheck_all。要在 CI 期间运行检查,你必须将你的检查器目标放入顶层 BUILD.gncheck_all 组的 deps 里。

group("check_all") {
  deps = [ ":check_kernel" ]
}

运行覆盖率测试

我们在编译时通过添加-Cinstrument-coverage来生成覆盖率统计所需的代码,并通过集成minicovsemihosting来生成覆盖率数据。最后,我们使用grcov工具生成可读的HTML格式覆盖率数据。所有这些都已集成到我们的构建系统中,可以使用以下命令生成覆盖率数据:

gn gen out/qemu_riscv64.cov/ --args=build_type="coverage" board="qemu_riscv64"
ninja -C out/qemu_riscv64.cov check_coverage

如果你收到提示说grcov未找到,可以通过

cargo install grcov

构建并运行后,可以在 ./out/qemu_riscv64.cov/cov_report 目录中找到合并的覆盖率报告。打开该目录中的 index.html 文件以查看覆盖率数据。

Run shell in QEMU

We have implemented a simple shell to demonstrate the functionality of the BlueOS kernel. First let’s build the shell executable.

gn gen out/shell_test --args='board="qemu_mps2_an385" build_type="release"'
ninja -C out/shell_test shell_runner

The ninja will output a line at last, which is like

Generated /build/vivoblueos/out/shell_test/bin/shell_runner-qemu.sh

Now we can run the shell via the generated script. If everything is going smooth, we can see

Hello, shell!
>

Please type help for playing with this shell.

添加一个新的syscall

在内核中添加一个新的syscall很容易。

有3个步骤。

  1. Add new syscall number for the new syscall in header/src/lib.rs.
pub enum NR {
    Read,
}
  1. kernel/src/syscall_handlers/mod.rs中实现新的syscall。

这通常通过define_syscall_handler!完成,如果系统调用是简单的。例如

define_syscall_handler!(
read(fd: i32, buf: *mut i8, len: usize) -> c_long {
    // 实现你的 vfs 读取。});
  1. kernel/src/syscall_handlers/mod.rs中注册新的syscall。

syscall_table! 中添加一个新条目。例如,

(Read, read),

必须注意,第一个操作数必须NR 枚举的非限定名称。

调用一个syscall

BlueOS kernel offers two modes of invoking syscall.

  1. 软件中断

这通常见于大多数OS内核中,用于从用户空间切换到内核空间。

  1. 直接调用

在此模式下,syscall处理程序通过函数调用直接调用。

在两种模式下,如果需要调用syscall,请使用blueos_scal crate中的bk_syscall!宏。例如,

use blueos_scal::bk_syscall;
use blueos_header::syscalls::NR::{Read};

fn read_something(fd: i32, buf: *mut i8, len: usize) -> i32 {
    bk_syscall!(Read, buf, len) as i32
}

切换到另一种模式时无需更改代码。默认使用SWI模式。如需使用直接调用模式,只需在构建内核时传入--cfg direct_syscall_handler即可。

内核的基本数据类型

为内核实现了一个定制的Arc(infra/src/tinyarc.rs)。与alloc::sync::Arc相比,差异不大,只是它仅有strong_count因而减小了体积,对嵌入式设备更友好。此外其内存布局对blueos_infra是已知的,因此我们的侵入式列表可以轻松与之协作。

侵入式列表

blueos_infra的ilist(infra/src/list/typed_ilist.rs)是类型化且不安全的。它类似于C风格的ilist,但我们建议开发者不要直接使用它,而是使用智能指针。我们在上述提到的Arc基础上实现了ArcList,并保证了安全性。

Submit pull requests

As mentioned before, we are using repo to manage the project, sometimes developer’s change might be involved in more than one repositories. We recommend developers to use following workflow to submit PRs.

Assume a developer has to change code in kernel and build repositories. The developer first has to fork the both repositories. Then add new remote for the both repositories. For example

git remote add <user> git@github.com:<user>/kernel
git checkout -b feature-x
# Change code and commit.
git push <user>

Then create a new PR for the kernel repository.

Do the same to the build repository.

To trigger a build in CI, the developer has to issue following comment on the page of one of the PRs.

build_prs <url-of-the-kernel-pr> <url-of-the-build-pr>