merge from upstream

This commit is contained in:
luchangcheng2333 2022-05-21 21:46:10 +08:00
commit d53432fab7
71 changed files with 1368 additions and 897 deletions

View File

@ -1,9 +1,16 @@
[alias]
xtask = "run --package xtask --"
xtask = "run --package xtask --release --"
git-proxy = "xtask git-proxy"
setup = "xtask setup"
update-all = "xtask update-all"
check-style = "xtask check-style"
rootfs = "xtask rootfs"
libc-test = "xtask libc-test"
other-test = "xtask other-test"
image = "xtask image"
asm = "xtask asm"
qemu = "xtask qemu"
gdb = "xtask gdb"

13
.github/scripts/add-doc-index.sh vendored Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
cat > target/doc/index.html << EOF
<html>
<head>
<meta http-equiv="refresh" content="0;URL=kernel_hal/index.html">
<title>Redirection</title>
</head>
<body onload="window.location = 'kernel_hal/index.html'">
<p>Redirecting to <a href="kernel_hal/index.html">kernel_hal/index.html</a>...</p>
</body>
</html>
EOF

5
.github/scripts/install-deps.sh vendored Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
sudo apt-get update
sudo apt-get install -y $@
pip3 install -r tests/requirements.txt

7
.github/scripts/install-qemu.sh vendored Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
wget https://download.qemu.org/qemu-$1.tar.xz
tar -xJf qemu-$1.tar.xz
cd qemu-$1
./configure --target-list=x86_64-softmmu,riscv64-softmmu
make -j$nproc > /dev/null 2>&1

View File

@ -6,15 +6,19 @@ on:
schedule:
- cron: '0 22 * * *' # every day at 22:00 UTC
env:
rust_toolchain: nightly-2022-01-20
jobs:
workspace:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
override: true
components: rust-src, rustfmt, clippy
@ -23,16 +27,19 @@ jobs:
with:
command: fmt
args: --all -- --check
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --all-features
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-features
- name: Build docs
uses: actions-rs/cargo@v1
with:
@ -43,12 +50,14 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
override: true
target: aarch64-unknown-linux-gnu
- uses: actions-rs/cargo@v1
with:
command: build
@ -56,20 +65,19 @@ jobs:
args: --target aarch64-unknown-linux-gnu --workspace --exclude linux-syscall --exclude zcore-loader --exclude zcore
build-user:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/zircon/x64/libc.so,prebuilt/zircon/x64/libfdio.so,prebuilt/zircon/x64/libunwind.so,prebuilt/zircon/x64/libzircon.so,prebuilt/zircon/x64/Scrt1.o
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
target: x86_64-fuchsia
- name: Build Zircon user programs
run: cd zircon-user && make build MODE=release
@ -82,18 +90,19 @@ jobs:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- uses: Swatinem/rust-cache@v1
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --package zcore --features "${{ matrix.mode }} libos"
- name: Clippy
uses: actions-rs/cargo@v1
with:
@ -111,20 +120,22 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
- uses: Swatinem/rust-cache@v1
- name: Build ${{ matrix.arch }} bare-metal zircon
if: matrix.arch == 'x86_64'
run: cd zCore && make build ARCH=${{ matrix.arch }}
- name: Clippy ${{ matrix.arch }} bare-metal zircon
if: matrix.arch == 'x86_64'
run: cd zCore && make clippy ARCH=${{ matrix.arch }}
@ -132,6 +143,7 @@ jobs:
- name: Build ${{ matrix.arch }} bare-metal linux
if: matrix.arch == 'riscv64'
run: cd zCore && make build ARCH=${{ matrix.arch }} LINUX=1
- name: Clippy ${{ matrix.arch }} bare-metal linux
if: matrix.arch == 'riscv64'
run: cd zCore && make clippy ARCH=${{ matrix.arch }} LINUX=1

View File

@ -4,30 +4,25 @@ on:
push:
pull_request:
env:
rust_toolchain: nightly-2022-01-20
jobs:
doc:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
- uses: Swatinem/rust-cache@v1
toolchain: ${{ env.rust_toolchain }}
- name: Build docs
run: |
cargo doc --no-deps --all-features
cat >target/doc/index.html <<EOF
<html>
<head>
<meta http-equiv="refresh" content="0;URL=kernel_hal/index.html">
<title>Redirection</title>
</head>
<body onload="window.location = 'kernel_hal/index.html'">
<p>Redirecting to <a href="kernel_hal/index.html">kernel_hal/index.html</a>...</p>
</body>
</html>
EOF
.github/scripts/add-doc-index.sh
- name: Deploy to Github Pages
if: ${{ github.ref == 'refs/heads/master' }}
uses: JamesIves/github-pages-deploy-action@releases/v3

View File

@ -6,33 +6,39 @@ on:
schedule:
- cron: '0 22 * * *' # every day at 22:00 UTC
env:
rust_toolchain: nightly-2022-01-20
qemu_version: 7.0.0
jobs:
test:
unit-test:
name: Unit Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so,prebuilt/zircon/x64/bringup.zbi,prebuilt/zircon/x64/libzircon-libos.so,prebuilt/zircon/x64/userboot-libos.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
# - uses: Swatinem/rust-cache@v1
- name: Prepare rootfs
run: make rootfs
- name: Test
run: cargo test --no-fail-fast
- name: Run unit test
uses: actions-rs/cargo@v1
with:
command: test
args: --no-fail-fast
env:
CARGO_INCREMENTAL: '0'
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort'
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort'
- name: Cache grcov
uses: actions/cache@v3
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-grcov
- name: Gather coverage data
id: coverage
uses: actions-rs/grcov@v0.1
@ -43,125 +49,131 @@ jobs:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: ${{ steps.coverage.outputs.report }}
bench:
bench-test:
name: Bench Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
# - uses: Swatinem/rust-cache@v1
- uses: actions-rs/cargo@v1
- name: Run bench test
uses: actions-rs/cargo@v1
with:
command: bench
zircon-core-test-libos:
name: Zircon Core Test Libos
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/zircon/x64/core-tests.zbi,prebuilt/zircon/x64/libzircon-libos.so,prebuilt/zircon/x64/userboot-libos.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
# - uses: Swatinem/rust-cache@v1
- name: Install dependencies
run: pip3 install -r tests/requirements.txt
- name: Pull prebuilt images
run: |
git lfs pull -I prebuilt/zircon/x64/core-tests.zbi
git lfs pull -I prebuilt/zircon/x64/libzircon-libos.so
git lfs pull -I prebuilt/zircon/x64/userboot-libos.so
- name: Install python dependencies
run: .github/scripts/install-deps.sh
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 zircon_core_test.py --libos --fast --no-failed
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 zircon_core_test.py --libos
linux-libc-test-libos:
name: Linux Libc Test Libos
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
# - uses: Swatinem/rust-cache@v1
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install musl-tools musl-dev -y
pip3 install -r tests/requirements.txt
run: .github/scripts/install-deps.sh musl-tools musl-dev
- name: Prepare rootfs
run: make libc-test
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_libc_test.py --libos --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_libc_test.py --libos
zircon-core-test-baremetal:
name: Zircon Core Test Baremetal
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
arch: [x86_64]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/zircon/x64/core-tests.zbi,prebuilt/zircon/x64/libzircon.so,prebuilt/zircon/x64/userboot.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
# - uses: Swatinem/rust-cache@v1
- name: Pull prebuilt images
run: |
git lfs pull -I prebuilt/zircon/x64/core-tests.zbi
git lfs pull -I prebuilt/zircon/x64/libzircon.so
git lfs pull -I prebuilt/zircon/x64/userboot.so
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install ninja-build -y
pip3 install -r tests/requirements.txt
run: .github/scripts/install-deps.sh ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-7.0.0
key: qemu-7.0.0-x86_64-riscv64
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
if [ ! -d qemu-7.0.0 ]; then
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar -xf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=x86_64-softmmu,riscv64-softmmu
make -j
else
cd qemu-7.0.0
fi
sudo make install
qemu-system-${{ matrix.arch }} --version
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-x86_64 --version
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 zircon_core_test.py --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 zircon_core_test.py
linux-libc-test-baremetal:
name: Linux Libc Test Baremetal
runs-on: ubuntu-20.04
strategy:
fail-fast: false
@ -171,54 +183,51 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- if: matrix.arch == 'riscv64'
uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
# - uses: Swatinem/rust-cache@v1
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install musl-tools musl-dev ninja-build -y
pip3 install -r tests/requirements.txt
run: .github/scripts/install-deps.sh musl-tools musl-dev ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-7.0.0
key: qemu-7.0.0-x86_64-riscv64
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
if [ ! -d qemu-7.0.0 ]; then
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar -xf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=x86_64-softmmu,riscv64-softmmu
make -j
else
cd qemu-7.0.0
fi
sudo make install
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
run: make test-image ARCH=${{ matrix.arch }}
run: make libc-test ARCH=${{ matrix.arch }} && make image ARCH=${{ matrix.arch }}
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_libc_test.py --arch ${{ matrix.arch }} --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_libc_test.py --arch ${{ matrix.arch }}
linux-other-test-baremetal:
name: Linux Other Test Baremetal
runs-on: ubuntu-20.04
strategy:
fail-fast: false
@ -228,49 +237,45 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-01-20
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- if: matrix.arch == 'riscv64'
uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
# - uses: Swatinem/rust-cache@v1
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install musl-tools musl-dev ninja-build -y
pip3 install -r tests/requirements.txt
run: .github/scripts/install-deps.sh musl-tools musl-dev ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-7.0.0
key: qemu-7.0.0-x86_64-riscv64
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
if [ ! -d qemu-7.0.0 ]; then
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar -xf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=x86_64-softmmu,riscv64-softmmu
make -j
else
cd qemu-7.0.0
fi
sudo make install
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
run: make image ARCH=${{ matrix.arch }}
run: make other-test ARCH=${{ matrix.arch }} && make image ARCH=${{ matrix.arch }}
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_other_test.py --arch ${{ matrix.arch }} --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_other_test.py --arch ${{ matrix.arch }}

3
.gitmodules vendored
View File

@ -4,3 +4,6 @@
[submodule "tests"]
path = tests
url = https://github.com/rcore-os/zcore-tests.git
[submodule "libc-test"]
path = libc-test
url = https://github.com/rcore-os/libc-test

13
.vscode/settings.json vendored
View File

@ -1,7 +1,10 @@
{
"files.associations": {
"unistd.h": "c",
"time.h": "c"
},
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
// Prevent "can't find crate for `test`" error on no_std
// Ref: https://github.com/rust-lang/vscode-rust/issues/729
// For vscode-rust plugin users:
"rust.target": "riscv64imac-unknown-none-elf",
"rust.all_targets": false,
// For Rust Analyzer plugin users:
"rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf",
"rust-analyzer.checkOnSave.enable": false
}

View File

@ -2,7 +2,7 @@
ARCH ?= x86_64
.PHONY: help setup rootfs libc-test image test-image check doc clean
.PHONY: help setup update rootfs libc-test other-test image check doc clean
# print top level help
help:
@ -18,18 +18,19 @@ update:
# put rootfs for linux mode
rootfs:
cargo rootfs $(ARCH)
cargo rootfs --arch $(ARCH)
# put libc-test into rootfs
# put libc tests into rootfs
libc-test:
cargo libc-test $(ARCH)
cargo libc-test --arch $(ARCH)
# put other tests into rootfs
other-test:
cargo other-test --arch $(ARCH)
# build image from rootfs
image:
cargo image $(ARCH)
# build image with libc-test
test-image: libc-test image
cargo image --arch $(ARCH)
# check code style
check:
@ -44,16 +45,14 @@ aarch64-image: rcore-fs-fuse aarch64-rootfs
@rcore-fs-fuse zCore/aarch64.img aarch64_rootfs zip
@qemu-img resize -f raw zCore/aarch64.img +5M
# clean targets
clean:
cargo clean
find zCore -maxdepth 1 -name "*.img" -delete
rm -rf rootfs
rm -rf riscv-rootfs
rm -rf aarch64_rootfs
find zCore/target -type f -name "*.zbi" -delete
find zCore/target -type f -name "*.elf" -delete
rm -rf ignored/target
find zCore -maxdepth 1 -name "*.img" -delete
rt-test:
cd rootfs && git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/clrkwllms/rt-tests --depth 1
cd rootfs/rt-tests && make
cd rootfs/x86_64 && git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/clrkwllms/rt-tests --depth 1
cd rootfs/x86_64/rt-tests && make
echo x86 gcc build rt-test,now need manual modificy.

View File

@ -14,8 +14,8 @@
1. 先决条件
目前受关注的开发环境为较新版本的 Ubuntu建议使用 Ubuntu20.04 或 Ubuntu22.04。
本文假设读者使用二者之一
目前已测试的开发环境包括 Ubuntu20.04、Ubuntu22.04 和 Debian11
Ubuntu22.04 不能正确编译 x86_64 的 libc 测试
若不需要烧写到物理硬件,使用 WSL2 或其他虚拟机的操作与真机并无不同之处。
在开始之前,确保你的计算机上安装了 git 和 rustup。要在虚拟环境开发或测试需要 QEMU。
@ -44,6 +44,12 @@
make help
```
6. 推到仓库前,现在本机执行测试
```bash
make check # CI/build 的一部分,未来会实现更多快速测试指令
```
## Linux 模式
zCore 根据向用户提供的系统调用的不同,可分为 zircon 模式和 linux 模式。
@ -67,6 +73,12 @@ make rootfs ARCH=riscv64
make libc-test <ARCH=?>
```
要执行 CI 的其他测试,需要向文件系统中添加相应测试集:
```bash
make other-test <ARCH=?>
```
要以裸机模式启动 zCore需要构造将放到设备或虚拟环境中的镜像文件
```bash

View File

@ -1,3 +1,18 @@
// 解析设备树,创建已知的设备并为它们注册中断。
//
// 涉及到中断的设备包括:
//
// - 接收中断的中断控制器
// - 发出中断的设备
//
// 有效的中断控制器应该具有下列三个属性:
//
// - `interrupt-controller`: 指示这是一个中断控制器
// - `interrupt-cells`: 只是要向此控制器注册中断需要几个参数
// - `phandle`: 向此控制器注册中断时使用的一个号码,如果没有设备需要向它注册,可能不存在
//
// 设备注册中断需要 `interrupts_extended` 属性,这是一个 `Vec<u32>`,形式为 `[{phandle, ...,}*]`
// 即控制器引用和控制器指定数量的参数。
//! Probe devices and create drivers from device tree.
//!
//! Specification: <https://github.com/devicetree-org/devicetree-specification/releases/download/v0.3/devicetree-specification-v0.3.pdf>.
@ -5,24 +20,27 @@
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use super::IoMapper;
use crate::utils::devicetree::{parse_interrupts, parse_reg};
use crate::utils::devicetree::{Devicetree, InheritProps, InterruptsProp, Node, StringList};
use crate::{Device, DeviceError, DeviceResult, VirtAddr};
use crate::{
utils::devicetree::{
parse_interrupts, parse_reg, Devicetree, InheritProps, InterruptsProp, Node, StringList,
},
Device, DeviceError, DeviceResult, VirtAddr,
};
/// A wrapper of [`Device`] which provides interrupt information additionally.
#[derive(Debug)]
struct DevWithInterrupt {
/// For interrupt controller, represent the `phandle` property, otherwise
/// is `None`.
phandle: Option<u32>,
/// For interrupt controller, represent the `interrupt_cells` property,
/// otherwise is `None`.
interrupt_cells: Option<u32>,
/// A unified representation of the `interrupts` and `interrupts_extended`
/// properties for any interrupt generating device.
interrupts_extended: InterruptsProp,
/// The inner [`Device`] structure.
dev: Device,
const MODULE: &str = "device-tree";
type DevWithInterrupt = (Device, InterruptsProp);
/// 设备树中中断控制器特有的属性
struct IntcProps {
phandle: u32,
interrupt_cells: u32,
}
/// 查找表保存的中断控制器信息
struct Intc {
index: usize,
cells: usize,
}
/// A builder to probe devices and create drivers from device tree.
@ -42,26 +60,78 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Parse the device tree from root, and returns an array of [`Device`] it found.
pub fn build(&self) -> DeviceResult<Vec<Device>> {
let mut intc_map = BTreeMap::new();
let mut dev_list = Vec::new();
let mut intc_map = BTreeMap::new(); // phandle -> intc
let mut dev_list = Vec::new(); // devices
// 解析设备树
self.dt.walk(&mut |node, comp, props| {
if let Ok(dev) = self.parse_device(node, comp, props) {
// create the phandle-device mapping
if node.has_prop("interrupt-controller") {
if let Some(phandle) = dev.phandle {
intc_map.insert(phandle, dev_list.len());
debug!(
"{MODULE}: parsing node {:?} with compatible {comp:?}",
node.name
);
// parse interrupt controller
let res = if node.has_prop("interrupt-controller") {
self.parse_intc(node, comp, props).map(|(dev, intc)| {
intc_map.insert(
intc.phandle,
Intc {
index: dev_list.len(),
cells: intc.interrupt_cells as _,
},
);
dev
})
} else {
// parse other device
match comp {
#[cfg(feature = "virtio")]
c if c.contains("virtio,mmio") => self.parse_virtio(node, props),
c if c.contains("allwinner,sunxi-gmac") => {
self.parse_ethernet(node, comp, props)
}
c if c.contains("ns16550a") || c.contains("allwinner,sun20i-uart") => {
self.parse_uart(node, comp, props)
}
_ => Err(DeviceError::NotSupported),
}
dev_list.push(dev);
};
match res {
Ok(dev) => dev_list.push(dev),
Err(DeviceError::NotSupported) => {}
Err(err) => warn!("{MODULE}: failed to parsing node {:?}: {err:?}", node.name),
}
});
for dev in &dev_list {
register_interrupt(dev, &dev_list, &intc_map).ok();
// 注册中断
for (device, interrupts_extended) in &dev_list {
let mut extended = interrupts_extended.as_slice();
// 分解 interrupts_extended
while let [phandle, irq_num, ..] = extended {
if let Some(Intc { index, cells }) = intc_map.get(phandle) {
let (intc, _) = &dev_list[*index];
extended = &extended[1 + cells..];
if let Device::Irq(irq) = intc {
if *irq_num != 0xffff_ffff {
info!("{MODULE}: register interrupts for {intc:?}: {device:?}, irq_num={irq_num}");
if irq.register_device(*irq_num as _, device.inner()).is_ok() {
irq.unmask(*irq_num as _)?;
}
}
} else {
warn!("{MODULE}: node with phandle {phandle:#x} is not an interrupt-controller");
return Err(DeviceError::InvalidParam);
}
} else {
warn!(
"{MODULE}: no such node with phandle {phandle:#x} as the interrupt-parent"
);
return Err(DeviceError::InvalidParam);
}
}
}
Ok(dev_list.into_iter().map(|d| d.dev).collect())
// 丢弃中断信息
Ok(dev_list.into_iter().map(|(dev, _)| dev).collect())
}
}
@ -70,56 +140,20 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
#[allow(unused_variables)]
#[allow(unreachable_code)]
impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Parse device nodes
fn parse_device(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
debug!(
"device-tree: parsing node {:?} with compatible {:?}",
node.name, comp
);
// parse interrupt controller
let res = if node.has_prop("interrupt-controller") {
self.parse_intc(node, comp, props)
} else {
// parse other device
match comp {
#[cfg(feature = "virtio")]
c if c.contains("virtio,mmio") => self.parse_virtio(node, props),
c if c.contains("allwinner,sunxi-gmac") => self.parse_ethernet(node, comp, props),
c if c.contains("ns16550a") => self.parse_uart(node, comp, props),
c if c.contains("allwinner,sun20i-uart") => self.parse_uart(node, comp, props),
_ => Err(DeviceError::NotSupported),
}
};
if let Err(err) = &res {
if !matches!(err, DeviceError::NotSupported) {
warn!(
"device-tree: failed to parsing node {:?}: {:?}",
node.name, err
);
}
}
res
}
/// Parse nodes for interrupt controllers.
fn parse_intc(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
let phandle = node.prop_u32("phandle").ok();
let interrupt_cells = node.prop_u32("#interrupt-cells").ok();
) -> DeviceResult<(DevWithInterrupt, IntcProps)> {
let phandle = node
.prop_u32("phandle")
.map_err(|_| DeviceError::InvalidParam)?;
let interrupt_cells = node
.prop_u32("#interrupt-cells")
.map_err(|_| DeviceError::InvalidParam)?;
let interrupts_extended = parse_interrupts(node, props)?;
if phandle.is_none() || interrupt_cells.is_none() {
return Err(DeviceError::InvalidParam);
}
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
@ -134,12 +168,13 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
_ => return Err(DeviceError::NotSupported),
});
Ok(DevWithInterrupt {
phandle,
interrupt_cells,
interrupts_extended,
dev,
})
Ok((
(dev, interrupts_extended),
IntcProps {
phandle,
interrupt_cells,
},
))
}
/// Parse nodes for virtio devices over MMIO.
@ -159,7 +194,7 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
return Err(DeviceError::NotSupported);
}
info!(
"device-tree: detected virtio device: vendor_id={:#X}, type={:?}",
"{MODULE}: detected virtio device: vendor_id={:#X}, type={:?}",
header.vendor_id(),
header.device_type()
);
@ -172,12 +207,7 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
_ => return Err(DeviceError::NotSupported),
};
Ok(DevWithInterrupt {
phandle: None,
interrupt_cells: None,
interrupts_extended,
dev,
})
Ok((dev, interrupts_extended))
}
/// Parse nodes for Ethernet devices.
@ -207,12 +237,7 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
_ => return Err(DeviceError::NotSupported),
});
Ok(DevWithInterrupt {
phandle: None,
interrupt_cells: None,
interrupts_extended,
dev,
})
Ok((dev, interrupts_extended))
}
/// Parse nodes for UART devices.
@ -240,57 +265,6 @@ impl<M: IoMapper> DevicetreeDriverBuilder<M> {
_ => return Err(DeviceError::NotSupported),
});
Ok(DevWithInterrupt {
phandle: None,
interrupt_cells: None,
interrupts_extended,
dev,
})
Ok((dev, interrupts_extended))
}
}
/// Register interrupts for `dev` according to its interrupt parent, which can
/// be found from the phandle-device mapping.
fn register_interrupt(
dev: &DevWithInterrupt,
dev_list: &[DevWithInterrupt],
intc_map: &BTreeMap<u32, usize>,
) -> DeviceResult {
let mut pos = 0;
while pos < dev.interrupts_extended.len() {
let parent = dev.interrupts_extended[pos];
// find the interrupt parent in `dev_list`
if let Some(intc) = intc_map.get(&parent).map(|&i| &dev_list[i]) {
let cells = intc.interrupt_cells.ok_or(DeviceError::InvalidParam)?;
if let Device::Irq(irq) = &intc.dev {
// get irq_num from the `interrupts_extended` property
let irq_num = dev.interrupts_extended[pos + 1] as usize;
if irq_num != 0xffff_ffff {
info!(
"device-tree: register interrupts for {:?}: {:?}, irq_num={}",
intc.dev, dev.dev, irq_num
);
irq.register_device(irq_num, dev.dev.inner())?;
// enable the interrupt after registration
irq.unmask(irq_num)?;
}
} else {
warn!(
"device-tree: node with phandle {:#x} is not an interrupt-controller",
parent
);
return Err(DeviceError::InvalidParam);
}
// process the next interrupt parent
pos += 1 + cells as usize;
} else {
warn!(
"device-tree: no such node with phandle {:#x} as the interrupt-parent",
parent
);
return Err(DeviceError::InvalidParam);
}
}
Ok(())
}

View File

@ -13,7 +13,6 @@ cfg_if::cfg_if! {
}
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
mod x86_apic;
/// Implementation of x86 Advanced Programmable Interrupt Controller.
#[doc(cfg(any(target_arch = "x86", target_arch = "x86_64")))]
pub mod x86 {

View File

@ -1,5 +1,5 @@
use lock::Mutex;
use riscv::register::sie;
use spin::Once;
use crate::prelude::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
@ -23,29 +23,29 @@ pub enum ScauseIntCode {
pub struct Intc {
name: String,
soft_handler: Once<IrqHandler>,
timer_handler: Once<IrqHandler>,
ext_handler: Once<IrqHandler>,
soft_handler: Mutex<Option<IrqHandler>>,
timer_handler: Mutex<Option<IrqHandler>>,
ext_handler: Mutex<Option<IrqHandler>>,
}
impl Intc {
pub fn new() -> Self {
Self {
name: format!("riscv-intc-cpu{}", INTC_NUM.fetch_add(1, Ordering::Relaxed)),
soft_handler: Once::new(),
timer_handler: Once::new(),
ext_handler: Once::new(),
soft_handler: Mutex::new(None),
timer_handler: Mutex::new(None),
ext_handler: Mutex::new(None),
}
}
fn with_handler<F>(&self, cause: usize, op: F) -> DeviceResult
where
F: FnOnce(&Once<IrqHandler>) -> DeviceResult,
F: FnOnce(&mut Option<IrqHandler>) -> DeviceResult,
{
match cause {
S_SOFT => op(&self.soft_handler),
S_TIMER => op(&self.timer_handler),
S_EXT => op(&self.ext_handler),
S_SOFT => op(&mut self.soft_handler.lock()),
S_TIMER => op(&mut self.timer_handler.lock()),
S_EXT => op(&mut self.ext_handler.lock()),
_ => {
error!("invalid SCAUSE value {:#x}!", cause);
Err(DeviceError::InvalidParam)
@ -66,9 +66,8 @@ impl Scheme for Intc {
}
fn handle_irq(&self, cause: usize) {
trace!("intc handle irq, cause {}", cause);
self.with_handler(cause, |opt| {
if let Some(h) = opt.get() {
if let Some(h) = opt {
h();
} else {
warn!("no registered handler for SCAUSE {}!", cause);
@ -110,16 +109,23 @@ impl IrqScheme for Intc {
fn register_handler(&self, cause: usize, handler: IrqHandler) -> DeviceResult {
self.with_handler(cause, |opt| {
if opt.is_completed() {
if opt.is_some() {
Err(DeviceError::AlreadyExists)
} else {
opt.call_once(|| handler);
*opt = Some(handler);
Ok(())
}
})
}
fn unregister(&self, _cause: usize) -> DeviceResult {
panic!("unregister intc handler unsupported!");
fn unregister(&self, cause: usize) -> DeviceResult {
self.with_handler(cause, |opt| {
if opt.is_some() {
*opt = None;
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
})
}
}

View File

@ -1,4 +1,6 @@
use x2apic::lapic::{xapic_base, LocalApic as LocalApicInner, LocalApicBuilder};
use x2apic::lapic::{
xapic_base, LocalApic as LocalApicInner, LocalApicBuilder, TimerDivide, TimerMode,
};
use super::{consts, Phys2VirtFn};
@ -47,4 +49,24 @@ impl LocalApic {
pub fn eoi(&mut self) {
unsafe { self.inner.end_of_interrupt() }
}
pub fn disable_timer(&mut self) {
unsafe { self.inner.disable_timer() }
}
pub fn enable_timer(&mut self) {
unsafe { self.inner.enable_timer() }
}
pub fn set_timer_mode(&mut self, mode: TimerMode) {
unsafe { self.inner.set_timer_mode(mode) }
}
pub fn set_timer_divide(&mut self, divide: TimerDivide) {
unsafe { self.inner.set_timer_divide(divide) }
}
pub fn set_timer_initial(&mut self, initial: u32) {
unsafe { self.inner.set_timer_initial(initial) }
}
}

View File

@ -168,4 +168,9 @@ impl IrqScheme for Apic {
Err(DeviceError::InvalidParam)
}
}
fn apic_timer_enable(&self) {
// SAFETY: this will called only once for every core
Apic::local_apic().enable_timer();
}
}

View File

@ -76,4 +76,9 @@ pub trait IrqScheme: Scheme {
fn init_hart(&self) {
unimplemented!()
}
/// [for x86_64] enable apic timer
fn apic_timer_enable(&self) {
unimplemented!()
}
}

View File

@ -61,6 +61,7 @@ executor = { git = "https://github.com/DeathWish5/PreemptiveScheduler", rev = "5
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
executor-origin = { git = "https://github.com/rcore-os/executor.git", package = "executor", rev = "85b9335" }
[target.'cfg(target_os = "none")'.dependencies]
executor = { git = "https://github.com/DeathWish5/PreemptiveScheduler", rev = "3b04ba4" }
naive-timer = "0.2.0"
# All mode on x86_64
[target.'cfg(target_arch = "x86_64")'.dependencies]
@ -72,6 +73,7 @@ x86_64 = "0.14"
uefi = "0.11"
raw-cpuid = "9.0"
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "1069df3" }
x2apic = "0.4"
# Bare-metal mode on riscv64
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]

View File

@ -26,7 +26,12 @@ pub(super) fn super_soft() {
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
let scause = scause::read();
trace!("trap happened: {:?}", TrapReason::from(scause));
debug!("kernel trap happened: {:?}", TrapReason::from(scause));
debug!(
"sepc = 0x{:x} pgtoken = 0x{:x}",
tf.sepc,
crate::vm::current_vmtoken()
);
match TrapReason::from(scause) {
TrapReason::SoftwareBreakpoint => breakpoint(&mut tf.sepc),
TrapReason::PageFault(vaddr, flags) => crate::KHANDLER.handle_page_fault(vaddr, flags),

View File

@ -33,7 +33,19 @@ pub(super) fn init() -> DeviceResult {
irq.unmask(trap::X86_ISA_IRQ_COM2)?;
}
}
use x2apic::lapic::{TimerDivide, TimerMode};
irq.register_local_apic_handler(trap::X86_INT_APIC_TIMER, Box::new(super::trap::super_timer))?;
// SAFETY: this will be called once and only once for every core
Apic::local_apic().set_timer_mode(TimerMode::Periodic);
Apic::local_apic().set_timer_divide(TimerDivide::Div256); // indeed it is Div1, the name is confusing.
let cycles =
super::cpu::cpu_frequency() as u64 * 1_000_000 / super::super::timer::TICKS_PER_SEC;
Apic::local_apic().set_timer_initial(cycles as u32);
Apic::local_apic().disable_timer();
drivers::add_device(Device::Irq(irq));
// PCI scan

View File

@ -57,7 +57,7 @@ pub fn primary_init() {
}
pub fn timer_init() {
// DO NOTHING
timer::init();
}
pub fn secondary_init() {

View File

@ -4,3 +4,8 @@ pub fn timer_now() -> Duration {
let cycle = unsafe { core::arch::x86_64::_rdtsc() };
Duration::from_nanos(cycle * 1000 / super::cpu::cpu_frequency() as u64)
}
pub fn init() {
let irq = crate::drivers::all_irq().first_unwrap();
irq.apic_timer_enable();
}

View File

@ -30,7 +30,7 @@ pub(super) fn super_timer() {
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
trace!(
debug!(
"Interrupt: {:#x} @ CPU{}",
tf.trap_num,
super::cpu::cpu_id()

View File

@ -17,7 +17,7 @@ lazy_static! {
hal_fn_impl! {
impl mod crate::hal_fn::timer {
fn timer_set_first() {
fn timer_enable() {
if !FIRST.load(Ordering::Relaxed) {
FIRST.store(true, Ordering::Relaxed);
super::arch::timer_init();

View File

@ -116,7 +116,7 @@ impl TrapReason {
#[cfg(target_arch = "aarch64")]
pub fn from(trap_num: usize, _elr: usize) -> Self {
// TODO: check if is right
use crate::{Info, Kind, Source, Syndrome, Fault};
use crate::{Fault, Info, Kind, Source, Syndrome};
use cortex_a::registers::{ESR_EL1, FAR_EL1};
use tock_registers::interfaces::Readable;
@ -129,12 +129,14 @@ impl TrapReason {
Kind::Synchronous => match Syndrome::from(esr) {
Syndrome::Breakpoint => Self::SoftwareBreakpoint,
Syndrome::Svc(_) => Self::Syscall,
Syndrome::DataAbort { kind: _, level: _ } => {
Self::PageFault(FAR_EL1.get() as _, MMUFlags::READ | MMUFlags::WRITE | MMUFlags::USER)
}
Syndrome::InstructionAbort { kind: Fault::Permission, level: _ } => {
Self::PageFault(FAR_EL1.get() as _, MMUFlags::EXECUTE | MMUFlags::USER)
}
Syndrome::DataAbort { kind: _, level: _ } => Self::PageFault(
FAR_EL1.get() as _,
MMUFlags::READ | MMUFlags::WRITE | MMUFlags::USER,
),
Syndrome::InstructionAbort {
kind: Fault::Permission,
level: _,
} => Self::PageFault(FAR_EL1.get() as _, MMUFlags::EXECUTE | MMUFlags::USER),
Syndrome::PCAlignmentFault | Syndrome::SpAlignmentFault => Self::UnalignedAccess,
_ => Self::GernelFault(esr as usize),
},

View File

@ -158,7 +158,7 @@ hal_fn_def! {
/// Time and clock functions.
pub mod timer {
/// Set the first time interrupt
pub fn timer_set_first();
pub fn timer_enable();
/// Get current time.
/// TODO: use `Instant` as return type.

View File

@ -69,7 +69,7 @@ impl GenericPageTable for PageTable {
fn query(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, MMUFlags, PageSize)> {
debug_assert!(is_aligned(vaddr));
if PMEM_MAP_VADDR <= vaddr && vaddr < PMEM_MAP_VADDR + PMEM_SIZE {
if (PMEM_MAP_VADDR..PMEM_MAP_VADDR + PMEM_SIZE).contains(&vaddr) {
Ok((
vaddr - PMEM_MAP_VADDR,
MMUFlags::READ | MMUFlags::WRITE,

1
libc-test Submodule

@ -0,0 +1 @@
Subproject commit 1426bea9f3482dec1aa98c31dcc3733a750fb090

View File

@ -72,7 +72,7 @@ impl Semaphore {
inner: Arc<Mutex<SemaphoreInner>>,
}
impl<'a> Future for SemaphoreFuture {
impl Future for SemaphoreFuture {
type Output = Result<(), LxError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
@ -157,13 +157,13 @@ impl Semaphore {
}
}
impl<'a> Drop for SemaphoreGuard<'a> {
impl Drop for SemaphoreGuard<'_> {
fn drop(&mut self) {
self.sem.release();
}
}
impl<'a> Deref for SemaphoreGuard<'a> {
impl Deref for SemaphoreGuard<'_> {
type Target = Semaphore;
fn deref(&self) -> &Self::Target {

View File

@ -112,11 +112,11 @@ impl Syscall<'_> {
}
let sem = &sem_array[num as usize];
let _result = match op {
match op {
1 => sem.release(),
-1 => sem.acquire().await?,
_ => unimplemented!("Semaphore: semop.(Not 1/-1)"),
};
}
sem.set_pid(self.zircon_process().id() as usize);
if flags.contains(SemFlags::SEM_UNDO) {
self.linux_process().semaphores_add_undo(id, num, op);

View File

@ -94,19 +94,27 @@ impl Syscall<'_> {
return Err(PagingError::NoMemory);
}
Err(PagingError::AlreadyMapped) => {
panic!("get_vaddr_flags error!!!");
panic!("get_vaddr_flags error vaddr(0x{:x})", vaddr);
}
}
if is_handle_read_pagefault {
if let Err(err) = vmar.handle_page_fault(vaddr, MMUFlags::READ) {
panic!("into_out_userptr handle_page_fault: {:?}", err);
error!(
"into_out_userptr handle_page_fault: {:?} vaddr(0x{:x})",
err, vaddr
);
return Err(PagingError::NotMapped);
}
}
if is_handle_write_pagefault {
if let Err(err) = vmar.handle_page_fault(vaddr, MMUFlags::WRITE) {
panic!("into_out_userptr handle_page_fault: {:?}", err);
error!(
"into_out_userptr handle_page_fault: {:?} vaddr(0x{:x})",
err, vaddr
);
return Err(PagingError::NotMapped);
}
}
Ok(())

View File

@ -106,7 +106,7 @@ impl Syscall<'_> {
Err(_e) => return Err(LxError::EACCES),
}
};
warn!(
debug!(
"Futex uaddr: {:#x}, op: {:x}, val: {}, timeout_ptr: {:x?}, val2: {}",
uaddr,
op.bits(),

View File

@ -8,6 +8,7 @@ use bitflags::bitflags;
use kernel_hal::context::UserContextField;
use linux_object::thread::{CurrentThreadExt, RobustList, ThreadExt};
use linux_object::{fs::INodeExt, loader::LinuxElfLoader};
use zircon_object::vm::USER_STACK_PAGES;
/// Syscalls for process.
///
@ -285,7 +286,7 @@ impl Syscall<'_> {
let (entry, sp) = LinuxElfLoader {
syscall_entry: self.syscall_entry,
stack_pages: 8,
stack_pages: USER_STACK_PAGES,
root_inode: proc.root_inode().clone(),
}
.load(&vmar, &data, args, envs, path)?;

View File

@ -13,7 +13,7 @@ async fn main() {
}
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin".into()];
let rootfs_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("../rootfs");
let rootfs_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("../rootfs/x86_64");
let hostfs = rcore_fs_hostfs::HostFS::new(rootfs_path);
let proc = zcore_loader::linux::run(args[1..].to_vec(), envs, hostfs);

View File

@ -11,7 +11,7 @@ use linux_object::fs::{vfs::FileSystem, INodeExt};
use linux_object::thread::{CurrentThreadExt, ThreadExt};
use linux_object::{loader::LinuxElfLoader, process::ProcessExt};
use zircon_object::task::{CurrentThread, Job, Process, Thread, ThreadState};
use zircon_object::{object::KernelObject, ZxError, ZxResult};
use zircon_object::{object::KernelObject, vm::USER_STACK_PAGES, ZxError, ZxResult};
/// Create and run main Linux process
pub fn run(args: Vec<String>, envs: Vec<String>, rootfs: Arc<dyn FileSystem>) -> Arc<Process> {
@ -22,7 +22,7 @@ pub fn run(args: Vec<String>, envs: Vec<String>, rootfs: Arc<dyn FileSystem>) ->
let thread = Thread::create_linux(&proc).unwrap();
let loader = LinuxElfLoader {
syscall_entry: kernel_hal::context::syscall_entry as usize,
stack_pages: 8,
stack_pages: USER_STACK_PAGES,
root_inode: rootfs.root_inode(),
};
@ -71,11 +71,20 @@ async fn run_user(thread: CurrentThread) {
}
// run
trace!("go to user: tid = {} ctx = {:#x?}", thread.id(), ctx);
kernel_hal::interrupt::intr_off(); // trapframe can't be interrupted
debug!(
"go to user: tid = {} pc = {:x}",
thread.id(),
ctx.get_field(UserContextField::InstrPointer)
);
// trace!("ctx = {:#x?}", ctx);
ctx.enter_uspace();
kernel_hal::interrupt::intr_on();
trace!("back from user: tid = {} ctx = {:#x?}", thread.id(), ctx);
debug!(
"back from user: tid = {} pc = {:x} trap reason = {:?}",
thread.id(),
ctx.get_field(UserContextField::InstrPointer),
ctx.trap_reason(),
);
// trace!("ctx = {:#x?}", ctx);
// handle trap/interrupt/syscall
if let Err(err) = handle_user_trap(&thread, ctx).await {
thread.exit_linux(err as i32);
@ -128,7 +137,6 @@ async fn handle_signal(
} else {
(*ctx).setup_uspace(action.handler, sp, &[signal as usize, 0, 0]);
}
(*ctx).enter_uspace();
}
ctx
}
@ -143,6 +151,16 @@ pub unsafe fn push_stack<T>(stack_top: usize, val: T) -> usize {
stack_top as usize
}
use kernel_hal::interrupt::{intr_off, intr_on};
macro_rules! run_with_irq_enable {
($($statements:stmt)*) => {
intr_on();
$($statements)*
intr_off();
};
}
async fn handle_user_trap(thread: &CurrentThread, mut ctx: Box<UserContext>) -> ZxResult {
let reason = ctx.trap_reason();
if let TrapReason::Syscall = reason {
@ -156,7 +174,9 @@ async fn handle_user_trap(thread: &CurrentThread, mut ctx: Box<UserContext>) ->
syscall_entry: kernel_hal::context::syscall_entry as usize,
};
trace!("Syscall : {} {:x?}", num as u32, args);
let ret = syscall.syscall(num as u32, args).await as usize;
run_with_irq_enable! {
let ret = syscall.syscall(num as u32, args).await as usize
}
thread.with_context(|ctx| ctx.set_field(UserContextField::ReturnValue, ret))?;
return Ok(());
}
@ -166,7 +186,9 @@ async fn handle_user_trap(thread: &CurrentThread, mut ctx: Box<UserContext>) ->
let pid = thread.proc().id();
match reason {
TrapReason::Interrupt(vector) => {
kernel_hal::interrupt::handle_irq(vector);
run_with_irq_enable! {
kernel_hal::interrupt::handle_irq(vector)
}
#[cfg(not(feature = "libos"))]
if vector == kernel_hal::context::TIMER_INTERRUPT_VEC {
kernel_hal::thread::yield_now().await;

View File

@ -1,13 +1,15 @@
use rcore_fs_hostfs::HostFS;
use std::fs;
const LIBOS_ROOTFS: &str = "../rootfs/x86_64";
/// test with cmd line
async fn test(cmdline: &str) -> i64 {
kernel_hal::init();
let args: Vec<String> = cmdline.split(' ').map(|s| s.into()).collect();
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()]; // TODO
let hostfs = HostFS::new("../rootfs");
let hostfs = HostFS::new(LIBOS_ROOTFS);
let proc = zcore_loader::linux::run(args, envs, hostfs);
proc.wait_for_exit().await
}
@ -45,24 +47,26 @@ async fn test_dir() {
#[async_std::test]
async fn test_create_remove_file() {
let test_file = format!("{LIBOS_ROOTFS}/testfile");
test("/bin/busybox rm testfile").await; // can't remove
fs::read("../rootfs/testfile").unwrap_err();
fs::read(&test_file).unwrap_err();
test("/bin/busybox touch testfile").await;
fs::read("../rootfs/testfile").unwrap();
fs::read(&test_file).unwrap();
test("/bin/busybox touch testfile").await;
fs::read("../rootfs/testfile").unwrap();
fs::read(&test_file).unwrap();
test("/bin/busybox rm testfile").await;
fs::read("../rootfs/testfile").unwrap_err();
fs::read(&test_file).unwrap_err();
}
#[async_std::test]
async fn test_create_remove_dir() {
let test = format!("{LIBOS_ROOTFS}/test");
test("/bin/busybox rmdir test").await; // can't remove
fs::read_dir("../rootfs/test").unwrap_err();
fs::read_dir(&test).unwrap_err();
test("/bin/busybox mkdir test").await;
fs::read_dir("../rootfs/test").unwrap();
fs::read_dir(&test).unwrap();
test("/bin/busybox rmdir test").await;
fs::read_dir("../rootfs/test").unwrap_err();
fs::read_dir(&test).unwrap_err();
}
#[async_std::test]
@ -73,22 +77,24 @@ async fn test_readfile() {
#[async_std::test]
async fn test_cp_mv() {
let hostname = format!("{LIBOS_ROOTFS}/etc/hostname.bak");
test("/bin/busybox cp /etc/hostnama /etc/hostname.bak").await; // can't move
fs::read("../rootfs/etc/hostname.bak").unwrap_err();
fs::read(&hostname).unwrap_err();
test("/bin/busybox cp /etc/hostname /etc/hostname.bak").await;
fs::read("../rootfs/etc/hostname.bak").unwrap();
fs::read(&hostname).unwrap();
test("/bin/busybox mv /etc/hostname.bak /etc/hostname.mv").await;
fs::read("../rootfs/etc/hostname.bak").unwrap_err();
fs::read(&hostname).unwrap_err();
}
#[async_std::test]
async fn test_link() {
let hostname = format!("{LIBOS_ROOTFS}/etc/hostname.ln");
test("/bin/busybox ln /etc/hostnama /etc/hostname.ln").await; // can't ln
fs::read("../rootfs/etc/hostname.ln").unwrap_err();
fs::read(&hostname).unwrap_err();
test("/bin/busybox ln /etc/hostname /etc/hostname.ln").await;
fs::read("../rootfs/etc/hostname.ln").unwrap();
fs::read(&hostname).unwrap();
test("/bin/busybox unlink /etc/hostname.ln").await;
fs::read("../rootfs/etc/hostname.ln").unwrap_err();
fs::read(&hostname).unwrap_err();
}
#[async_std::test]
@ -109,7 +115,7 @@ async fn test_sleep() {
#[async_std::test]
async fn test_truncate() {
assert_eq!(test("/bin/busybox truncate -s 12 testtruncate").await, 0);
fs::read("../rootfs/testtruncate").unwrap();
fs::read(format!("{LIBOS_ROOTFS}/testtruncate")).unwrap();
}
#[async_std::test]

BIN
prebuilt/linux/README.md (Stored with Git LFS)

Binary file not shown.

BIN
prebuilt/linux/libc-libos.so (Stored with Git LFS)

Binary file not shown.

BIN
prebuilt/linux/riscv64/busybox (Stored with Git LFS)

Binary file not shown.

BIN
prebuilt/linux/riscv64/hello (Stored with Git LFS)

Binary file not shown.

54
xtask/CHANGLOG.md Normal file
View File

@ -0,0 +1,54 @@
# 更新公告
最新的更新将出现在最上方。
## 20220521(YdrMaster)
- 适用于 libos 的 musl libc so 文件不再通过 git lfs 获取;
## 20220513(YdrMaster)
- 选择架构现在是一个参数而不是子命令,例如 `cargo rootfs --arch x86_64`
- 增加 `asm` 指令将指定参数编译的内核反汇编到文件;
- 增加 `qemu` 指令在 QEMU 中启动 zCore目前仅支持 RiscV64+Linux可配置 SMP可配置连接 gdb
- 增加 `gdb` 指令启动 gdb 并连接指定端口(目前仅支持 RiscV64
## 20220512(YdrMaster)
`cargo check-style` 现在会依 CI/build 的方式工作。
## 20220511(YdrMaster)
### 目录结构定义
- 现在用于 linux 模式的 rootfs 统一放在 `rootfs/{arch}` 目录,未来新增 aarch64 或更多架构也将放在这个目录;
- 不再将构建过程产生的东西放在 `prebuilt` 目录。现在 `prebuilt` 目录完全来自 `git lfs pull`
- 下载的源文件放在 `ignored/origin/{arch}``make clean` 时不会清除这些文件;
- 解压或构建过程产生的其他文件放在 `ignored/target/{arch}``make clean` 会清除这些文件;
- `libc-test` 现在是一个子模块,不再需要单独 `git clone`
### 使用步骤
- 现在 `cargo rootfs {arch}` 将清空已有 `rootfs/{arch}`,然后产生供 zCore 以 linux 模式启动的最小文件系统——只有 `/bin/busybox``lib/lib/ld-musl-{arch}.so.1` 两个文件,以及一些指向 busybox 的符号链接;
- `cargo libc-test {arch}` 命令将向 `rootfs/{arch}` 放入 libc 测试的测例文件,在必要时下载交叉编译工具链;
- 增加 `cargo other-test {arch}` 命令,向 `rootfs/{arch}` 放入其他测试的测例文件;
- `cargo image {arch}` 命令将 `rootfs/{arch}` 打包成 `zCore/{arch}.img` 文件,过程中不关心 `rootfs/{arch}` 的内容。因此如需要向文件系统加入文件,在 `image` 之前放入 `rootfs/{arch}` 即可;
- `libc-test`、`other-test`、`image` 都不需要先 `rootfs`,如果 `rootfs/{arch}` 目录不存在,将自动创建;
### 实现变更
- 使用 `std::os::unix::fs::symlink` 建立符号链接,不再依赖 `ln` 应用程序;
## 20220506(YdrMaster)
顶层的 Makefile 已经尽量迁移到 rust并在子项目 README.md 中更新了子命令说明。
计划提起一次 PR。
## 20220504(YdrMaster)
初步的计划是先尽量将 Makefile 转化为类型安全且更有可能工程化结构化的 Rust xtask。
尤其是要将 zCore 目录内外的两个 Makefile 合并。
目前已经架空了外面 Makefile 的 rootfs 指令,这个指令是用于将加载到内存的最小系统的。
外面的 Makefile 还剩打包镜像、启动某些测试集的功能,但目前命令之间不正交,还需要进一步梳理。

View File

@ -10,13 +10,14 @@ build = "build.rs"
[dependencies]
clap = { version = "3.1", features = ["derive"] }
clap-verbosity-flag = "1.0"
dircpy = "0.3"
rand = "0.8"
shadow-rs = "0.11"
rcore-fs = { git = "https://github.com/rcore-os/rcore-fs", rev = "1a3246b" }
rcore-fs-sfs = { git = "https://github.com/rcore-os/rcore-fs", rev = "1a3246b" }
rcore-fs-fuse = { git = "https://github.com/rcore-os/rcore-fs", rev = "1a3246b" }
[build-dependencies]
[target.'cfg(target_arch = "x86_64")'.dependencies]
shadow-rs = "0.11"
[target.'cfg(target_arch = "x86_64")'.build-dependencies]
shadow-rs = "0.11"

View File

@ -1,5 +1,7 @@
# zCore tasks
[更新公告](CHANGLOG.md)
## 使用说明
使用 `cargo xtask help` 将打印出命令说明。对主要子命令的简要说明如下:
@ -8,11 +10,15 @@
2. `setup`: 拉取预编译文件和子项目,补全仓库;
3. `update-all`: 更新工具链、依赖和子项目;
4. `check-style`: 编译前检查;
5. `rootfs <arch>`: 创建架构相关的 linux 启动文件系统;
6. `libc-test <arch>`: 向启动文件系统加入 libc 测试;
7. `image <arch>`: 打包指定架构的系统镜像;
5. `rootfs --arch <arch>`: 创建架构相关的 linux 启动文件系统;
6. `libc-test --arch <arch>`: 向启动文件系统加入 libc 测试;
7. `other-test --arch <arch>`: 向启动文件系统加入其他测试;
8. `image --arch <arch>`: 打包指定架构的系统镜像;
9. `asm --arch <arch> --output <file name>`: 构建指定架构内核,并反汇编到文件;
10. `qemu --arch <arch>`: 启动指定架构的 Qemu 运行内核;
11. `gdb --arch <arch> --port <port>`: 启动指定架构的 GDB 并连接到端口;
其中 5、6、7 必须顺序执行,后者依赖前者的结果;
其中 6、7、8 执行时若 5 的结果不存在,将自动递归执行 5
## 项目愿景
@ -28,23 +34,7 @@
- [x] 更新测例和测试系统
- [ ] 支持 zCore 使用
- [ ] 以 LibOS 形式启动 zCore
- [ ] 在 Qemu 环境中启动 zCore
- [x] 在 Qemu 环境中启动 zCore
- [x] 打包可烧写到其他存储介质的 zCore 镜像
- [ ] 启动与 Qemu 关联的 GDB 以支持内核调试
- [x] 启动与 Qemu 关联的 GDB 以支持内核调试
- [ ] 自动化测试:实现与 CI 的逻辑一致性
## 进度日志
- 2022/05/06
顶层的 Makefile 已经尽量迁移到 rust并在子项目 README.md 中更新了子命令说明。
计划提起一次 PR。
- 2022/05/04
初步的计划是先尽量将 Makefile 转化为类型安全且更有可能工程化结构化的 Rust xtask。
尤其是要将 zCore 目录内外的两个 Makefile 合并。
目前已经架空了外面 Makefile 的 rootfs 指令,这个指令是用于将加载到内存的最小系统的。
外面的 Makefile 还剩打包镜像、启动某些测试集的功能,但目前命令之间不正交,还需要进一步梳理。

View File

@ -1,298 +1,247 @@
use crate::{
dir,
download::{git_clone, wget},
CommandExt, ALPINE_ROOTFS_VERSION, ALPINE_WEBSITE,
//! 平台相关的操作。
use crate::{
command::{dir, download::wget, CommandExt, Ext, Make, Qemu, Tar},
Arch, ALPINE_ROOTFS_VERSION, ALPINE_WEBSITE,
};
use dircpy::copy_dir;
use std::{
ffi::{OsStr, OsString},
fs,
io::Write,
os::unix,
path::{Path, PathBuf},
process::Command,
};
#[derive(Args)]
pub(super) struct Arch {
#[clap(subcommand)]
command: ArchCommands,
pub(crate) struct ArchArg {
/// Build architecture, `riscv64` or `x86_64`.
#[clap(short, long)]
pub arch: Arch,
}
#[derive(Subcommand)]
enum ArchCommands {
#[clap(name = "riscv64")]
Riscv64,
#[clap(name = "x86_64")]
X86_64,
}
impl ArchArg {
const RISCV64: Self = Self {
arch: Arch::Riscv64,
};
const X86_64: Self = Self { arch: Arch::X86_64 };
impl Arch {
/// 构造启动内存文件系统 rootfs。
///
/// 将在文件系统中放置必要的库文件,并下载用于交叉编译的工具链。
pub fn rootfs(&self, clear: bool) {
self.wget_alpine();
match self.command {
ArchCommands::Riscv64 => {
const DIR: &str = "riscv_rootfs";
const ARCH: &str = "riscv64";
let dir = Path::new(DIR);
if dir.is_dir() && !clear {
return;
}
dir::clear(dir).unwrap();
let tar = dir::detect(&format!("prebuilt/linux/{ARCH}"), "minirootfs").unwrap();
Tar::xf(&tar, Some(DIR))
.args(&["--strip-components", "1"])
.join();
Command::new("ln")
.args(&["-s", "busybox", "riscv_rootfs/bin/ls"])
.status()
.unwrap()
.exit_ok()
.expect("FAILED: ln -s busybox riscv_rootfs/bin/ls");
}
ArchCommands::X86_64 => {
const DIR: &str = "rootfs";
const ARCH: &str = "x86_64";
let dir = Path::new(DIR);
if dir.is_dir() && !clear {
return;
}
dir::clear(DIR).unwrap();
let tar = dir::detect(&format!("prebuilt/linux/{ARCH}"), "minirootfs").unwrap();
Tar::xf(&tar, Some(DIR)).join();
// libc-libos.so (convert syscall to function call) is from https://github.com/rcore-os/musl/tree/rcore
fs::copy(
"prebuilt/linux/libc-libos.so",
format!("{DIR}/lib/ld-musl-{ARCH}.so.1"),
)
.unwrap();
{
const TEST_DIR: &str = "linux-syscall/test";
const DEST_DIR: &str = "rootfs/bin/";
// for linux syscall tests
fs::read_dir(TEST_DIR)
.unwrap()
.filter_map(|res| res.ok())
.map(|entry| entry.path())
.filter(|path| path.extension().map_or(false, |ext| ext == OsStr::new("c")))
.for_each(|c| {
let o = format!(
"{DEST_DIR}/{}",
c.file_prefix().and_then(|s| s.to_str()).unwrap()
);
Command::new("gcc")
.arg(&c)
.args(&["-o", &o])
.arg("-Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1")
.status()
.unwrap()
.exit_ok()
.expect("FAILED: gcc {c:?}");
});
}
}
/// 对于 x86_64这个文件系统可用于 libos 启动。
/// 若设置 `clear`,将清除已存在的目录。
pub fn make_rootfs(&self, clear: bool) {
// 若已存在且不需要清空,可以直接退出
let dir = self.rootfs();
if dir.is_dir() && !clear {
return;
}
// 下载压缩文件并解压
let src = self.prebuild_rootfs();
// 创建目标目录
dir::clear(&dir).unwrap();
fs::create_dir(dir.join("bin")).unwrap();
fs::create_dir(dir.join("lib")).unwrap();
// 拷贝 busybox
fs::copy(src.join("bin/busybox"), dir.join("bin/busybox")).unwrap();
// 拷贝 libc.so
let libc_so = format!("lib/ld-musl-{arch}.so.1", arch = self.arch.as_str());
let so = match self.arch {
Arch::Riscv64 => src.join(&libc_so),
Arch::X86_64 => libos_libc_so(),
};
fs::copy(so, dir.join(libc_so)).unwrap();
// 为常用功能建立符号链接
const SH: &[&str] = &[
"cat", "cp", "echo", "false", "grep", "gzip", "kill", "ln", "ls", "mkdir", "mv",
"pidof", "ping", "ping6", "printenv", "ps", "pwd", "rm", "rmdir", "sh", "sleep",
"stat", "tar", "touch", "true", "uname", "usleep", "watch",
];
let bin = dir.join("bin");
for sh in SH {
unix::fs::symlink("busybox", bin.join(sh)).unwrap();
}
}
/// 将 libc-test 放入 rootfs。
pub fn libc_test(&self) {
self.rootfs(false);
git_clone(
"https://github.com/rcore-os/libc-test.git",
"ignored/libc-test",
);
match self.command {
ArchCommands::Riscv64 => {
const DIR: &str = "riscv_rootfs/libc-test";
const PRE: &str = "riscv_rootfs/libc-test-prebuild";
fs::rename(DIR, PRE).unwrap();
copy_dir("ignored/libc-test", DIR).unwrap();
fs::copy(format!("{DIR}/config.mak.def"), format!("{DIR}/config.mak")).unwrap();
pub fn put_libc_test(&self) {
// 递归 rootfs
self.make_rootfs(false);
// 拷贝仓库
let dir = self.libc_test();
dir::rm(&dir).unwrap();
copy_dir("libc-test", &dir).unwrap();
// 编译
fs::copy(dir.join("config.mak.def"), dir.join("config.mak")).unwrap();
match self.arch {
Arch::Riscv64 => {
Make::new(None)
.env("ARCH", "riscv64")
.env("ARCH", self.arch.as_str())
.env("CROSS_COMPILE", "riscv64-linux-musl-")
.env("PATH", riscv64_linux_musl_cross())
.current_dir(DIR)
.join();
.current_dir(&dir)
.invoke();
fs::copy(
format!("{PRE}/functional/tls_align-static.exe"),
format!("{DIR}/src/functional/tls_align-static.exe"),
self.target()
.join("rootfs/libc-test/functional/tls_align-static.exe"),
dir.join("src/functional/tls_align-static.exe"),
)
.unwrap();
dir::rm(PRE).unwrap();
}
ArchCommands::X86_64 => {
const DIR: &str = "rootfs/libc-test";
dir::rm(DIR).unwrap();
copy_dir("ignored/libc-test", DIR).unwrap();
fs::copy(format!("{DIR}/config.mak.def"), format!("{DIR}/config.mak")).unwrap();
Arch::X86_64 => {
fs::OpenOptions::new()
.append(true)
.open(format!("{DIR}/config.mak"))
.open(dir.join("config.mak"))
.unwrap()
.write_all(b"CC := musl-gcc\nAR := ar\nRANLIB := ranlib")
.unwrap();
Make::new(None).current_dir(DIR).join();
Make::new(None).current_dir(dir).invoke();
}
}
}
/// 将其他测试放入 rootfs。
pub fn put_other_test(&self) {
// 递归 rootfs
self.make_rootfs(false);
let rootfs = self.rootfs();
match self.arch {
Arch::Riscv64 => {
let target = self.target();
copy_dir(target.join("rootfs/oscomp"), rootfs.join("oscomp")).unwrap();
}
Arch::X86_64 => {
let bin = rootfs.join("bin");
fs::read_dir("linux-syscall/test")
.unwrap()
.filter_map(|res| res.ok())
.map(|entry| entry.path())
.filter(|path| path.extension().map_or(false, |ext| ext == OsStr::new("c")))
.for_each(|c| {
Ext::new("gcc")
.arg(&c)
.arg("-o")
.arg(bin.join(c.file_prefix().unwrap()))
.arg("-Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1")
.invoke();
});
}
}
}
/// 生成镜像。
pub fn image(&self) {
self.rootfs(false);
let image = match self.command {
ArchCommands::Riscv64 => {
const ARCH: &str = "riscv64";
let image = format!("zCore/{ARCH}.img");
fuse("riscv_rootfs", &image);
// 递归 rootfs
self.make_rootfs(false);
let arch_str = self.arch.as_str();
let image = match self.arch {
Arch::Riscv64 => {
let rootfs = format!("rootfs/{arch_str}");
let image = format!("zCore/{arch_str}.img");
fuse(rootfs, &image);
image
}
ArchCommands::X86_64 => {
const ARCH: &str = "x86_64";
const TMP_ROOTFS: &str = "/tmp/rootfs";
const ROOTFS_LIB: &str = "rootfs/lib";
Arch::X86_64 => {
let rootfs = self.rootfs();
let to = rootfs.join("lib/ld-musl-x86_64.so.1");
fs::copy(self.target().join("rootfs/lib/ld-musl-x86_64.so.1"), &to).unwrap();
// ld-musl-x86_64.so.1 替换为适用 bare-matel 的版本
dir::clear(TMP_ROOTFS).unwrap();
let tar = dir::detect(&format!("prebuilt/linux/{ARCH}"), "minirootfs").unwrap();
Tar::xf(&tar, Some(TMP_ROOTFS)).join();
dir::clear(ROOTFS_LIB).unwrap();
fs::copy(
format!("{TMP_ROOTFS}/lib/ld-musl-x86_64.so.1"),
format!("{ROOTFS_LIB}/ld-musl-x86_64.so.1"),
)
.unwrap();
let image = format!("zCore/{arch_str}.img");
fuse(rootfs, &image);
fs::copy(libos_libc_so(), to).unwrap();
let image = format!("zCore/{ARCH}.img");
fuse("rootfs", &image);
fs::copy(
"prebuilt/linux/libc-libos.so",
format!("{ROOTFS_LIB}/ld-musl-x86_64.so.1"),
)
.unwrap();
image
}
};
Command::new("qemu-img")
.args(&["resize", &image, "+5M"])
.status()
.unwrap()
.exit_ok()
.expect("FAILED: qemu-img resize");
Qemu::img()
.arg("resize")
.args(&["-f", "raw"])
.arg(image)
.arg("+5M")
.invoke();
}
/// 获取 alpine 镜像。
fn wget_alpine(&self) {
let (local_path, web_url) = match self.command {
ArchCommands::Riscv64 => {
const ARCH: &str = "riscv64";
const FILE_NAME: &str = "minirootfs.tar.xz";
const WEB_URL: &str = "https://github.com/rcore-os/libc-test-prebuilt/releases/download/0.1/prebuild.tar.xz";
fn rootfs(&self) -> PathBuf {
let mut path = PathBuf::new();
path.push("rootfs");
path.push(self.arch.as_str());
path
}
let local_path = PathBuf::from(format!("prebuilt/linux/{ARCH}/{FILE_NAME}"));
if local_path.exists() {
return;
}
(local_path, WEB_URL.into())
}
ArchCommands::X86_64 => {
const ARCH: &str = "x86_64";
const FILE_NAME: &str = "minirootfs.tar.gz";
fn libc_test(&self) -> PathBuf {
let mut path = self.rootfs();
path.push("libc-test");
path
}
let local_path = PathBuf::from(format!("prebuilt/linux/{ARCH}/{FILE_NAME}"));
if local_path.exists() {
return;
}
(
local_path,
format!(
"{ALPINE_WEBSITE}/{ARCH}/alpine-minirootfs-{ALPINE_ROOTFS_VERSION}-{ARCH}.tar.gz"
),
)
}
fn origin(&self) -> PathBuf {
let mut path = PathBuf::new();
path.push("ignored");
path.push("origin");
path.push(self.arch.as_str());
path
}
fn target(&self) -> PathBuf {
let mut path = PathBuf::new();
path.push("ignored");
path.push("target");
path.push(self.arch.as_str());
path
}
/// 下载并解压 minirootfs。
fn prebuild_rootfs(&self) -> PathBuf {
// 构造压缩文件路径
let file_name = match self.arch {
Arch::Riscv64 => "minirootfs.tar.xz",
Arch::X86_64 => "minirootfs.tar.gz",
};
fs::create_dir_all(local_path.parent().unwrap()).unwrap();
wget(&web_url, &local_path);
}
}
struct Make(Command);
impl AsRef<Command> for Make {
fn as_ref(&self) -> &Command {
&self.0
}
}
impl AsMut<Command> for Make {
fn as_mut(&mut self) -> &mut Command {
&mut self.0
}
}
impl CommandExt for Make {}
impl Make {
fn new(j: Option<usize>) -> Self {
let mut make = Self(Command::new("make"));
match j {
Some(0) => {}
Some(j) => {
make.arg(format!("-j{j}"));
}
None => {
make.arg("-j");
}
let tar = self.origin().join(file_name);
// 若压缩文件不存在,需要下载
if !tar.exists() {
let url = match self.arch {
Arch::Riscv64 => String::from("https://github.com/rcore-os/libc-test-prebuilt/releases/download/0.1/prebuild.tar.xz"),
Arch::X86_64 => format!("{ALPINE_WEBSITE}/x86_64/alpine-minirootfs-{ALPINE_ROOTFS_VERSION}-x86_64.tar.gz"),
};
wget(url, &tar);
}
make
}
}
struct Tar(Command);
impl AsRef<Command> for Tar {
fn as_ref(&self) -> &Command {
&self.0
}
}
impl AsMut<Command> for Tar {
fn as_mut(&mut self) -> &mut Command {
&mut self.0
}
}
impl CommandExt for Tar {}
impl Tar {
fn xf(src: &impl AsRef<OsStr>, dst: Option<&str>) -> Self {
let mut cmd = Command::new("tar");
cmd.arg("xf").arg(src);
if let Some(dst) = dst {
cmd.arg("-C").arg(dst);
// 解压到目标路径
let dir = self.target().join("rootfs");
dir::clear(&dir).unwrap();
let mut tar = Tar::xf(&tar, Some(&dir));
match self.arch {
Arch::Riscv64 => tar.args(&["--strip-components", "1"]).invoke(),
Arch::X86_64 => tar.invoke(),
}
Self(cmd)
dir
}
}
/// 下载适用于 libos 的 musl libc so。
fn libos_libc_so() -> PathBuf {
const NAME: &str = "libc-libos.so";
let url =
format!("https://github.com/rcore-os/libc-test-prebuilt/releases/download/master/{NAME}");
let dst = ArchArg::X86_64.origin().join(NAME);
wget(url, &dst);
dst
}
/// 下载 riscv64-musl 工具链。
fn riscv64_linux_musl_cross() -> OsString {
const DIR: &str = "ignored";
const NAME: &str = "riscv64-linux-musl-cross";
let dir = format!("{DIR}/{NAME}");
let tgz = format!("{dir}.tgz");
wget(&format!("https://musl.cc/{NAME}.tgz"), &tgz);
let origin = ArchArg::RISCV64.origin();
let target = ArchArg::RISCV64.target();
let tgz = origin.join(format!("{NAME}.tgz"));
let dir = target.join(NAME);
dir::rm(&dir).unwrap();
Tar::xf(&tgz, Some(DIR)).join();
wget(format!("https://musl.cc/{NAME}.tgz"), &tgz);
Tar::xf(&tgz, Some(target)).invoke();
// 将交叉工具链加入 PATH 环境变量
let mut path = OsString::new();
@ -301,7 +250,9 @@ fn riscv64_linux_musl_cross() -> OsString {
path.push(":");
}
path.push(std::env::current_dir().unwrap());
path.push("/ignored/riscv64-linux-musl-cross/bin");
path.push("/");
path.push(dir);
path.push("/bin");
path
}

168
xtask/src/build.rs Normal file
View File

@ -0,0 +1,168 @@
use crate::{
arch::ArchArg,
command::{Cargo, CommandExt, Ext, Qemu},
Arch,
};
use std::{fs, path::PathBuf};
#[derive(Args)]
pub(crate) struct BuildArgs {
#[clap(flatten)]
arch: ArchArg,
/// Build as debug mode.
#[clap(long)]
debug: bool,
}
#[derive(Args)]
pub(crate) struct AsmArgs {
#[clap(flatten)]
build: BuildArgs,
/// The file to save asm.
#[clap(short, long)]
output: PathBuf,
}
#[derive(Args)]
pub(crate) struct QemuArgs {
#[clap(flatten)]
build: BuildArgs,
/// Number of hart (SMP for Symmetrical Multiple Processor).
#[clap(long)]
smp: Option<u8>,
/// Port for gdb to connect. If set, qemu will block and wait gdb to connect.
#[clap(long)]
gdb: Option<u16>,
}
#[derive(Args)]
pub(crate) struct GdbArgs {
#[clap(flatten)]
arch: ArchArg,
#[clap(long)]
port: u16,
}
impl BuildArgs {
fn arch(&self) -> Arch {
self.arch.arch
}
fn dir(&self) -> String {
format!(
"target/{arch}/{mode}",
arch = self.arch().as_str(),
mode = if self.debug { "debug" } else { "release" }
)
}
fn build(&self) {
let mut cargo = Cargo::build();
cargo
.package("zcore")
.features(false, &["linux", "board-qemu"])
.target(format!("zCore/{arch}.json", arch = self.arch().as_str()))
.args(&["-Z", "build-std=core,alloc"])
.args(&["-Z", "build-std-features=compiler-builtins-mem"]);
if !self.debug {
cargo.release();
}
cargo.invoke();
}
}
impl AsmArgs {
/// 打印 asm。
pub fn asm(&self) {
// 递归 build
self.build.build();
let out = Ext::new("rust-objdump")
.arg(format!("{dir}/zcore", dir = self.build.dir()))
.arg("-d")
.output()
.stdout;
fs::write(&self.output, out).unwrap();
}
}
impl QemuArgs {
/// 在 qemu 中启动。
pub fn qemu(&self) {
// 递归 image
self.build.arch.image();
// 递归 build
self.build.build();
// 构造各种字符串
let arch = self.build.arch();
let arch_str = arch.as_str();
let dir = self.build.dir();
let obj = format!("{dir}/zcore");
let bin = format!("{dir}/zcore.bin");
// 裁剪内核二进制文件
Ext::new("rust-objcopy")
.arg(format!("--binary-architecture={arch_str}"))
.arg(obj)
.arg("--strip-all")
.args(&["-O", "binary", &bin])
.invoke();
// 设置 Qemu 参数
let mut qemu = Qemu::system(arch);
qemu.args(&["-m", "512M"])
.args(&["-kernel", &bin])
.args(&["-initrd", &format!("zCore/{arch_str}.img")])
.args(&["-append", "\"LOG=warn\""])
.args(&["-display", "none"])
.arg("-no-reboot")
.arg("-nographic");
if let Some(smp) = self.smp {
qemu.args(&["-smp", &smp.to_string()]);
}
match arch {
Arch::Riscv64 => {
qemu.args(&["-machine", "virt"])
.arg("-bios")
.arg(rustsbi_qemu())
.args(&["-serial", "mon:stdio"]);
}
Arch::X86_64 => todo!(),
}
if let Some(port) = self.gdb {
qemu.args(&["-S", "-gdb", &format!("tcp::{port}")]);
}
qemu.invoke();
}
}
impl GdbArgs {
pub fn gdb(&self) {
match self.arch.arch {
Arch::Riscv64 => {
Ext::new("riscv64-unknown-elf-gdb")
.args(&["-ex", &format!("target remote localhost:{}", self.port)])
.invoke();
}
Arch::X86_64 => todo!(),
}
}
}
/// 下载 rustsbi。
fn rustsbi_qemu() -> PathBuf {
// const NAME: &str = "rustsbi-qemu-release";
// let origin = Arch::Riscv64.origin();
// let target = Arch::Riscv64.target();
// let zip = origin.join(format!("{NAME}.zip"));
// let dir = target.join(NAME);
// let url =
// format!("https://github.com/rustsbi/rustsbi-qemu/releases/download/v0.1.1/{NAME}.zip");
// dir::rm(&dir).unwrap();
// wget(url, &zip);
// Ext::new("unzip").arg("-d").arg(&dir).arg(zip).invoke();
// dir.join("rustsbi-qemu.bin")
PathBuf::from("default")
// PathBuf::from("../rustsbi-qemu/target/riscv64imac-unknown-none-elf/release/rustsbi-qemu.bin")
}

View File

@ -1,29 +1,13 @@
use crate::CommandExt;
use super::{ext, CommandExt};
use std::{ffi::OsStr, process::Command};
pub(crate) struct Cargo {
cmd: Command,
}
pub(crate) struct Cargo(Command);
impl AsRef<Command> for Cargo {
fn as_ref(&self) -> &Command {
&self.cmd
}
}
impl AsMut<Command> for Cargo {
fn as_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}
impl CommandExt for Cargo {}
ext!(Cargo);
impl Cargo {
fn new(sub: &(impl AsRef<OsStr> + ?Sized)) -> Self {
let mut git = Self {
cmd: Command::new("cargo"),
};
let mut git = Self(Command::new("cargo"));
git.arg(sub);
git
}
@ -40,6 +24,14 @@ impl Cargo {
Self::new("clippy")
}
pub fn doc() -> Self {
Self::new("doc")
}
pub fn build() -> Self {
Self::new("build")
}
pub fn all_features(&mut self) -> &mut Self {
self.arg("--all-features");
self
@ -72,4 +64,14 @@ impl Cargo {
self.arg("--target").arg(target);
self
}
pub fn package(&mut self, package: impl AsRef<OsStr>) -> &mut Self {
self.arg("--package").arg(package);
self
}
pub fn release(&mut self) -> &mut Self {
self.arg("--release");
self
}
}

View File

@ -1,33 +1,45 @@
//! 操作目录。
use std::path::{Path, PathBuf};
use std::{
fs,
path::{Path, PathBuf},
};
/// 删除指定路径。
///
/// 如果返回 `Ok(())``path` 将不存在。
pub fn rm(path: &(impl AsRef<Path> + ?Sized)) -> std::io::Result<()> {
pub fn rm(path: impl AsRef<Path>) -> std::io::Result<()> {
let path = path.as_ref();
if !path.exists() {
Ok(())
} else if path.is_dir() {
std::fs::remove_dir_all(path)
fs::remove_dir_all(path)
} else {
std::fs::remove_file(path)
fs::remove_file(path)
}
}
/// 创建 `path` 的父目录。
pub fn create_parent(path: impl AsRef<Path>) -> std::io::Result<()> {
match path.as_ref().parent() {
Some(parent) => fs::create_dir_all(parent),
None => Ok(()),
}
}
/// 清理 `path` 目录。
///
/// 如果返回 `Ok(())``path` 将是一个存在的空目录。
pub fn clear(path: &(impl AsRef<Path> + ?Sized)) -> std::io::Result<()> {
rm(path)?;
std::fs::create_dir_all(path)
pub fn clear(path: impl AsRef<Path>) -> std::io::Result<()> {
rm(&path)?;
std::fs::create_dir_all(&path)
}
/// 在 `dir` 目录中根据文件名前半部分 `prefix` 找到对应文件。
///
/// 不会递归查找。
pub fn detect(path: &(impl AsRef<Path> + ?Sized), prefix: &str) -> Option<PathBuf> {
#[allow(unused)]
pub fn detect(path: impl AsRef<Path>, prefix: impl AsRef<str>) -> Option<PathBuf> {
path.as_ref()
.read_dir()
.ok()?
@ -37,6 +49,6 @@ pub fn detect(path: &(impl AsRef<Path> + ?Sized), prefix: &str) -> Option<PathBu
.find(|path| {
path.file_name()
.and_then(|s| s.to_str())
.map_or(false, |s| s.starts_with(prefix))
.map_or(false, |s| s.starts_with(prefix.as_ref()))
})
}

View File

@ -1,4 +1,4 @@
use crate::{dir, git::Git, CommandExt};
use super::{dir, git::Git, CommandExt};
use std::{ffi::OsStr, fs, path::Path, process::Command};
pub(crate) fn wget(url: impl AsRef<OsStr>, dst: impl AsRef<Path>) {
@ -15,9 +15,11 @@ pub(crate) fn wget(url: impl AsRef<OsStr>, dst: impl AsRef<Path>) {
.status()
.unwrap();
if status.success() {
fs::rename(&tmp, dst).unwrap();
dir::create_parent(&dst).unwrap();
fs::copy(&tmp, dst).unwrap();
dir::rm(tmp).unwrap();
} else {
dir::rm(&tmp).unwrap();
dir::rm(tmp).unwrap();
panic!(
"Failed with code {}: wget {:?}",
status.code().unwrap(),
@ -26,10 +28,11 @@ pub(crate) fn wget(url: impl AsRef<OsStr>, dst: impl AsRef<Path>) {
}
}
#[allow(unused)]
pub(crate) fn git_clone(repo: impl AsRef<OsStr>, dst: impl AsRef<Path>) {
let dst = dst.as_ref();
if dst.is_dir() {
Git::pull().current_dir(dst).join();
Git::pull().current_dir(dst).invoke();
return;
}
@ -38,10 +41,11 @@ pub(crate) fn git_clone(repo: impl AsRef<OsStr>, dst: impl AsRef<Path>) {
let mut git = Git::clone(repo, Some(&tmp));
let status = git.status();
if status.success() {
dir::clear(dst).unwrap();
fs::rename(&tmp, dst).unwrap();
dir::create_parent(&dst).unwrap();
dircpy::copy_dir(&tmp, dst).unwrap();
dir::rm(tmp).unwrap();
} else {
dir::rm(&tmp).unwrap();
dir::rm(tmp).unwrap();
panic!(
"Failed with code {}: {:?}",
status.code().unwrap(),

View File

@ -1,31 +1,15 @@
//! 操作 git。
use crate::CommandExt;
use super::{ext, CommandExt};
use std::{ffi::OsStr, process::Command};
pub(super) struct Git {
cmd: Command,
}
pub(crate) struct Git(Command);
impl AsRef<Command> for Git {
fn as_ref(&self) -> &Command {
&self.cmd
}
}
impl AsMut<Command> for Git {
fn as_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}
impl CommandExt for Git {}
ext!(Git);
impl Git {
fn new(sub: impl AsRef<OsStr>) -> Self {
let mut git = Self {
cmd: Command::new("git"),
};
let mut git = Self(Command::new("git"));
git.arg(sub);
git
}

22
xtask/src/command/make.rs Normal file
View File

@ -0,0 +1,22 @@
use super::{ext, CommandExt};
use std::process::Command;
pub(crate) struct Make(Command);
ext!(Make);
impl Make {
pub fn new(j: Option<usize>) -> Self {
let mut make = Self(Command::new("make"));
match j {
Some(0) => {}
Some(j) => {
make.arg(format!("-j{j}"));
}
None => {
make.arg("-j");
}
}
make
}
}

131
xtask/src/command/mod.rs Normal file
View File

@ -0,0 +1,131 @@
use m::ext;
use std::{
ffi::{OsStr, OsString},
path::Path,
process::{Command, ExitStatus, Output},
};
mod cargo;
pub mod dir;
pub mod download;
mod git;
mod make;
mod qemu;
mod tar;
pub(crate) use cargo::Cargo;
pub(crate) use git::Git;
pub(crate) use make::Make;
pub(crate) use qemu::Qemu;
pub(crate) use tar::Tar;
pub(crate) trait CommandExt: AsRef<Command> + AsMut<Command> {
fn arg(&mut self, s: impl AsRef<OsStr>) -> &mut Self {
self.as_mut().arg(s);
self
}
fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
for arg in args {
self.arg(arg);
}
self
}
fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
self.as_mut().current_dir(dir);
self
}
fn env(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
self.as_mut().env(key, val);
self
}
fn status(&mut self) -> ExitStatus {
self.as_mut().status().unwrap()
}
fn info(&self) -> OsString {
let cmd = self.as_ref();
let mut msg = OsString::new();
if let Some(dir) = cmd.get_current_dir() {
msg.push("cd ");
msg.push(dir);
msg.push(" && ");
}
msg.push(cmd.get_program());
for a in cmd.get_args() {
msg.push(" ");
msg.push(a);
}
for (k, v) in cmd.get_envs() {
msg.push(" ");
msg.push(k);
if let Some(v) = v {
msg.push("=");
msg.push(v);
}
}
msg
}
fn invoke(&mut self) {
let status = self.status();
if !status.success() {
panic!(
"Failed with code {}: {:?}",
status.code().unwrap(),
self.info()
);
}
}
fn output(&mut self) -> Output {
let output = self.as_mut().output().unwrap();
if !output.status.success() {
panic!(
"Failed with code {}: {:?}",
output.status.code().unwrap(),
self.info()
);
}
output
}
}
pub(crate) struct Ext(Command);
ext!(Ext);
impl Ext {
pub fn new(program: impl AsRef<OsStr>) -> Self {
Self(Command::new(program))
}
}
mod m {
macro_rules! ext {
($ty:ty) => {
impl AsRef<Command> for $ty {
fn as_ref(&self) -> &Command {
&self.0
}
}
impl AsMut<Command> for $ty {
fn as_mut(&mut self) -> &mut Command {
&mut self.0
}
}
impl super::CommandExt for $ty {}
};
}
pub(crate) use ext;
}

17
xtask/src/command/qemu.rs Normal file
View File

@ -0,0 +1,17 @@
use super::ext;
use crate::Arch;
use std::process::Command;
pub(crate) struct Qemu(Command);
ext!(Qemu);
impl Qemu {
pub fn img() -> Self {
Self(Command::new("qemu-img"))
}
pub fn system(arch: Arch) -> Self {
Self(Command::new(format!("qemu-system-{}", arch.as_str())))
}
}

17
xtask/src/command/tar.rs Normal file
View File

@ -0,0 +1,17 @@
use super::ext;
use std::{ffi::OsStr, process::Command};
pub(crate) struct Tar(Command);
ext!(Tar);
impl Tar {
pub fn xf(src: &impl AsRef<OsStr>, dst: Option<impl AsRef<OsStr>>) -> Self {
let mut cmd = Command::new("tar");
cmd.arg("xf").arg(src);
if let Some(dst) = dst {
cmd.arg("-C").arg(dst);
}
Self(cmd)
}
}

32
xtask/src/enums.rs Normal file
View File

@ -0,0 +1,32 @@
use crate::XError;
use std::str::FromStr;
#[derive(Clone, Copy)]
pub(crate) enum Arch {
Riscv64,
X86_64,
}
impl Arch {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Riscv64 => "riscv64",
Self::X86_64 => "x86_64",
}
}
}
impl FromStr for Arch {
type Err = XError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"riscv64" => Ok(Self::Riscv64),
"x86_64" => Ok(Self::X86_64),
_ => Err(XError::EnumParse {
type_name: "Arch",
value: s.into(),
}),
}
}
}

21
xtask/src/errors.rs Normal file
View File

@ -0,0 +1,21 @@
use std::fmt::Display;
#[derive(Debug)]
pub(crate) enum XError {
EnumParse {
type_name: &'static str,
value: String,
},
}
impl Display for XError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
XError::EnumParse { type_name, value } => {
write!(f, "Parse {type_name} from {value} failed.")
}
}
}
}
impl std::error::Error for XError {}

View File

@ -5,25 +5,22 @@
extern crate clap;
use clap::Parser;
use clap_verbosity_flag::Verbosity;
use std::{
ffi::{OsStr, OsString},
fs::read_to_string,
net::Ipv4Addr,
path::Path,
process::{Command, ExitStatus},
};
use std::{fs::read_to_string, net::Ipv4Addr};
#[cfg(target_arch = "x86_64")]
mod dump;
mod arch;
mod cargo;
mod dir;
mod download;
mod dump;
mod git;
mod build;
mod command;
mod enums;
mod errors;
use arch::Arch;
use cargo::Cargo;
use git::Git;
use arch::ArchArg;
use build::{AsmArgs, GdbArgs, QemuArgs};
use command::{Cargo, CommandExt, Ext, Git, Make};
use enums::*;
use errors::XError;
const ALPINE_WEBSITE: &str = "https://dl-cdn.alpinelinux.org/alpine/v3.12/releases";
const ALPINE_ROOTFS_VERSION: &str = "3.12.0";
@ -35,10 +32,6 @@ const ALPINE_ROOTFS_VERSION: &str = "3.12.0";
struct Cli {
#[clap(subcommand)]
command: Commands,
#[clap(flatten)]
env: Env,
#[clap(flatten)]
verbose: Verbosity,
}
#[derive(Subcommand)]
@ -48,6 +41,9 @@ enum Commands {
/// Input your proxy port to set the proxy,
/// or leave blank to unset it.
GitProxy(ProxyPort),
/// Dump build config.
#[cfg(target_arch = "x86_64")]
Dump,
/// First time running.
Setup,
@ -57,25 +53,20 @@ enum Commands {
CheckStyle,
/// Build rootfs
Rootfs(Arch),
/// Put libc-test.
LibcTest(Arch),
Rootfs(ArchArg),
/// Put libc test into rootfs.
LibcTest(ArchArg),
/// Put other test into rootfs.
OtherTest(ArchArg),
/// Build image
Image(Arch),
Image(ArchArg),
/// Unit test
Test,
}
#[derive(Args)]
struct Env {
/// Build in release mode.
#[clap(short, long, global = true)]
release: bool,
/// Dump build config.
#[clap(short, long, global = true)]
dump: bool,
/// Dump asm of kernel
Asm(AsmArgs),
/// Run zCore in qemu
Qemu(QemuArgs),
/// Launch GDB
Gdb(GdbArgs),
}
#[derive(Args)]
@ -89,13 +80,7 @@ struct ProxyPort {
}
fn main() {
let cli = Cli::parse();
if cli.env.dump {
dump::dump_config();
}
match cli.command {
match Cli::parse().command {
Commands::GitProxy(ProxyPort { port, global }) => {
if let Some(port) = port {
set_git_proxy(global, port);
@ -103,16 +88,21 @@ fn main() {
unset_git_proxy(global);
}
}
#[cfg(target_arch = "x86_64")]
Commands::Dump => dump::dump_config(),
Commands::Setup => {
make_git_lfs();
git_submodule_update(true);
}
Commands::UpdateAll => update_all(),
Commands::CheckStyle => check_style(),
Commands::Rootfs(arch) => arch.rootfs(true),
Commands::LibcTest(arch) => arch.libc_test(),
Commands::Image(arch) => arch.image(),
Commands::Test => todo!(),
Commands::Rootfs(arg) => arg.make_rootfs(true),
Commands::LibcTest(arg) => arg.put_libc_test(),
Commands::OtherTest(arg) => arg.put_other_test(),
Commands::Image(arg) => arg.image(),
Commands::Asm(args) => args.asm(),
Commands::Qemu(args) => args.qemu(),
Commands::Gdb(args) => args.gdb(),
}
}
@ -126,25 +116,20 @@ fn make_git_lfs() {
{
panic!("Cannot find git lfs, see https://git-lfs.github.com/ for help.");
}
Git::lfs().arg("install").join();
Git::lfs().arg("pull").join();
Git::lfs().arg("install").invoke();
Git::lfs().arg("pull").invoke();
}
/// 更新子项目。
fn git_submodule_update(init: bool) {
Git::submodule_update(init).join();
Git::submodule_update(init).invoke();
}
/// 更新工具链和依赖。
fn update_all() {
git_submodule_update(false);
Command::new("rustup")
.arg("update")
.status()
.unwrap()
.exit_ok()
.expect("FAILED: rustup update");
Cargo::update().join();
Ext::new("rustup").arg("update").invoke();
Cargo::update().invoke();
}
/// 设置 git 代理。
@ -158,108 +143,49 @@ fn set_git_proxy(global: bool, port: u16) {
})
.expect("FAILED: detect DNS");
let proxy = format!("socks5://{dns}:{port}");
Git::config(global).args(&["http.proxy", &proxy]).join();
Git::config(global).args(&["http.proxy", &proxy]).join();
Git::config(global).args(&["http.proxy", &proxy]).invoke();
Git::config(global).args(&["https.proxy", &proxy]).invoke();
println!("git proxy = {proxy}");
}
/// 移除 git 代理。
fn unset_git_proxy(global: bool) {
Git::config(global).args(&["--unset", "http.proxy"]).join();
Git::config(global).args(&["--unset", "https.proxy"]).join();
Git::config(global)
.args(&["--unset", "http.proxy"])
.invoke();
Git::config(global)
.args(&["--unset", "https.proxy"])
.invoke();
println!("git proxy =");
}
/// 风格检查。
fn check_style() {
println!("fmt -----------------------------------------");
Cargo::fmt().arg("--all").arg("--").arg("--check").join();
println!("clippy --------------------------------------");
Cargo::clippy().all_features().join();
println!("clippy x86_64 zircon smp=1 ------------------");
println!("Check workspace");
Cargo::fmt().arg("--all").arg("--").arg("--check").invoke();
Cargo::clippy().all_features().invoke();
Cargo::doc().all_features().arg("--no-deps").invoke();
println!("Check libos");
Cargo::clippy()
.features(false, &["zircon"])
.target("x86_64.json")
.args(&["-Z", "build-std=core,alloc"])
.args(&["-Z", "build-std-features=compiler-builtins-mem"])
.current_dir("zCore")
.env("SMP", "1")
.join();
println!("clippy riscv64 linux smp=4 ------------------");
.package("zcore")
.features(false, &["zircon", "libos"])
.invoke();
Cargo::clippy()
.features(false, &["linux", "board-qemu"])
.target("riscv64.json")
.args(&["-Z", "build-std=core,alloc"])
.args(&["-Z", "build-std-features=compiler-builtins-mem"])
.package("zcore")
.features(false, &["linux", "libos"])
.invoke();
println!("Check bare-metal");
Make::new(None)
.arg("clippy")
.env("ARCH", "x86_64")
.current_dir("zCore")
.env("SMP", "4")
.env("PLATFORM", "board-qemu")
.join();
}
trait CommandExt: AsRef<Command> + AsMut<Command> {
fn arg(&mut self, s: impl AsRef<OsStr>) -> &mut Self {
self.as_mut().arg(s);
self
}
fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
for arg in args {
self.arg(arg);
}
self
}
fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
self.as_mut().current_dir(dir);
self
}
fn env(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
self.as_mut().env(key, val);
self
}
fn status(&mut self) -> ExitStatus {
self.as_mut().status().unwrap()
}
fn info(&self) -> OsString {
let cmd = self.as_ref();
let mut msg = OsString::new();
if let Some(dir) = cmd.get_current_dir() {
msg.push("cd ");
msg.push(dir);
msg.push(" && ");
}
msg.push(cmd.get_program());
for a in cmd.get_args() {
msg.push(" ");
msg.push(a);
}
for (k, v) in cmd.get_envs() {
msg.push(" ");
msg.push(k);
if let Some(v) = v {
msg.push("=");
msg.push(v);
}
}
msg
}
fn join(&mut self) {
let status = self.status();
if !status.success() {
panic!(
"Failed with code {}: {:?}",
status.code().unwrap(),
self.info()
);
}
}
.invoke();
Make::new(None)
.arg("clippy")
.env("ARCH", "riscv64")
.env("LINUX", "1")
.current_dir("zCore")
.invoke();
}

View File

@ -3,4 +3,9 @@ set architecture riscv:rv64
target remote 127.0.0.1:15234
symbol-file ../target/riscv64/release/zcore
display/10i $pc
break *0x8020004a
# tbreak *(&jump_higher - 0xffffffff00000000)
tbreak *0x802623b4
c
si
si
si

View File

@ -201,11 +201,15 @@ else
qemu_opts += -display none -nographic
endif
ifeq ($(ACCEL), 1)
ifeq ($(shell uname), Darwin)
qemu_opts += -accel hvf
else
qemu_opts += -accel kvm -cpu host,migratable=no,+invtsc
ifeq ($(ARCH), x86_64)
ifeq ($(PLATFORM), qemu)
ifeq ($(ACCEL), 1)
ifeq ($(shell uname), Darwin)
qemu_opts += -accel hvf
else
qemu_opts += -accel kvm -cpu host,migratable=no,+invtsc
endif
endif
endif
endif
@ -240,13 +244,13 @@ endif
ifeq ($(ARCH), x86_64)
gdb := gdb
else
else
gdb := riscv64-unknown-elf-gdb
endif
.PHONY: debugrun
debugrun: $(qemu_disk)
cp .gdbinit_$(MODE) .gdbinit
cp .gdbinit_$(ARCH) .gdbinit
ifeq ($(ARCH), x86_64)
$(sed) 's#initramfs=.*#initramfs=\\EFI\\zCore\\$(notdir $(user_img))#' $(esp)/EFI/Boot/rboot.conf
$(sed) 's#cmdline=.*#cmdline=$(CMDLINE)#' $(esp)/EFI/Boot/rboot.conf
@ -308,7 +312,7 @@ ifeq ($(PLATFORM), d1)
run_d1: build
$(OBJCOPY) ../prebuilt/firmware/d1/fw_payload.elf --strip-all -O binary ./zcore_d1.bin
dd if=$(kernel_img) of=zcore_d1.bin bs=512 seek=2048
xfel ddr ddr3
xfel ddr d1
xfel write 0x40000000 zcore_d1.bin
xfel exec 0x40000000
endif

View File

@ -4,11 +4,10 @@ fn main() {
//println!("cargo:rerun-if-env-changed=PLATFORM");
if std::env::var("TARGET").unwrap().contains("riscv64") {
let board = std::env::var("PLATFORM").unwrap();
let kernel_base_addr: u64 = if board.contains("d1") {
let kernel_base_addr: u64 = if std::env::var("PLATFORM").map_or(false, |p| p.contains("d1"))
{
0xffffffffc0100000
} else {
// opensbi仍旧把kernel放在0x80200000物理内存中
0xffffffff80200000
};

View File

@ -1,22 +1,3 @@
#![allow(dead_code)]
fn init_ram_disk() -> Option<&'static mut [u8]> {
if cfg!(feature = "link-user-img") {
extern "C" {
fn _user_img_start();
fn _user_img_end();
}
Some(unsafe {
core::slice::from_raw_parts_mut(
_user_img_start as *mut u8,
_user_img_end as usize - _user_img_start as usize,
)
})
} else {
kernel_hal::boot::init_ram_disk()
}
}
cfg_if! {
if #[cfg(feature = "linux")] {
use alloc::sync::Arc;
@ -24,12 +5,14 @@ cfg_if! {
#[cfg(feature = "libos")]
pub fn rootfs() -> Arc<dyn FileSystem> {
let base = if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") {
let mut rootfs = if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") {
std::path::Path::new(&dir).join("..")
} else {
std::env::current_dir().unwrap()
};
rcore_fs_hostfs::HostFS::new(base.join("rootfs"))
rootfs.push("rootfs");
rootfs.push(std::env::consts::ARCH);
rcore_fs_hostfs::HostFS::new(rootfs)
}
#[cfg(not(feature = "libos"))]
@ -61,6 +44,24 @@ cfg_if! {
}
}
#[cfg(not(feature = "libos"))]
fn init_ram_disk() -> Option<&'static mut [u8]> {
if cfg!(feature = "link-user-img") {
extern "C" {
fn _user_img_start();
fn _user_img_end();
}
Some(unsafe {
core::slice::from_raw_parts_mut(
_user_img_start as *mut u8,
_user_img_end as usize - _user_img_start as usize,
)
})
} else {
kernel_hal::boot::init_ram_disk()
}
}
// Hard link rootfs img
#[cfg(not(feature = "libos"))]
#[cfg(feature = "link-user-img")]

View File

@ -10,7 +10,7 @@ fn panic(info: &PanicInfo) -> ! {
println!("\n\n{info}");
error!("\n\n{info}");
if cfg!(feature = "baremetal-test") {
if cfg!(any(feature = "baremetal-test", feature = "board-qemu")) {
kernel_hal::cpu::reset();
} else {
loop {

View File

@ -59,7 +59,9 @@ fn primary_main(config: kernel_hal::KernelConfig) {
#[cfg(not(any(feature = "libos", target_arch = "aarch64")))]
fn secondary_main() -> ! {
while !STARTED.load(Ordering::SeqCst) {}
while !STARTED.load(Ordering::SeqCst) {
core::hint::spin_loop();
}
// Don't print anything between previous line and next line.
// Boot hart has initialized the UART chip, so we will use
// UART for output instead of SBI, but the current HART is

View File

@ -1,2 +1,2 @@
pub mod consts;
pub mod entry;
pub mod consts;

View File

@ -50,8 +50,8 @@ impl BootPageTable {
// 设置线程指针
asm!("mv tp, {}", in(reg) hartid);
// 设置内核可访问用户页
let sstatus: usize;
asm!("csrrsi {}, sstatus, 18", out(reg) sstatus);
let mut sstatus = 1usize << 18;
asm!("csrrs {0}, sstatus, {0}", inlateout(reg) sstatus);
sstatus
}

View File

@ -11,5 +11,6 @@ cfg_if! {
}
pub const PHYSICAL_MEMORY_OFFSET: usize = KERNEL_OFFSET - PHYS_MEMORY_BASE;
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; // 8 MB
pub const KERNEL_HEAP_SIZE: usize = 80 * 1024 * 1024; // 80 MB
pub const STACK_PAGES_PER_HART: usize = 32;
pub const MAX_HART_NUM: usize = 8;

View File

@ -1,4 +1,7 @@
use super::{boot_page_table::BootPageTable, consts::PHYSICAL_MEMORY_OFFSET};
use super::{
boot_page_table::BootPageTable,
consts::{MAX_HART_NUM, PHYSICAL_MEMORY_OFFSET, STACK_PAGES_PER_HART},
};
use core::arch::asm;
use device_tree::parse_smp;
use kernel_hal::{
@ -55,13 +58,15 @@ extern "C" fn primary_rust_main(hartid: usize, device_tree_paddr: usize) -> ! {
BOOT_PAGE_TABLE.launch(hartid)
};
println!();
println!("boot page table launched, sstatus = {sstatus:#x}");
println!("parse device tree from {device_tree_paddr:#x}");
let smp = parse_smp(device_tree_paddr);
println!();
println!(
"
boot page table launched, sstatus = {sstatus:#x}
parse device tree from {device_tree_paddr:#x}
"
);
// 启动副核
let smp = parse_smp(device_tree_paddr);
println!("smp = {smp}");
for id in 0..smp {
if id != hartid {
@ -106,9 +111,6 @@ extern "C" fn secondary_rust_main(hartid: usize) -> ! {
/// 裸函数。
#[naked]
unsafe extern "C" fn select_stack(hartid: usize) {
const STACK_PAGES_PER_HART: usize = 16;
const MAX_HART_NUM: usize = 10;
const STACK_LEN_PER_HART: usize = 4096 * STACK_PAGES_PER_HART;
const STACK_LEN_TOTAL: usize = STACK_LEN_PER_HART * MAX_HART_NUM;
@ -121,8 +123,8 @@ unsafe extern "C" fn select_stack(hartid: usize) {
" li t1, {len_per_hart}",
"1: add sp, sp, t1",
" addi t0, t0, -1",
" bgtz t0, 1b",
"2: ret",
" bnez t0, 1b",
" ret",
stack = sym BOOT_STACK,
len_per_hart = const STACK_LEN_PER_HART,
options(noreturn)

View File

@ -0,0 +1,2 @@
/* Generated by build.rs. DO NOT EDIT. */
PROVIDE_HIDDEN(BASE_ADDRESS = 0xffffffff80200000);

View File

@ -25,18 +25,17 @@ SECTIONS
sdata = .;
.data : {
*(.data .data.*)
}
. = ALIGN(4K);
edata = .;
. = ALIGN(4K);
edata = .;
.bss : {
bootstack = .;
*(.bss.bootstack)
bootstacktop = .;
}
sbss = .;
.bss : {
. = ALIGN(4K);
sbss = .;
*(.bss .bss.* .sbss)
}

View File

@ -119,7 +119,8 @@ pub fn wait_for_exit(proc: Option<Arc<Process>>) -> ! {
#[cfg(not(feature = "libos"))]
pub fn wait_for_exit(proc: Option<Arc<Process>>) -> ! {
kernel_hal::timer::timer_set_first();
kernel_hal::timer::timer_enable();
info!("executor run!");
loop {
#[cfg(target_arch = "aarch64")]
let has_task = executor_origin::run_until_idle();

View File

@ -1009,6 +1009,8 @@ pub const USER_ASPACE_BASE: u64 = 0;
// pub const USER_ASPACE_BASE: u64 = 0x0000_0000_0100_0000;
/// The size of user address space
pub const USER_ASPACE_SIZE: u64 = (1u64 << 47) - 4096 - USER_ASPACE_BASE;
/// The default number of user stack pages
pub const USER_STACK_PAGES: usize = 128;
#[cfg(test)]
mod tests {

View File

@ -234,15 +234,12 @@ impl Syscall<'_> {
let mut ret: ZxResult = Ok(());
for disposition in dispositions.iter_mut() {
if let Ok((object, src_rights)) = proc.get_dyn_object_and_rights(disposition.handle) {
match handle_check(disposition, &object, src_rights, handle) {
Err(e) => {
disposition.result = e as _;
if ret.is_ok() {
ret = Err(e);
}
if let Err(e) = handle_check(disposition, &object, src_rights, handle) {
disposition.result = e as _;
if ret.is_ok() {
ret = Err(e);
}
Ok(()) => (),
};
}
let new_rights = if disposition.rights != Rights::SAME_RIGHTS.bits() {
Rights::from_bits(disposition.rights).unwrap()
} else {