Compare commits

..

27 Commits

Author SHA1 Message Date
Luoyuan Xiao 2c903512c8 network functions testing 2022-05-20 00:12:42 +08:00
Luoyuan Xiao ae8450f87c
Merge pull request #244 from shzhxh/net-testing
update test scripts
2021-12-06 16:45:58 +08:00
shzhxh ec695dcac8 update test scripts 2021-12-06 16:42:38 +08:00
GCYY 59181a89f0 add loopback net function 2021-11-02 12:31:58 +08:00
GCYY 475a335d7b fmt 2021-10-31 12:41:58 +08:00
GCYY a7a26829ac Modify ip addr 2021-10-31 00:45:48 +08:00
GCYY 6ea8243554 Merge branch 'net-testing' of https://github.com/rcore-os/zCore into net-testing 2021-10-28 20:23:28 +08:00
Luoyuan Xiao fb13d4078f
Update rtl8211f.rs 2021-10-28 14:39:16 +08:00
elliott10 5d8ce43ca1 Add all network testing threads into future-executor 2021-10-22 21:04:24 +08:00
Luoyuan Xiao 348094a644
Merge pull request #235 from rcore-os/net-testing-pr
add kernel-state network verification function
2021-10-22 18:28:38 +08:00
GCYY c7a4652230 add kernel-state network verification function 2021-10-22 16:26:21 +08:00
Luoyuan Xiao bb99eefe7e
Merge pull request #234 from rcore-os/net-testing-pr
enable user state tcp and udp
2021-10-21 11:26:01 +08:00
GCYY 133e2c75ef modify some net display info 2021-10-20 21:06:30 +08:00
GCYY 4b1123686a modify some net display info 2021-10-20 20:58:33 +08:00
GCYY 953b8d58f4 Merge branch 'net-testing' of https://github.com/rcore-os/zCore into net-testing 2021-10-20 20:12:05 +08:00
GCYY 67b6796757 fmt code 2021-10-20 20:11:31 +08:00
GCYY 535b41e70a modify udp and icmp 2021-10-20 20:10:38 +08:00
GCYY 1110f894a1 modify to make tcp work 2021-10-19 18:25:47 +08:00
GCYY 011a87a003 modify code to adapt socket object 2021-10-19 15:07:43 +08:00
GCYY 99980683e0 add socket object and update smoltcp dependencies 2021-10-19 14:41:53 +08:00
GCYY a3df5ddb69 add socket syscall interface and fmt code 2021-10-19 14:28:01 +08:00
Luoyuan Xiao c5d829bc32
Merge pull request #232 from shzhxh/net-testing
add testcase
2021-10-19 14:23:51 +08:00
shzhxh 1990915537 add testcase 2021-10-19 12:09:51 +08:00
elliott10 a9d85db871 Enable PLIC Irq Manager 2021-10-15 19:57:49 +08:00
elliott10 1ba370de72 D1 board support network: ping, udp, tcp 2021-10-09 16:38:48 +08:00
elliott10 9d0fb90eb9 Virtio Network response PING by smoltcp 2021-10-03 21:24:44 +08:00
elliott10 9757df80ae Add virtio network driver 2021-09-26 22:24:40 +08:00
412 changed files with 20766 additions and 18940 deletions

View File

@ -1,16 +0,0 @@
[alias]
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"

View File

@ -1,13 +0,0 @@
#!/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

View File

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

View File

@ -1,7 +0,0 @@
#!/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

@ -1,149 +0,0 @@
name: Build CI
on:
push:
pull_request:
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: ${{ env.rust_toolchain }}
override: true
components: rust-src, rustfmt, clippy
- name: Check format
uses: actions-rs/cargo@v1
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:
command: doc
args: --all-features --no-deps
build-aarch64:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
override: true
target: aarch64-unknown-linux-gnu
- uses: actions-rs/cargo@v1
with:
command: build
use-cross: true
args: --target aarch64-unknown-linux-gnu --workspace --exclude linux-syscall --exclude zcore-loader --exclude zcore
build-user:
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: ${{ env.rust_toolchain }}
target: x86_64-fuchsia
- name: Build Zircon user programs
run: cd zircon-user && make build MODE=release
test-libos:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
mode: [linux, zircon]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- 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:
command: clippy
args: --package zcore --features "${{ matrix.mode }} libos"
test-bare-metal:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
arch: [x86_64, riscv64]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
- 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 }}
- 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

@ -1,28 +1,20 @@
name: Deploy docs
name: deploy CI
on:
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: ${{ env.rust_toolchain }}
- uses: actions/checkout@v2
- name: Build docs
run: |
cargo doc --no-deps --all-features
.github/scripts/add-doc-index.sh
run: arch=x86_64 cargo doc --no-deps --all-features
# uses: actions-rs/cargo@v1
# with:
# command: doc
# args: --no-deps --all-features
- name: Deploy to Github Pages
if: ${{ github.ref == 'refs/heads/master' }}
uses: JamesIves/github-pages-deploy-action@releases/v3

281
.github/workflows/rustc20210727.yml vendored Normal file
View File

@ -0,0 +1,281 @@
name: current CI
on:
push:
pull_request:
schedule:
- cron: '40 3 * * *' # every day at 3:40
jobs:
check:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-07-27
override: true
components: rustfmt, clippy
- name: Check code format
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Clippy x86_64
run: arch=x86_64 cargo clippy
- name: Clippy riscv64
run: arch=riscv64 cargo clippy
# uses: actions-rs/cargo@v1
# with:
# command: clippy
#x86_64
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-07-27
components: rust-src
- name: Build
run: arch=x86_64 cargo build
# uses: actions-rs/cargo@v1
# with:
# command: build
- name: Build zCore
run: |
cd zCore
make build arch=x86_64
# FIX LATER
# - name: Build zCore with hypervisor
# run: |
# cd zCore
# make build hypervisor=1
build-aarch64:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-07-27
override: true
target: aarch64-unknown-linux-gnu
- uses: actions-rs/cargo@v1
with:
command: build
use-cross: true
args: -p zircon-loader --target aarch64-unknown-linux-gnu
build-user:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
steps:
- uses: actions/checkout@v2
- 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-2021-07-27
target: x86_64-fuchsia
- name: Build Zircon user programs
run: |
cd zircon-user
make build mode=release
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Prepare rootfs
run: make rootfs
- name: Test
run: arch=x86_64 cargo test --all-features --no-fail-fast --workspace --exclude zircon-loader
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --all-features --no-fail-fast --workspace --exclude zircon-loader
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@v2
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-grcov
- name: Gather coverage data
id: coverage
uses: actions-rs/grcov@v0.1
# FIXME: 'error from lcovParse: Failed to parse string'
# - name: Coveralls upload
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: ${{ steps.coverage.outputs.report }}
bench:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Run benchmarks
run: arch=x86_64 cargo bench
# uses: actions-rs/cargo@v1
# with:
# command: bench
core-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
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-2021-07-27
components: rust-src
- name: Install QEMU
run: |
sudo apt update
sudo apt install qemu-system-x86
- name: Build zCore
run: |
cd zCore
make build mode=release
cd ..
- name: Run core-tests
run: |
cd scripts
pip3 install pexpect
python3 core-tests.py
core-test-libos:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
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-2021-07-27
components: rust-src
- name: Run core-tests
run: |
cd scripts
pip3 install pexpect
python3 unix-core-tests.py
libc-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Install musl toolchain
run: sudo apt-get install musl-tools musl-dev -y
- name: Prepare rootfs and libc-test
run: make rootfs && make libc-test
- name: Build
run: arch=x86_64 cargo build --release -p linux-loader
# uses: actions-rs/cargo@v1
# with:
# command: build
# args: --release -p linux-loader
- name: Run libc-tests
run: |
cd scripts
python3 libc-tests.py
cat linux/test-result.txt
doc:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Build docs
run: arch=x86_64 cargo doc --no-deps --all-features
# uses: actions-rs/cargo@v1
# with:
# command: doc
# args: --no-deps --all-features
# - name: Deploy to Github Pages
# if: ${{ github.ref == 'refs/heads/master' }}
# uses: JamesIves/github-pages-deploy-action@releases/v3
# with:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# BRANCH: gh-pages
# FOLDER: target/doc
baremetal-libc-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-07-27
components: rust-src
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Install musl toolchain qemu-system-x86
run: |
sudo apt-get update
sudo apt-get install musl-tools musl-dev qemu-system-x86 -y
- name: Prepare rootfs and libc-test
run: make baremetal-test-img
- name: Build kernel
run: cd zCore && make build mode=release linux=1 arch=x86_64
- name: create qemu disk
run: cd zCore && make baremetal-qemu-disk mode=release linux=1 arch=x86_64
- name: Run baremetal-libc-test
run: |
cd scripts
python3 ./baremetal-libc-test.py
baremetal-rv64-oscomp-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-07-27
components: rust-src
- name: Install cargo tools and qemu-system-riscv64
run: |
sudo apt update
sudo apt install qemu-utils
cargo install cargo-binutils
rustup component add llvm-tools-preview
wget https://github.com/rcore-os/qemu-prebuilt/releases/download/5.2.0-riscv64/qemu-system-riscv64.tar.xz > /dev/null
tar xJf qemu-system-riscv64.tar.xz && sudo cp qemu-system-riscv64 /usr/local/bin
wget https://github.com/rcore-os/qemu-prebuilt/releases/download/qemu-share/qemu-share.tar.xz > /dev/null
tar xJf qemu-share.tar.xz && sudo cp -r qemu /usr/local/share/
- name: Prepare rootfs and oscomp
run: make riscv-image
- name: Run baremetal-libc-test
run: |
cd scripts
python3 baremetal-test-riscv64.py

295
.github/workflows/rustcnightly.yml vendored Normal file
View File

@ -0,0 +1,295 @@
name: rustcnightly CI
on:
push:
pull_request:
schedule:
- cron: '40 3 * * *' # every day at 3:40
jobs:
check:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt, clippy
- run: echo "nightly" >rust-toolchain
- name: Check code format
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Clippy x86_64
run: arch=x86_64 cargo clippy
- name: Clippy riscv64
run: arch=riscv64 cargo clippy
# uses: actions-rs/cargo@v1
# with:
# command: clippy
#x86_64
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
components: rust-src
- run: echo "nightly" >rust-toolchain
- run: echo "nightly" >./rboot/rust-toolchain
- name: Build
run: arch=x86_64 cargo build
# uses: actions-rs/cargo@v1
# with:
# command: build
- name: Build zCore
run: |
cd zCore
make build arch=x86_64
# FIX LATER
# - name: Build zCore with hypervisor
# run: |
# cd zCore
# make build hypervisor=1
build-aarch64:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
target: aarch64-unknown-linux-gnu
- run: echo "nightly" >rust-toolchain
- uses: actions-rs/cargo@v1
with:
command: build
use-cross: true
args: -p zircon-loader --target aarch64-unknown-linux-gnu
build-user:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
steps:
- uses: actions/checkout@v2
- 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
target: x86_64-fuchsia
- run: echo "nightly" >rust-toolchain
- name: Build Zircon user programs
run: |
cd zircon-user
make build mode=release
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Prepare rootfs
run: make rootfs
- run: echo "nightly" >rust-toolchain
- name: Test
run: arch=x86_64 cargo test --all-features --no-fail-fast --workspace --exclude zircon-loader
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --all-features --no-fail-fast --workspace --exclude zircon-loader
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@v2
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-grcov
- name: Gather coverage data
id: coverage
uses: actions-rs/grcov@v0.1
# FIXME: 'error from lcovParse: Failed to parse string'
# - name: Coveralls upload
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: ${{ steps.coverage.outputs.report }}
bench:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Run benchmarks
run: arch=x86_64 cargo bench
# uses: actions-rs/cargo@v1
# with:
# command: bench
core-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
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
components: rust-src
- name: Install QEMU
run: |
sudo apt update
sudo apt install qemu-system-x86
- run: echo "nightly" >rust-toolchain
- run: echo "nightly" >./rboot/rust-toolchain
- name: Build zCore
run: |
cd zCore
make build mode=release
cd ..
- name: Run core-tests
run: |
cd scripts
pip3 install pexpect
python3 core-tests.py
core-test-libos:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
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
components: rust-src
- name: Run core-tests
run: |
cd scripts
pip3 install pexpect
python3 unix-core-tests.py
libc-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Install musl toolchain
run: sudo apt-get install musl-tools musl-dev -y
- name: Prepare rootfs and libc-test
run: make rootfs && make libc-test
- run: echo "nightly" >rust-toolchain
- name: Build
run: arch=x86_64 cargo build --release -p linux-loader
# uses: actions-rs/cargo@v1
# with:
# command: build
# args: --release -p linux-loader
- name: Run libc-tests
run: |
cd scripts
python3 libc-tests.py
cat linux/test-result.txt
doc:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- run: echo "nightly" >rust-toolchain
- name: Build docs
run: arch=x86_64 cargo doc --no-deps --all-features
# uses: actions-rs/cargo@v1
# with:
# command: doc
# args: --no-deps --all-features
# - name: Deploy to Github Pages
# if: ${{ github.ref == 'refs/heads/master' }}
# uses: JamesIves/github-pages-deploy-action@releases/v3
# with:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# BRANCH: gh-pages
# FOLDER: target/doc
baremetal-libc-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
components: rust-src
- run: echo "nightly" >rust-toolchain
- run: echo "nightly" >./rboot/rust-toolchain
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/linux/libc-libos.so
- name: Install musl toolchain qemu-system-x86
run: |
sudo apt-get update
sudo apt-get install musl-tools musl-dev qemu-system-x86 -y
- name: Prepare rootfs and libc-test
run: make baremetal-test-img
- name: Build kernel
run: cd zCore && make build mode=release linux=1 arch=x86_64
- name: create qemu disk
run: cd zCore && make baremetal-qemu-disk mode=release linux=1 arch=x86_64
- name: Run baremetal-libc-test
run: |
cd scripts
python3 ./baremetal-libc-test.py
baremetal-rv64-oscomp-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
components: rust-src
- run: echo "nightly" >rust-toolchain
- run: echo "nightly" >./rboot/rust-toolchain
- name: Install cargo tools and qemu-system-riscv64
run: |
sudo apt-get update
sudo apt install qemu-utils
cargo install cargo-binutils
rustup component add llvm-tools-preview
wget https://github.com/rcore-os/qemu-prebuilt/releases/download/5.2.0-riscv64/qemu-system-riscv64.tar.xz > /dev/null
tar xJf qemu-system-riscv64.tar.xz && sudo cp qemu-system-riscv64 /usr/local/bin
wget https://github.com/rcore-os/qemu-prebuilt/releases/download/qemu-share/qemu-share.tar.xz > /dev/null
tar xJf qemu-share.tar.xz && sudo cp -r qemu /usr/local/share/
- name: Prepare rootfs and oscomp
run: make riscv-image
- name: Run baremetal-libc-test
run: |
cd scripts
python3 baremetal-test-riscv64.py

View File

@ -1,281 +0,0 @@
name: Test CI
on:
push:
pull_request:
schedule:
- cron: '0 22 * * *' # every day at 22:00 UTC
env:
rust_toolchain: nightly-2022-01-20
qemu_version: 7.0.0
jobs:
unit-test:
name: Unit Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- 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
# FIXME: 'error from lcovParse: Failed to parse string'
# - name: Coveralls upload
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: ${{ steps.coverage.outputs.report }}
bench-test:
name: Bench Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- 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'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- 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'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Install dependencies
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
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- 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: .github/scripts/install-deps.sh ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
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: |
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
matrix:
arch: [x86_64, riscv64]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
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
- name: Install dependencies
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-${{ 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: |
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
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
matrix:
arch: [x86_64, riscv64]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
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
- name: Install dependencies
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-${{ 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: |
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
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 }}

29
.gitignore vendored
View File

@ -1,18 +1,21 @@
**/.*
!.github/
!.cargo/
!.vscode/settings.json
/target
**/target
**/*.rs.bk
*.img
*.bin
*.log
Cargo.lock
/ignored
/rootfs
zCore/src/platform/riscv/kernel-vars.ld
/riscv_rootfs
/prebuilt/linux/alpine*
/prebuilt/linux/riscv64/prebuild*
.idea
scripts/linux/test-result.txt
rusty-tags.vi
*.img
zCore/src/link_user.S
scripts/rboot.conf
stdout-baremetal-test
stdout-zcore
scripts/script.sh
stdout-baremetal-test-rv64
stdout-rv64
zCore/fw-zCore.bin
.DS_Store
__pycache__

6
.gitmodules vendored
View File

@ -1,9 +1,3 @@
[submodule "rboot"]
path = rboot
url = https://github.com/rcore-os/rboot.git
[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

10
.vscode/settings.json vendored
View File

@ -1,10 +0,0 @@
{
// 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

@ -1,17 +1,18 @@
[workspace]
members = [
"drivers",
"kernel-hal",
"zircon-object",
"zircon-syscall",
"zircon-loader",
"linux-object",
"linux-syscall",
"loader",
"zCore",
"xtask",
"zircon-syscall",
"linux-loader",
"kernel-hal-unix",
"kernel-hal",
]
default-members = ["xtask"]
exclude = ["zircon-user", "rboot"]
[profile.release]
lto = true
exclude = [
"zircon-user",
"zCore",
"rboot",
"linux-syscall",
"kernel-hal-bare",
]

123
Makefile
View File

@ -1,53 +1,106 @@
# Makefile for top level of zCore
ROOTFS_TAR := alpine-minirootfs-3.12.0-x86_64.tar.gz
ROOTFS_URL := http://dl-cdn.alpinelinux.org/alpine/v3.12/releases/x86_64/$(ROOTFS_TAR)
RISCV64_ROOTFS_TAR := prebuild.tar.xz
RISCV64_ROOTFS_URL := https://github.com/rcore-os/libc-test-prebuilt/releases/download/0.1/$(RISCV64_ROOTFS_TAR)
ARCH ?= x86_64
rcore_fs_fuse_revision := 7f5eeac
OUT_IMG := zCore/$(ARCH).img
TMP_ROOTFS := /tmp/rootfs
.PHONY: help setup update rootfs libc-test other-test image check doc clean
# for linux syscall tests
TEST_DIR := linux-syscall/test/
DEST_DIR := rootfs/bin/
TEST_PATH := $(wildcard $(TEST_DIR)*.c)
BASENAMES := $(notdir $(basename $(TEST_PATH)))
# print top level help
help:
cargo xtask help
CFLAG := -Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1
# setup git lfs and git submodules
setup:
cargo setup
.PHONY: rootfs libc-test rcore-fs-fuse image
# update toolchain and dependencies
update:
cargo update-all
prebuilt/linux/$(ROOTFS_TAR):
wget $(ROOTFS_URL) -O $@
# put rootfs for linux mode
rootfs:
cargo rootfs --arch $(ARCH)
prebuilt/linux/riscv64/$(RISCV64_ROOTFS_TAR):
@wget $(RISCV64_ROOTFS_URL) -O $@
rootfs: prebuilt/linux/$(ROOTFS_TAR)
rm -rf rootfs && mkdir -p rootfs
tar xf $< -C rootfs
# libc-libos.so (convert syscall to function call) is from https://github.com/rcore-os/musl/tree/rcore
cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
@for VAR in $(BASENAMES); do gcc $(TEST_DIR)$$VAR.c -o $(DEST_DIR)$$VAR $(CFLAG); done
riscv-rootfs:prebuilt/linux/riscv64/$(RISCV64_ROOTFS_TAR)
@rm -rf riscv_rootfs && mkdir -p riscv_rootfs
@tar -xvf $< -C riscv_rootfs --strip-components 1
@ln -s busybox riscv_rootfs/bin/ls
# put libc tests into rootfs
libc-test:
cargo libc-test --arch $(ARCH)
cd rootfs && git clone git://repo.or.cz/libc-test --depth 1
cd rootfs/libc-test && cp config.mak.def config.mak && echo 'CC := musl-gcc' >> config.mak && make -j
# put other tests into rootfs
other-test:
cargo other-test --arch $(ARCH)
rcore-fs-fuse:
ifneq ($(shell rcore-fs-fuse dir image git-version), $(rcore_fs_fuse_revision))
@echo Installing rcore-fs-fuse
@cargo install rcore-fs-fuse --git https://github.com/rcore-os/rcore-fs --rev $(rcore_fs_fuse_revision) --force
endif
# build image from rootfs
image:
cargo image --arch $(ARCH)
$(OUT_IMG): prebuilt/linux/$(ROOTFS_TAR) rcore-fs-fuse
@echo Generating $(ARCH).img
@rm -rf $(TMP_ROOTFS)
@mkdir -p $(TMP_ROOTFS)
@tar xf $< -C $(TMP_ROOTFS)
@cp $(TMP_ROOTFS)/lib/ld-musl-x86_64.so.1 rootfs/lib/
@rcore-fs-fuse $@ rootfs zip
# recover rootfs/ld-musl-x86_64.so.1 for zcore usr libos
# libc-libos.so (convert syscall to function call) is from https://github.com/rcore-os/musl/tree/rcore
@cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
# check code style
check:
cargo check-style
image: $(OUT_IMG)
@echo Resizing $(ARCH).img
@qemu-img resize $(OUT_IMG) +5M
# build and open project document
doc:
cargo doc --open
# clean targets
riscv-image: rcore-fs-fuse riscv-rootfs
@echo building riscv.img
@rcore-fs-fuse zCore/riscv64.img riscv_rootfs zip
@qemu-img resize -f raw zCore/riscv64.img +5M
clean:
cargo clean
rm -rf rootfs
rm -rf ignored/target
find zCore -maxdepth 1 -name "*.img" -delete
rm -rf rootfs
rm -rf riscv-rootfs
find zCore/target -type f -name "*.zbi" -delete
find zCore/target -type f -name "*.elf" -delete
cd linux-syscall/test-oscomp && make clean
cd linux-syscall/busybox && make clean
cd linux-syscall/lua && make clean
cd linux-syscall/lmbench && make clean
rt-test:
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.
doc:
arch=x86_64 cargo doc --open
baremetal-test-img: prebuilt/linux/$(ROOTFS_TAR) rcore-fs-fuse
@echo Generating $(ARCH).img
@rm -rf $(TMP_ROOTFS)
@mkdir -p $(TMP_ROOTFS)
@tar xf $< -C $(TMP_ROOTFS)
@mkdir -p rootfs/lib
@cp $(TMP_ROOTFS)/lib/ld-musl-x86_64.so.1 rootfs/lib/
@cd rootfs && rm -rf libc-test && git clone git://repo.or.cz/libc-test --depth 1
@cd rootfs/libc-test && cp config.mak.def config.mak && echo 'CC := musl-gcc' >> config.mak && make -j
@rcore-fs-fuse $(OUT_IMG) rootfs zip
# recover rootfs/ld-musl-x86_64.so.1 for zcore usr libos
# libc-libos.so (convert syscall to function call) is from https://github.com/rcore-os/musl/tree/rcore
@cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
@echo Resizing $(ARCH).img
@qemu-img resize $(OUT_IMG) +5M
baremetal-test:
@make -C zCore baremetal-test mode=release linux=1 | tee stdout-baremetal-test
baremetal-test-rv64:
@make -C zCore baremetal-test-rv64 arch=riscv64 mode=release linux=1 ROOTPROC=$(ROOTPROC) | tee -a stdout-baremetal-test-rv64 | tee stdout-rv64

195
README.md
View File

@ -1,15 +1,11 @@
# zCore
[![CI](https://github.com/rcore-os/zCore/workflows/CI/badge.svg?branch=master)](https://github.com/rcore-os/zCore/actions)
[![Docs](https://img.shields.io/badge/docs-alpha-blue)](https://rcore-os.github.io/zCore/)
[![Docs](https://img.shields.io/badge/docs-alpha-blue)](https://rcore-os.github.io/zCore/zircon_object/)
[![Coverage Status](https://coveralls.io/repos/github/rcore-os/zCore/badge.svg?branch=master)](https://coveralls.io/github/rcore-os/zCore?branch=master)
Reimplement [Zircon][zircon] microkernel in safe Rust as a userspace program!
## Manual
[This](docs/Manual.md) is a new simple chinese manual.
## Dev Status
🚧 Working In Progress
@ -17,26 +13,25 @@ Reimplement [Zircon][zircon] microkernel in safe Rust as a userspace program!
- 2020.04.16: Zircon console is working on zCore! 🎉
## Quick start for RISCV64
```sh
```
make riscv-image
cd zCore
make run ARCH=riscv64 LINUX=1
make run arch=riscv64 linux=1
```
## Getting started
Environments
- [Rust toolchain](http://rustup.rs)
- [QEMU](https://www.qemu.org)
- [Git LFS](https://git-lfs.github.com)
* [Rust toolchain](http://rustup.rs)
* [QEMU](https://www.qemu.org)
* [Git LFS](https://git-lfs.github.com)
### Developing environment info
- current rustc -- rustc 1.60.0-nightly (5e57faa78 2022-01-19)
- current rust-toolchain -- nightly-2022-01-20
- current qemu -- 5.2.0 -> 6.2.0
- current rustc -- rustc 1.56.0-nightly (08095fc1f 2021-07-26)
- current rust-toolchain -- nightly-2021-07-27
- current qemu -- 5.2.0
Clone repo and pull prebuilt fuchsia images:
@ -52,118 +47,77 @@ For users in China, there's a mirror you can try:
```sh
git clone https://github.com.cnpmjs.org/rcore-os/zCore --recursive
```
### Run zcore in libos mode
#### Run zcore in linux-libos mode
##### step1: Prepare Alpine Linux rootfs:
- step 1: Prepare Alpine Linux rootfs:
```sh
make rootfs
```
```sh
make rootfs
```
##### step2: Compile&Run native Linux program (Busybox) in libos mode:
- step 2: Compile & Run native Linux program (Busybox) in libos mode:
```sh
cargo run --release --features "linux libos" -- /bin/busybox [args]
```
You can also add the feature `graphic` to show the graphical output (with [sdl2](https://www.libsdl.org) installed).
To debug, set the `LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
```sh
cargo run --release -p linux-loader -- /bin/busybox [args]
```
#### Run native Zircon program (shell) in zircon-libos mode:
- step 1: Compile and Run Zircon shell
#### step1: Compile and Run Zircon shell
```sh
cargo run --release --features "zircon libos" -- prebuilt/zircon/x64/bringup.zbi
```
```sh
cargo run --release -p zircon-loader -- prebuilt/zircon/x64
```
The `graphic` and `LOG` options are the same as Linux.
To debug, set `RUST_LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
### Run zcore in bare-metal mode
#### Run Linux shell in linux-bare-metal mode:
##### step1: Prepare Alpine Linux rootfs:
- step 1: Prepare Alpine Linux rootfs:
```sh
make rootfs
```
##### step2: Create Linux rootfs image:
Note: Before below step, you can add some special apps in zCore/rootfs
```sh
make rootfs
```
```sh
make image
```
##### step3: build and run zcore in linux-bare-metal mode:
- step 2: Create Linux rootfs image:
Note: Before below step, you can add some special apps in zCore/rootfs
```sh
make image
```
- step 3: Build and run zcore in linux-bare-metal mode:
```sh
cd zCore && make run MODE=release LINUX=1 [LOG=warn] [GRAPHIC=on] [ACCEL=1]
```
```sh
cd zCore && make run mode=release linux=1 [graphic=on] [accel=1]
```
#### Run Zircon shell in zircon-bare-metal mode:
##### step1.1 : build and run zcore in zircon-bare-metal mode:
- step 1: Build and run zcore in zircon-bare-metal mode:
```sh
cd zCore && make run mode=release [graphic=on] [accel=1]
```
```sh
cd zCore && make run MODE=release [LOG=warn] [GRAPHIC=on] [ACCEL=1]
```
##### step1.2 : Build and run your own Zircon user programs:
- step 2: Build and run your own Zircon user programs:
```sh
# See template in zircon-user
cd zircon-user && make zbi mode=release
```sh
# See template in zircon-user
cd zircon-user && make zbi MODE=release
# Run your programs in zCore
cd zCore && make run mode=release user=1
```
To debug, set `RUST_LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
# Run your programs in zCore
cd zCore && make run MODE=release USER=1 [LOG=warn] [GRAPHIC=on] [ACCEL=1]
```
## Testing
### LibOS Mode Testing
#### Zircon related
Run Zircon official core-tests:
```sh
pip3 install pexpect
cd scripts && python3 unix-core-testone.py 'Channel.*'
```
Run all (non-panicked) core-tests for CI:
```sh
pip3 install pexpect
cd scripts && python3 unix-core-tests.py
# Check `zircon/test-result.txt` for results.
```
#### Linux related
Run Linux musl libc-tests for CI:
```sh
make rootfs && make libc-test
cd scripts && python3 libos-libc-tests.py
# Check `linux/test-result.txt` for results.
```
### Bare-metal Mode Testing
#### Zircon related
Run Zircon official core-tests on bare-metal:
```sh
cd zCore && make test MODE=release [ACCEL=1] TEST_FILTER='Channel.*'
cd zCore && make test mode=release [accel=1] test_filter='Channel.*'
```
Run all (non-panicked) core-tests for CI:
@ -173,68 +127,74 @@ pip3 install pexpect
cd scripts && python3 core-tests.py
# Check `zircon/test-result.txt` for results.
```
#### x86-64 Linux related
#### Linux related
Run Linux musl libc-tests for CI:
```sh
make rootfs && make libc-test
cd scripts && python3 libc-tests.py
# Check `linux/test-result.txt` for results.
```
### Baremetal Mode Testing
#### x86-64 Linux related
Run Linux musl libc-tests for CI:
```
## Prepare rootfs with libc-test apps
make baremetal-test-img
## Build zCore kernel
cd zCore && make build MODE=release LINUX=1 ARCH=x86_64
cd zCore && make build mode=release linux=1 arch=x86_64
## Testing
cd scripts && python3 baremetal-libc-test.py
##
cd ../scripts && python3 ./baremetal-libc-test.py
##
```
You can use [`scripts/baremetal-libc-test-ones.py`](./scripts/baremetal-libc-test-ones.py) & [`scripts/linux/baremetal-test-ones.txt`](./scripts/linux/baremetal-test-ones.txt) to test specified apps.
You can use [`scripts/baremetal-libc-test-ones.py`](./scripts/baremetal-libc-test-ones.py) & [`scripts/linux/baremetal-test-ones.txt`](./scripts/linux/baremetal-test-ones.txt) to test specified apps.
[`scripts/linux/baremetal-test-fail.txt`](./scripts/linux/baremetal-test-fail.txt) includes all failed x86-64 apps (We need YOUR HELP to fix bugs!)
#### riscv-64 Linux related
Run Linux musl libc-tests for CI:
```sh
```
## Prepare rootfs with libc-test & oscomp apps
make riscv-image
## Build zCore kernel & Testing
cd scripts && python3 baremetal-test-riscv64.py
##
cd ../scripts && python3 baremetal-test-riscv64.py
##
```
You can use[scripts/baremetal-libc-test-ones-riscv64.py](./scripts/baremetal-libc-test-ones-riscv64.py) & [`scripts/linux/baremetal-test-ones-rv64.txt`](scripts/linux/baremetal-test-ones-rv64.txt)to test
You can use[ `scripts/baremetal-libc-test-ones-riscv64.py`](./scripts/baremetal-libc-test-ones-riscv64.py) & [`scripts/linux/baremetal-test-ones-rv64.txt`](scripts/linux/baremetal-test-ones-rv64.txt)to test
specified apps.
[`scripts/linux/baremetal-test-fail-riscv64.txt`](./scripts/linux/baremetal-test-fail-riscv64.txt)includes all failed riscv-64 apps (We need YOUR HELP to fix bugs!)
## Graph/Game
snake game: <https://github.com/rcore-os/rcore-user/blob/master/app/src/snake.c>
snake game: https://github.com/rcore-os/rcore-user/blob/master/app/src/snake.c
### Step1: compile usr app
We can use musl-gcc compile it in x86_64 mode
### Step2: change zcore for run snake app first.
change zCore/zCore/main.rs L176
vec!["/bin/busybox".into(), "sh".into()]
TO
vec!["/bin/snake".into(), "sh".into()]
### Step3: prepare root fs image, run zcore in linux-bare-metal mode
exec:
```sh
```
cd zCore #zCore ROOT DIR
make rootfs
cp ../rcore-user/app/snake rootfs/bin #copy snake ELF file to rootfs/bin
make image # build rootfs image
cd zCore #zCore kernel dir
make run MODE=release LINUX=1 GRAPHIC=on
make run mode=release linux=1 graphic=on
```
Then you can play the game.
@ -250,15 +210,11 @@ Operation
- `Middle`: Pause/Resume
## Doc
```
make doc
```
### RISC-V 64 porting info
### riscv64 porting info
- [porting riscv64 doc](./docs/porting-rv64.md)
## Components
### Overview
@ -278,5 +234,4 @@ make doc
| Exception Handling | Interrupt | Signal |
### Small Goal & Little Plans
- <https://github.com/rcore-os/zCore/wiki/Plans>
- https://github.com/rcore-os/zCore/wiki/Plans

View File

@ -1,86 +0,0 @@
# zCore 项目使用指南
## 预定功能
预定功能指的是 zCore 作为一个项目,为开发者和用户常用操作提供的封装。
由于历史原因,目前预定功能分为顶层预定功能和内核预定功能。
所有顶层提供的预定功能都定义于 [顶层 Makefile](../Makefile)
并且所有预定功能最终都将移动到顶层。
## 常规操作流程
对于一般开发者和用户,可以按以下步骤设置 zCore 项目。
1. 先决条件
目前已测试的开发环境包括 Ubuntu20.04、Ubuntu22.04 和 Debian11
Ubuntu22.04 不能正确编译 x86_64 的 libc 测试。
若不需要烧写到物理硬件,使用 WSL2 或其他虚拟机的操作与真机并无不同之处。
在开始之前,确保你的计算机上安装了 git 和 rustup。要在虚拟环境开发或测试需要 QEMU。
2. 克隆项目
```bash
git clone https://github.com/rcore-os/zCore.git
```
3. 初始化存储库
```bash
make setup
```
4. 保持更新
```bash
make update
```
5. 探索更多操作
```bash
make help
```
6. 推到仓库前,现在本机执行测试
```bash
make check # CI/build 的一部分,未来会实现更多快速测试指令
```
## Linux 模式
zCore 根据向用户提供的系统调用的不同,可分为 zircon 模式和 linux 模式。
要以 linux 模式启动,需要先构建 linux 的启动文件系统。
这个指令构建适于 x86_64 架构的启动文件系统。
```bash
make rootfs ARCH=x86_64
```
这个指令构建适于 riscv64 架构的启动文件系统。
```bash
make rootfs ARCH=riscv64
```
要执行 musl-libc 测试集,需要向文件系统中添加 libc 测试集:
```bash
make libc-test <ARCH=?>
```
要执行 CI 的其他测试,需要向文件系统中添加相应测试集:
```bash
make other-test <ARCH=?>
```
要以裸机模式启动 zCore需要构造将放到设备或虚拟环境中的镜像文件
```bash
make image <ARCH=?>
```

View File

@ -1,68 +1,49 @@
# zCore for riscv64
## 编译 zCore 系统镜像
## 编译zCore系统镜像
先在源码根目录下编译 riscv64 的文件系统。
先在源码根目录下编译riscv64的文件系统。<br>
然后进入子目录zCore编译内核,会生成系统镜像`target/riscv64/debug/zcore.bin`
然后进入子目录 zCore 编译内核,会生成系统镜像`zcore.bin`
```sh
```
make riscv-image
cd zCore
make build LINUX=1 ARCH=riscv64 PLATFORM=d1 MODE=release
make build linux=1 arch=riscv64 board=d1
```
## riscv64 开发板的烧写
## riscv64开发板的烧写
以全志D1 c906开发板为例.<br>
以全志 D1 c906 开发板为例。
下载并编译烧写工具 `xfel`:
```sh
下载并编译烧写工具xfel:
```
git clone https://github.com/xboot/xfel.git
cd xfel
make
```
### 自动烧写运行:
安装好工具 `xfel`,开发板进入 FEL 模式,可在开发板的 Linux 系统中执行 `reboot efex` 命令进入 FEL 模式。然后运行:
```sh
make run_d1 LINUX=1 ARCH=riscv64 PLATFORM=d1 MODE=release
生成包含了opensbi和zCore的待烧写固件:
```
cp ../prebuilt/firmware/fw_jump-0x40020000.bin fw-zCore.bin
dd if=target/riscv64/debug/zcore.bin of=fw-zCore.bin bs=1 seek=131072
```
### 手动烧写运行:
启动全志D1 c906开发板并进入FEL模式。可在开发板的Linux系统中执行`reboot efex`命令进入FEL模式。<br>
然后通过烧写工具xfel把zCore系统镜像载入到DDR中并运行
```
sudo xfel ddr ddr3
sudo xfel write 0x40000000 fw-zCore.bin
sudo xfel exec 0x40000000
```
1. 下载 D1 开发板的 [OpenSBI](https://github.com/elliott10/opensbi) 源码,并编译出镜像 build/platform/thead/c910/firmware/fw_payload.elf
```sh
git clone https://github.com/elliott10/opensbi -b thead
cd opensbi
make PLATFORM=thead/c910 CROSS_COMPILE=/path/to/toolchain/bin/riscv64-unknown-linux-gnu- SUNXI_CHIP=sun20iw1p1 PLATFORM_RISCV_ISA=rv64gcxthead
```
或使用预编译的镜像 [prebuilt/firmware/d1/fw_payload.elf](../prebuilt/firmware/d1/fw_payload.elf)。
2. 生成包含了 OpenSBI, dtb, zCore 的待烧写固件:
```sh
rust-objcopy --binary-architecture=riscv64 ../prebuilt/firmware/d1/fw_payload.elf --strip-all -O binary ./zcore_d1.bin
dd if=../target/riscv64/release/zcore.bin of=zcore_d1.bin bs=512 seek=2048
```
3. 启动全志 D1 c906 开发板,并进入 FEL 模式。然后通过烧写工具 `xfel` 把 zCore 系统镜像载入到 DDR 中:
```
sudo xfel ddr ddr3
sudo xfel write 0x40000000 zcore_d1.bin
sudo xfel exec 0x40000000
```
或者在安装好工具xfel开发板进入FEL模式后直接运行
```
make run-thead linux=1 arch=riscv64 board=d1
```
## 引导运行
zCore 成功引导后, OpenSBI 会将 dtb 加载到高地址 `0x5ff00000`,运行如下所示:
zCore成功引导后将如下所示
```
OpenSBI smartx-d1-tina-v1.0.1-release
____ _____ ____ _____
@ -100,7 +81,7 @@ Uart output testing
+++ Setting up UART interrupts +++
+++ Setting up PLIC +++
+++ setup interrupt +++
Exception::Breakpoint: A breakpoint set @0xffffffffc0167f56
Exception::Breakpoint: A breakpoint set @0xffffffffc0167f56
Device Tree @ 0x0
[138.8430296s WARN 0 0:0] elf relocate Err:".rela.dyn not found"
[139.2137079s WARN 0 0:0] brk: unimplemented
@ -114,6 +95,52 @@ bin dev tmp
/ # hello
Hello world from user mode program!
By xiaoluoyuan@163.com
/ #
/ #
```
## 网络测试时的网桥
```
# 创建网桥
ip link add name br0 type bridge
ip link set br0 up
# 添加ip地址 (addr当前网卡地址)
ip addr add [addr] brd + dev br0
# 创建tuntap
ip tuntap add dev tap0 mode tap user own
ip link set dev tap0 up
# 添加进网桥
ip link set tap0 master br0
ip link set eth0 master br0
# 刷掉eth0的数据
ip addr flush dev eth0
# 把br0变成默认网关
ip route add default via [route] dev br0
```
## 执行测例
```
# zCore在D1上运行起来后测试主机上退出串口连接程序
cd scripts
# 运行第一部分的测例
cp linux/baremetal-test-ones.txt.1 linux/baremetal-test-ones.txt
sudo python3 baremetal-libc-test-d1.py
# 运行第二部分的测例
cp linux/baremetal-test-ones.txt.2 linux/baremetal-test-ones.txt
sudo python3 baremetal-libc-test-d1.py
# 运行第三部分的测例
cp linux/baremetal-test-ones.txt.3 linux/baremetal-test-ones.txt
sudo python3 baremetal-libc-test-d1.py
# 运行第四部分的测例
cp linux/baremetal-test-ones.txt.4 linux/baremetal-test-ones.txt
sudo python3 baremetal-libc-test-d1.py
# 运行网络的测例
sudo python3 baremetal-net-test-d1.py
```

View File

@ -1,46 +0,0 @@
[package]
name = "zcore-drivers"
version = "0.1.0"
authors = ["Yuekai Jia <equation618@gmail.com>"]
edition = "2018"
description = "Device drivers of zCore"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
graphic = ["rcore-console"]
mock = ["async-std", "sdl2"]
virtio = ["virtio-drivers"]
[dependencies]
log = "0.4"
spin = "0.9"
cfg-if = "1.0"
bitflags = "1.3"
lazy_static = "1.4"
numeric-enum-macro = "0.2"
device_tree = { git = "https://github.com/rcore-os/device_tree-rs", rev = "2f2e55f" }
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev = "88e871a5" }
pci = { git = "https://github.com/elliott10/pci-rs", rev = "8f33774b" }
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "2aaf7d6", optional = true }
rcore-console = { git = "https://github.com/rcore-os/rcore-console", default-features = false, rev = "ca5b1bc", optional = true }
lock = { git = "https://github.com/DeathWish5/kernel-sync", rev = "01b2e70" }
isomorphic_drivers = { git = "https://github.com/rcore-os/isomorphic_drivers", rev = "f7cd97a8", features = ["log"] }
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "35e833e3", default-features = false, features = ["log", "alloc", "verbose", "proto-ipv4", "proto-ipv6", "proto-igmp", "medium-ip", "medium-ethernet", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "async"] }
# LibOS mode
[target.'cfg(not(target_os = "none"))'.dependencies]
async-std = { version = "1.10", optional = true }
sdl2 = { version = "0.34", optional = true }
# Bare-metal mode
[target.'cfg(target_os = "none")'.dependencies]
[target.'cfg(target_arch = "x86_64")'.dependencies]
acpi = "4.1"
x2apic = "0.4"
x86_64 = "0.14"
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = "0.8"

View File

@ -1,270 +0,0 @@
// 解析设备树,创建已知的设备并为它们注册中断。
//
// 涉及到中断的设备包括:
//
// - 接收中断的中断控制器
// - 发出中断的设备
//
// 有效的中断控制器应该具有下列三个属性:
//
// - `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>.
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use super::IoMapper;
use crate::{
utils::devicetree::{
parse_interrupts, parse_reg, Devicetree, InheritProps, InterruptsProp, Node, StringList,
},
Device, DeviceError, DeviceResult, VirtAddr,
};
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.
pub struct DevicetreeDriverBuilder<M: IoMapper> {
dt: Devicetree,
io_mapper: M,
}
impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Prepare to parse DTB from the given virtual address.
pub fn new(dtb_base_vaddr: VirtAddr, io_mapper: M) -> DeviceResult<Self> {
Ok(Self {
dt: Devicetree::from(dtb_base_vaddr)?,
io_mapper,
})
}
/// 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(); // phandle -> intc
let mut dev_list = Vec::new(); // devices
// 解析设备树
self.dt.walk(&mut |node, comp, props| {
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),
}
};
match res {
Ok(dev) => dev_list.push(dev),
Err(DeviceError::NotSupported) => {}
Err(err) => warn!("{MODULE}: failed to parsing node {:?}: {err:?}", node.name),
}
});
// 注册中断
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(|(dev, _)| dev).collect())
}
}
#[allow(dead_code)]
#[allow(unused_imports)]
#[allow(unused_variables)]
#[allow(unreachable_code)]
impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Parse nodes for interrupt controllers.
fn parse_intc(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> 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)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
use crate::irq::*;
let dev = Device::Irq(match comp {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
c if c.contains("riscv,cpu-intc") => Arc::new(riscv::Intc::new()),
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
c if c.contains("riscv,plic0") => Arc::new(riscv::Plic::new(base_vaddr?)),
_ => return Err(DeviceError::NotSupported),
});
Ok((
(dev, interrupts_extended),
IntcProps {
phandle,
interrupt_cells,
},
))
}
/// Parse nodes for virtio devices over MMIO.
#[cfg(feature = "virtio")]
fn parse_virtio(&self, node: &Node, props: &InheritProps) -> DeviceResult<DevWithInterrupt> {
use crate::virtio::*;
use virtio_drivers::{DeviceType, VirtIOHeader};
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
})?;
let header = unsafe { &mut *(base_vaddr as *mut VirtIOHeader) };
if !header.verify() {
return Err(DeviceError::NotSupported);
}
info!(
"{MODULE}: detected virtio device: vendor_id={:#X}, type={:?}",
header.vendor_id(),
header.device_type()
);
let dev = match header.device_type() {
DeviceType::Block => Device::Block(Arc::new(VirtIoBlk::new(header)?)),
DeviceType::GPU => Device::Display(Arc::new(VirtIoGpu::new(header)?)),
DeviceType::Input => Device::Input(Arc::new(VirtIoInput::new(header)?)),
DeviceType::Console => Device::Uart(Arc::new(VirtIoConsole::new(header)?)),
_ => return Err(DeviceError::NotSupported),
};
Ok((dev, interrupts_extended))
}
/// Parse nodes for Ethernet devices.
fn parse_ethernet(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
info!("Ethernet gmac init ...");
let irq_num = interrupts_extended[1];
use crate::net::*;
let dev = Device::Net(match comp {
#[cfg(target_arch = "riscv64")]
c if c.contains("allwinner,sunxi-gmac") => {
Arc::new(rtlx_init(irq_num as usize, |paddr, size| {
self.io_mapper.query_or_map(paddr, size)
})?)
}
_ => return Err(DeviceError::NotSupported),
});
Ok((dev, interrupts_extended))
}
/// Parse nodes for UART devices.
fn parse_uart(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
use crate::uart::*;
let dev = Device::Uart(match comp {
c if c.contains("ns16550a") => {
Arc::new(unsafe { Uart16550Mmio::<u8>::new(base_vaddr?) })
}
c if c.contains("allwinner,sun20i-uart") => {
Arc::new(unsafe { Uart16550Mmio::<u32>::new(base_vaddr?) })
}
_ => return Err(DeviceError::NotSupported),
});
Ok((dev, interrupts_extended))
}
}

View File

@ -1,18 +0,0 @@
//! Various builders to probe devices and create corresponding drivers
//! (e.g. device tree, ACPI table, ...)
mod devicetree;
pub use devicetree::DevicetreeDriverBuilder;
use crate::{PhysAddr, VirtAddr};
/// A trait implemented in kernel to translate device physical addresses to virtual
/// addresses.
pub trait IoMapper {
/// Translate the device physical address to virtual address. If not mapped
/// in the kernel page table, map the region specified by the given `size`.
///
/// If an error accurs during translation or mapping, returns `None`.
fn query_or_map(&self, paddr: PhysAddr, size: usize) -> Option<VirtAddr>;
}

View File

@ -1,38 +0,0 @@
#![allow(unused)]
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
pub mod pci;
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
unsafe { drivers_phys_to_virt(paddr) }
}
pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
unsafe { drivers_virt_to_phys(vaddr) }
}
extern "C" {
fn drivers_dma_alloc(pages: usize) -> PhysAddr;
fn drivers_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32;
fn drivers_phys_to_virt(paddr: PhysAddr) -> VirtAddr;
fn drivers_virt_to_phys(vaddr: VirtAddr) -> PhysAddr;
}
pub const PAGE_SIZE: usize = 4096;
type VirtAddr = usize;
type PhysAddr = usize;
use core::ptr::{read_volatile, write_volatile};
#[inline(always)]
pub fn write<T>(addr: usize, content: T) {
let cell = (addr) as *mut T;
unsafe {
write_volatile(cell, content);
}
}
#[inline(always)]
pub fn read<T>(addr: usize) -> T {
let cell = (addr) as *const T;
unsafe { read_volatile(cell) }
}

View File

@ -1,327 +0,0 @@
use super::{phys_to_virt, PAGE_SIZE};
use crate::builder::IoMapper;
use crate::{Device, DeviceError, DeviceResult, VirtAddr};
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
use pci::*;
use spin::Mutex;
const PCI_COMMAND: u16 = 0x04;
const BAR0: u16 = 0x10;
const PCI_CAP_PTR: u16 = 0x34;
const PCI_INTERRUPT_LINE: u16 = 0x3c;
const PCI_INTERRUPT_PIN: u16 = 0x3d;
const PCI_MSI_CTRL_CAP: u16 = 0x00;
const PCI_MSI_ADDR: u16 = 0x04;
const PCI_MSI_UPPER_ADDR: u16 = 0x08;
const PCI_MSI_DATA_32: u16 = 0x08;
const PCI_MSI_DATA_64: u16 = 0x0C;
const PCI_CAP_ID_MSI: u8 = 0x05;
struct PortOpsImpl;
#[cfg(target_arch = "x86_64")]
use x86_64::instructions::port::Port;
#[cfg(target_arch = "x86_64")]
impl PortOps for PortOpsImpl {
unsafe fn read8(&self, port: u16) -> u8 {
Port::new(port).read()
}
unsafe fn read16(&self, port: u16) -> u16 {
Port::new(port).read()
}
unsafe fn read32(&self, port: u32) -> u32 {
Port::new(port as u16).read()
}
unsafe fn write8(&self, port: u16, val: u8) {
Port::new(port).write(val);
}
unsafe fn write16(&self, port: u16, val: u16) {
Port::new(port).write(val);
}
unsafe fn write32(&self, port: u32, val: u32) {
Port::new(port as u16).write(val);
}
}
#[cfg(target_arch = "x86_64")]
const PCI_BASE: usize = 0; //Fix me
#[cfg(any(target_arch = "mips", target_arch = "riscv64"))]
use super::{read, write};
#[cfg(feature = "board_malta")]
const PCI_BASE: usize = 0xbbe00000;
#[cfg(target_arch = "riscv64")]
const PCI_BASE: usize = 0x30000000;
#[cfg(target_arch = "riscv64")]
const E1000_BASE: usize = 0x40000000;
// riscv64 Qemu
#[cfg(target_arch = "x86_64")]
const PCI_ACCESS: CSpaceAccessMethod = CSpaceAccessMethod::IO;
#[cfg(not(target_arch = "x86_64"))]
const PCI_ACCESS: CSpaceAccessMethod = CSpaceAccessMethod::MemoryMapped(PCI_BASE as *mut u8);
#[cfg(any(target_arch = "mips", target_arch = "riscv64"))]
impl PortOps for PortOpsImpl {
unsafe fn read8(&self, port: u16) -> u8 {
read(phys_to_virt(PCI_BASE) + port as usize)
}
unsafe fn read16(&self, port: u16) -> u16 {
read(phys_to_virt(PCI_BASE) + port as usize)
}
unsafe fn read32(&self, port: u32) -> u32 {
read(phys_to_virt(PCI_BASE) + port as usize)
}
unsafe fn write8(&self, port: u16, val: u8) {
write(phys_to_virt(PCI_BASE) + port as usize, val);
}
unsafe fn write16(&self, port: u16, val: u16) {
write(phys_to_virt(PCI_BASE) + port as usize, val);
}
unsafe fn write32(&self, port: u32, val: u32) {
write(phys_to_virt(PCI_BASE) + port as usize, val);
}
}
/// Enable the pci device and its interrupt
/// Return assigned MSI interrupt number when applicable
unsafe fn enable(loc: Location, paddr: u64) -> Option<usize> {
let ops = &PortOpsImpl;
//let am = CSpaceAccessMethod::IO;
let am = PCI_ACCESS;
if paddr != 0 {
// reveal PCI regs by setting paddr
let bar0_raw = am.read32(ops, loc, BAR0);
am.write32(ops, loc, BAR0, (paddr & !0xfff) as u32); //Only for 32-bit decoding
debug!(
"BAR0 set from {:#x} to {:#x}",
bar0_raw,
am.read32(ops, loc, BAR0)
);
}
// 23 and lower are used
static mut MSI_IRQ: u32 = 23;
let orig = am.read16(ops, loc, PCI_COMMAND);
// IO Space | MEM Space | Bus Mastering | Special Cycles | PCI Interrupt Disable
am.write32(ops, loc, PCI_COMMAND, (orig | 0x40f) as u32);
// find MSI cap
let mut msi_found = false;
let mut cap_ptr = am.read8(ops, loc, PCI_CAP_PTR) as u16;
let mut assigned_irq = None;
while cap_ptr > 0 {
let cap_id = am.read8(ops, loc, cap_ptr);
if cap_id == PCI_CAP_ID_MSI {
let orig_ctrl = am.read32(ops, loc, cap_ptr + PCI_MSI_CTRL_CAP);
// The manual Volume 3 Chapter 10.11 Message Signalled Interrupts
// 0 is (usually) the apic id of the bsp.
//am.write32(ops, loc, cap_ptr + PCI_MSI_ADDR, 0xfee00000 | (0 << 12));
am.write32(ops, loc, cap_ptr + PCI_MSI_ADDR, 0xfee00000);
MSI_IRQ += 1;
let irq = MSI_IRQ;
assigned_irq = Some(irq as usize);
// we offset all our irq numbers by 32
if (orig_ctrl >> 16) & (1 << 7) != 0 {
// 64bit
am.write32(ops, loc, cap_ptr + PCI_MSI_DATA_64, irq + 32);
} else {
// 32bit
am.write32(ops, loc, cap_ptr + PCI_MSI_DATA_32, irq + 32);
}
// enable MSI interrupt, assuming 64bit for now
am.write32(ops, loc, cap_ptr + PCI_MSI_CTRL_CAP, orig_ctrl | 0x10000);
debug!(
"MSI control {:#b}, enabling MSI interrupt {}",
orig_ctrl >> 16,
irq
);
msi_found = true;
}
debug!("PCI device has cap id {} at {:#X}", cap_id, cap_ptr);
cap_ptr = am.read8(ops, loc, cap_ptr + 1) as u16;
}
if !msi_found {
// Use PCI legacy interrupt instead
// IO Space | MEM Space | Bus Mastering | Special Cycles
am.write32(ops, loc, PCI_COMMAND, (orig | 0xf) as u32);
debug!("MSI not found, using PCI interrupt");
}
info!("pci device enable done");
assigned_irq
}
pub fn init_driver(dev: &PCIDevice, mapper: &Option<Arc<dyn IoMapper>>) -> DeviceResult<Device> {
let name = format!("enp{}s{}f{}", dev.loc.bus, dev.loc.device, dev.loc.function);
match (dev.id.vendor_id, dev.id.device_id) {
(0x8086, 0x100e) | (0x8086, 0x100f) | (0x8086, 0x10d3) => {
// 0x100e
// 82540EM Gigabit Ethernet Controller
// 0x100f
// 82545EM Gigabit Ethernet Controller (Copper)
// 0x10d3
// 82574L Gigabit Network Connection
// (e1000e 8086:10d3)
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
info!("Found e1000e dev {:?} BAR0 {:#x?}", dev, addr);
#[cfg(target_arch = "riscv64")]
let addr = if addr == 0 { E1000_BASE as u64 } else { addr };
if let Some(m) = mapper {
m.query_or_map(addr as usize, PAGE_SIZE * 8);
}
let irq = unsafe { enable(dev.loc, addr) };
let vaddr = phys_to_virt(addr as usize);
let dev = Device::Net(Arc::new(crate::net::e1000::init(
name,
irq.unwrap_or(0),
vaddr,
len as usize,
0,
)?));
return Ok(dev);
}
}
(0x8086, 0x10fb) => {
// 82599ES 10-Gigabit SFI/SFP+ Network Connection
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
let irq = unsafe { enable(dev.loc, 0) };
let vaddr = phys_to_virt(addr as usize);
info!("Found ixgbe dev {:#x}, irq: {:?}", vaddr, irq);
/*
let index = NET_DRIVERS.read().len();
PCI_DRIVERS.lock().insert(
dev.loc,
ixgbe::ixgbe_init(name, irq, vaddr, len as usize, index),
);
*/
return Err(DeviceError::NotSupported);
}
}
(0x8086, 0x1533) => {
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
info!("Intel Corporation I210 Gigabit Network Connection");
info!("DEV: {:?}, BAR0: {:#x}", dev, addr);
return Err(DeviceError::NotSupported);
}
}
(0x8086, 0x1539) => {
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
info!(
"Found Intel I211 ethernet controller dev {:?}, addr: {:x?}",
dev, addr
);
return Err(DeviceError::NotSupported);
}
}
_ => {}
}
if dev.id.class == 0x01 && dev.id.subclass == 0x06 {
// Mass storage class
// SATA subclass
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[5] {
info!("Found AHCI dev {:?} BAR5 {:x?}", dev, addr);
/*
let irq = unsafe { enable(dev.loc) };
assert!(len as usize <= PAGE_SIZE);
let vaddr = phys_to_virt(addr as usize);
if let Some(driver) = ahci::init(irq, vaddr, len as usize) {
PCI_DRIVERS.lock().insert(dev.loc, driver);
}
*/
return Err(DeviceError::NotSupported);
}
}
Err(DeviceError::NoResources)
}
pub fn detach_driver(loc: &Location) -> bool {
/*
match PCI_DRIVERS.lock().remove(loc) {
Some(driver) => {
DRIVERS
.write()
.retain(|dri| dri.get_id() != driver.get_id());
NET_DRIVERS
.write()
.retain(|dri| dri.get_id() != driver.get_id());
true
}
None => false,
}
*/
false
}
pub fn init(mapper: Option<Arc<dyn IoMapper>>) -> DeviceResult<Vec<Device>> {
let mapper_driver = if let Some(m) = mapper {
m.query_or_map(PCI_BASE, PAGE_SIZE * 256 * 32 * 8);
Some(m)
} else {
None
};
let mut dev_list = Vec::new();
let pci_iter = unsafe { scan_bus(&PortOpsImpl, PCI_ACCESS) };
info!("");
info!("--------- PCI bus:device:function ---------");
for dev in pci_iter {
info!(
"pci: {}:{}:{} {:04x}:{:04x} ({} {}) irq: {}:{:?}",
dev.loc.bus,
dev.loc.device,
dev.loc.function,
dev.id.vendor_id,
dev.id.device_id,
dev.id.class,
dev.id.subclass,
dev.pic_interrupt_line,
dev.interrupt_pin,
);
let res = init_driver(&dev, &mapper_driver);
match res {
Ok(d) => dev_list.push(d),
Err(e) => warn!(
"{:?}, failed to initialize PCI device: {:04x}:{:04x}",
e, dev.id.vendor_id, dev.id.device_id
),
}
}
info!("---------");
info!("");
Ok(dev_list)
}
pub fn find_device(vendor: u16, product: u16) -> Option<Location> {
let pci_iter = unsafe { scan_bus(&PortOpsImpl, PCI_ACCESS) };
for dev in pci_iter {
if dev.id.vendor_id == vendor && dev.id.device_id == product {
return Some(dev.loc);
}
}
None
}
pub fn get_bar0_mem(loc: Location) -> Option<(usize, usize)> {
unsafe { probe_function(&PortOpsImpl, loc, PCI_ACCESS) }
.and_then(|dev| dev.bars[0])
.map(|bar| match bar {
BAR::Memory(addr, len, _, _) => (addr as usize, len as usize),
_ => unimplemented!(),
})
}
// all devices stored inAllDeviceList

View File

@ -1,5 +0,0 @@
//! Only UEFI Display currently.
mod uefi;
pub use uefi::UefiDisplay;

View File

@ -1,34 +0,0 @@
//! UEFI Graphics Output Protocol
use crate::prelude::{DisplayInfo, FrameBuffer};
use crate::scheme::{DisplayScheme, Scheme};
pub struct UefiDisplay {
info: DisplayInfo,
}
impl UefiDisplay {
pub fn new(info: DisplayInfo) -> Self {
Self { info }
}
}
impl Scheme for UefiDisplay {
fn name(&self) -> &str {
"mock-display"
}
}
impl DisplayScheme for UefiDisplay {
#[inline]
fn info(&self) -> DisplayInfo {
self.info
}
#[inline]
fn fb(&self) -> FrameBuffer {
unsafe {
FrameBuffer::from_raw_parts_mut(self.info.fb_base_vaddr as *mut u8, self.info.fb_size)
}
}
}

View File

@ -1,808 +0,0 @@
//! Linux input event codes.
//!
//! Reference: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h>
#![allow(dead_code)]
#![allow(unused_parens)]
/// Device properties and quirks
pub mod input_prop {
/// needs a pointer
pub const INPUT_PROP_POINTER: u16 = 0x00;
/// direct input devices
pub const INPUT_PROP_DIRECT: u16 = 0x01;
/// has button(s) under pad
pub const INPUT_PROP_BUTTONPAD: u16 = 0x02;
/// touch rectangle only
pub const INPUT_PROP_SEMI_MT: u16 = 0x03;
/// softbuttons at top of pad
pub const INPUT_PROP_TOPBUTTONPAD: u16 = 0x04;
/// is a pointing stick
pub const INPUT_PROP_POINTING_STICK: u16 = 0x05;
/// has accelerometer
pub const INPUT_PROP_ACCELEROMETER: u16 = 0x06;
pub const INPUT_PROP_MAX: u16 = 0x1f;
pub const INPUT_PROP_CNT: u16 = (INPUT_PROP_MAX + 1);
}
/// Event types
pub mod ev {
pub const EV_SYN: u16 = 0x00;
pub const EV_KEY: u16 = 0x01;
pub const EV_REL: u16 = 0x02;
pub const EV_ABS: u16 = 0x03;
pub const EV_MSC: u16 = 0x04;
pub const EV_SW: u16 = 0x05;
pub const EV_LED: u16 = 0x11;
pub const EV_SND: u16 = 0x12;
pub const EV_REP: u16 = 0x14;
pub const EV_FF: u16 = 0x15;
pub const EV_PWR: u16 = 0x16;
pub const EV_FF_STATUS: u16 = 0x17;
pub const EV_MAX: u16 = 0x1f;
pub const EV_CNT: u16 = (EV_MAX + 1);
}
/// Synchronization events
pub mod syn {
pub const SYN_REPORT: u16 = 0;
pub const SYN_CONFIG: u16 = 1;
pub const SYN_MT_REPORT: u16 = 2;
pub const SYN_DROPPED: u16 = 3;
pub const SYN_MAX: u16 = 0xf;
pub const SYN_CNT: u16 = (SYN_MAX + 1);
}
/// Keys and buttons
pub mod key {
pub const KEY_RESERVED: u16 = 0;
pub const KEY_ESC: u16 = 1;
pub const KEY_1: u16 = 2;
pub const KEY_2: u16 = 3;
pub const KEY_3: u16 = 4;
pub const KEY_4: u16 = 5;
pub const KEY_5: u16 = 6;
pub const KEY_6: u16 = 7;
pub const KEY_7: u16 = 8;
pub const KEY_8: u16 = 9;
pub const KEY_9: u16 = 10;
pub const KEY_0: u16 = 11;
pub const KEY_MINUS: u16 = 12;
pub const KEY_EQUAL: u16 = 13;
pub const KEY_BACKSPACE: u16 = 14;
pub const KEY_TAB: u16 = 15;
pub const KEY_Q: u16 = 16;
pub const KEY_W: u16 = 17;
pub const KEY_E: u16 = 18;
pub const KEY_R: u16 = 19;
pub const KEY_T: u16 = 20;
pub const KEY_Y: u16 = 21;
pub const KEY_U: u16 = 22;
pub const KEY_I: u16 = 23;
pub const KEY_O: u16 = 24;
pub const KEY_P: u16 = 25;
pub const KEY_LEFTBRACE: u16 = 26;
pub const KEY_RIGHTBRACE: u16 = 27;
pub const KEY_ENTER: u16 = 28;
pub const KEY_LEFTCTRL: u16 = 29;
pub const KEY_A: u16 = 30;
pub const KEY_S: u16 = 31;
pub const KEY_D: u16 = 32;
pub const KEY_F: u16 = 33;
pub const KEY_G: u16 = 34;
pub const KEY_H: u16 = 35;
pub const KEY_J: u16 = 36;
pub const KEY_K: u16 = 37;
pub const KEY_L: u16 = 38;
pub const KEY_SEMICOLON: u16 = 39;
pub const KEY_APOSTROPHE: u16 = 40;
pub const KEY_GRAVE: u16 = 41;
pub const KEY_LEFTSHIFT: u16 = 42;
pub const KEY_BACKSLASH: u16 = 43;
pub const KEY_Z: u16 = 44;
pub const KEY_X: u16 = 45;
pub const KEY_C: u16 = 46;
pub const KEY_V: u16 = 47;
pub const KEY_B: u16 = 48;
pub const KEY_N: u16 = 49;
pub const KEY_M: u16 = 50;
pub const KEY_COMMA: u16 = 51;
pub const KEY_DOT: u16 = 52;
pub const KEY_SLASH: u16 = 53;
pub const KEY_RIGHTSHIFT: u16 = 54;
pub const KEY_KPASTERISK: u16 = 55;
pub const KEY_LEFTALT: u16 = 56;
pub const KEY_SPACE: u16 = 57;
pub const KEY_CAPSLOCK: u16 = 58;
pub const KEY_F1: u16 = 59;
pub const KEY_F2: u16 = 60;
pub const KEY_F3: u16 = 61;
pub const KEY_F4: u16 = 62;
pub const KEY_F5: u16 = 63;
pub const KEY_F6: u16 = 64;
pub const KEY_F7: u16 = 65;
pub const KEY_F8: u16 = 66;
pub const KEY_F9: u16 = 67;
pub const KEY_F10: u16 = 68;
pub const KEY_NUMLOCK: u16 = 69;
pub const KEY_SCROLLLOCK: u16 = 70;
pub const KEY_KP7: u16 = 71;
pub const KEY_KP8: u16 = 72;
pub const KEY_KP9: u16 = 73;
pub const KEY_KPMINUS: u16 = 74;
pub const KEY_KP4: u16 = 75;
pub const KEY_KP5: u16 = 76;
pub const KEY_KP6: u16 = 77;
pub const KEY_KPPLUS: u16 = 78;
pub const KEY_KP1: u16 = 79;
pub const KEY_KP2: u16 = 80;
pub const KEY_KP3: u16 = 81;
pub const KEY_KP0: u16 = 82;
pub const KEY_KPDOT: u16 = 83;
pub const KEY_ZENKAKUHANKAKU: u16 = 85;
pub const KEY_102ND: u16 = 86;
pub const KEY_F11: u16 = 87;
pub const KEY_F12: u16 = 88;
pub const KEY_RO: u16 = 89;
pub const KEY_KATAKANA: u16 = 90;
pub const KEY_HIRAGANA: u16 = 91;
pub const KEY_HENKAN: u16 = 92;
pub const KEY_KATAKANAHIRAGANA: u16 = 93;
pub const KEY_MUHENKAN: u16 = 94;
pub const KEY_KPJPCOMMA: u16 = 95;
pub const KEY_KPENTER: u16 = 96;
pub const KEY_RIGHTCTRL: u16 = 97;
pub const KEY_KPSLASH: u16 = 98;
pub const KEY_SYSRQ: u16 = 99;
pub const KEY_RIGHTALT: u16 = 100;
pub const KEY_LINEFEED: u16 = 101;
pub const KEY_HOME: u16 = 102;
pub const KEY_UP: u16 = 103;
pub const KEY_PAGEUP: u16 = 104;
pub const KEY_LEFT: u16 = 105;
pub const KEY_RIGHT: u16 = 106;
pub const KEY_END: u16 = 107;
pub const KEY_DOWN: u16 = 108;
pub const KEY_PAGEDOWN: u16 = 109;
pub const KEY_INSERT: u16 = 110;
pub const KEY_DELETE: u16 = 111;
pub const KEY_MACRO: u16 = 112;
pub const KEY_MUTE: u16 = 113;
pub const KEY_VOLUMEDOWN: u16 = 114;
pub const KEY_VOLUMEUP: u16 = 115;
pub const KEY_POWER: u16 = 116;
pub const KEY_KPEQUAL: u16 = 117;
pub const KEY_KPPLUSMINUS: u16 = 118;
pub const KEY_PAUSE: u16 = 119;
pub const KEY_SCALE: u16 = 120;
pub const KEY_KPCOMMA: u16 = 121;
pub const KEY_HANGEUL: u16 = 122;
pub const KEY_HANGUEL: u16 = KEY_HANGEUL;
pub const KEY_HANJA: u16 = 123;
pub const KEY_YEN: u16 = 124;
pub const KEY_LEFTMETA: u16 = 125;
pub const KEY_RIGHTMETA: u16 = 126;
pub const KEY_COMPOSE: u16 = 127;
pub const KEY_STOP: u16 = 128;
pub const KEY_AGAIN: u16 = 129;
pub const KEY_PROPS: u16 = 130;
pub const KEY_UNDO: u16 = 131;
pub const KEY_FRONT: u16 = 132;
pub const KEY_COPY: u16 = 133;
pub const KEY_OPEN: u16 = 134;
pub const KEY_PASTE: u16 = 135;
pub const KEY_FIND: u16 = 136;
pub const KEY_CUT: u16 = 137;
pub const KEY_HELP: u16 = 138;
pub const KEY_MENU: u16 = 139;
pub const KEY_CALC: u16 = 140;
pub const KEY_SETUP: u16 = 141;
pub const KEY_SLEEP: u16 = 142;
pub const KEY_WAKEUP: u16 = 143;
pub const KEY_FILE: u16 = 144;
pub const KEY_SENDFILE: u16 = 145;
pub const KEY_DELETEFILE: u16 = 146;
pub const KEY_XFER: u16 = 147;
pub const KEY_PROG1: u16 = 148;
pub const KEY_PROG2: u16 = 149;
pub const KEY_WWW: u16 = 150;
pub const KEY_MSDOS: u16 = 151;
pub const KEY_COFFEE: u16 = 152;
pub const KEY_SCREENLOCK: u16 = KEY_COFFEE;
pub const KEY_ROTATE_DISPLAY: u16 = 153;
pub const KEY_DIRECTION: u16 = KEY_ROTATE_DISPLAY;
pub const KEY_CYCLEWINDOWS: u16 = 154;
pub const KEY_MAIL: u16 = 155;
pub const KEY_BOOKMARKS: u16 = 156;
pub const KEY_COMPUTER: u16 = 157;
pub const KEY_BACK: u16 = 158;
pub const KEY_FORWARD: u16 = 159;
pub const KEY_CLOSECD: u16 = 160;
pub const KEY_EJECTCD: u16 = 161;
pub const KEY_EJECTCLOSECD: u16 = 162;
pub const KEY_NEXTSONG: u16 = 163;
pub const KEY_PLAYPAUSE: u16 = 164;
pub const KEY_PREVIOUSSONG: u16 = 165;
pub const KEY_STOPCD: u16 = 166;
pub const KEY_RECORD: u16 = 167;
pub const KEY_REWIND: u16 = 168;
pub const KEY_PHONE: u16 = 169;
pub const KEY_ISO: u16 = 170;
pub const KEY_CONFIG: u16 = 171;
pub const KEY_HOMEPAGE: u16 = 172;
pub const KEY_REFRESH: u16 = 173;
pub const KEY_EXIT: u16 = 174;
pub const KEY_MOVE: u16 = 175;
pub const KEY_EDIT: u16 = 176;
pub const KEY_SCROLLUP: u16 = 177;
pub const KEY_SCROLLDOWN: u16 = 178;
pub const KEY_KPLEFTPAREN: u16 = 179;
pub const KEY_KPRIGHTPAREN: u16 = 180;
pub const KEY_NEW: u16 = 181;
pub const KEY_REDO: u16 = 182;
pub const KEY_F13: u16 = 183;
pub const KEY_F14: u16 = 184;
pub const KEY_F15: u16 = 185;
pub const KEY_F16: u16 = 186;
pub const KEY_F17: u16 = 187;
pub const KEY_F18: u16 = 188;
pub const KEY_F19: u16 = 189;
pub const KEY_F20: u16 = 190;
pub const KEY_F21: u16 = 191;
pub const KEY_F22: u16 = 192;
pub const KEY_F23: u16 = 193;
pub const KEY_F24: u16 = 194;
pub const KEY_PLAYCD: u16 = 200;
pub const KEY_PAUSECD: u16 = 201;
pub const KEY_PROG3: u16 = 202;
pub const KEY_PROG4: u16 = 203;
pub const KEY_DASHBOARD: u16 = 204;
pub const KEY_SUSPEND: u16 = 205;
pub const KEY_CLOSE: u16 = 206;
pub const KEY_PLAY: u16 = 207;
pub const KEY_FASTFORWARD: u16 = 208;
pub const KEY_BASSBOOST: u16 = 209;
pub const KEY_PRINT: u16 = 210;
pub const KEY_HP: u16 = 211;
pub const KEY_CAMERA: u16 = 212;
pub const KEY_SOUND: u16 = 213;
pub const KEY_QUESTION: u16 = 214;
pub const KEY_EMAIL: u16 = 215;
pub const KEY_CHAT: u16 = 216;
pub const KEY_SEARCH: u16 = 217;
pub const KEY_CONNECT: u16 = 218;
pub const KEY_FINANCE: u16 = 219;
pub const KEY_SPORT: u16 = 220;
pub const KEY_SHOP: u16 = 221;
pub const KEY_ALTERASE: u16 = 222;
pub const KEY_CANCEL: u16 = 223;
pub const KEY_BRIGHTNESSDOWN: u16 = 224;
pub const KEY_BRIGHTNESSUP: u16 = 225;
pub const KEY_MEDIA: u16 = 226;
pub const KEY_SWITCHVIDEOMODE: u16 = 227;
pub const KEY_KBDILLUMTOGGLE: u16 = 228;
pub const KEY_KBDILLUMDOWN: u16 = 229;
pub const KEY_KBDILLUMUP: u16 = 230;
pub const KEY_SEND: u16 = 231;
pub const KEY_REPLY: u16 = 232;
pub const KEY_FORWARDMAIL: u16 = 233;
pub const KEY_SAVE: u16 = 234;
pub const KEY_DOCUMENTS: u16 = 235;
pub const KEY_BATTERY: u16 = 236;
pub const KEY_BLUETOOTH: u16 = 237;
pub const KEY_WLAN: u16 = 238;
pub const KEY_UWB: u16 = 239;
pub const KEY_UNKNOWN: u16 = 240;
pub const KEY_VIDEO_NEXT: u16 = 241;
pub const KEY_VIDEO_PREV: u16 = 242;
pub const KEY_BRIGHTNESS_CYCLE: u16 = 243;
pub const KEY_BRIGHTNESS_AUTO: u16 = 244;
pub const KEY_BRIGHTNESS_ZERO: u16 = KEY_BRIGHTNESS_AUTO;
pub const KEY_DISPLAY_OFF: u16 = 245;
pub const KEY_WWAN: u16 = 246;
pub const KEY_WIMAX: u16 = KEY_WWAN;
pub const KEY_RFKILL: u16 = 247;
pub const KEY_MICMUTE: u16 = 248;
pub const BTN_MISC: u16 = 0x100;
pub const BTN_0: u16 = 0x100;
pub const BTN_1: u16 = 0x101;
pub const BTN_2: u16 = 0x102;
pub const BTN_3: u16 = 0x103;
pub const BTN_4: u16 = 0x104;
pub const BTN_5: u16 = 0x105;
pub const BTN_6: u16 = 0x106;
pub const BTN_7: u16 = 0x107;
pub const BTN_8: u16 = 0x108;
pub const BTN_9: u16 = 0x109;
pub const BTN_MOUSE: u16 = 0x110;
pub const BTN_LEFT: u16 = 0x110;
pub const BTN_RIGHT: u16 = 0x111;
pub const BTN_MIDDLE: u16 = 0x112;
pub const BTN_SIDE: u16 = 0x113;
pub const BTN_EXTRA: u16 = 0x114;
pub const BTN_FORWARD: u16 = 0x115;
pub const BTN_BACK: u16 = 0x116;
pub const BTN_TASK: u16 = 0x117;
pub const BTN_JOYSTICK: u16 = 0x120;
pub const BTN_TRIGGER: u16 = 0x120;
pub const BTN_THUMB: u16 = 0x121;
pub const BTN_THUMB2: u16 = 0x122;
pub const BTN_TOP: u16 = 0x123;
pub const BTN_TOP2: u16 = 0x124;
pub const BTN_PINKIE: u16 = 0x125;
pub const BTN_BASE: u16 = 0x126;
pub const BTN_BASE2: u16 = 0x127;
pub const BTN_BASE3: u16 = 0x128;
pub const BTN_BASE4: u16 = 0x129;
pub const BTN_BASE5: u16 = 0x12a;
pub const BTN_BASE6: u16 = 0x12b;
pub const BTN_DEAD: u16 = 0x12f;
pub const BTN_GAMEPAD: u16 = 0x130;
pub const BTN_SOUTH: u16 = 0x130;
pub const BTN_A: u16 = BTN_SOUTH;
pub const BTN_EAST: u16 = 0x131;
pub const BTN_B: u16 = BTN_EAST;
pub const BTN_C: u16 = 0x132;
pub const BTN_NORTH: u16 = 0x133;
pub const BTN_X: u16 = BTN_NORTH;
pub const BTN_WEST: u16 = 0x134;
pub const BTN_Y: u16 = BTN_WEST;
pub const BTN_Z: u16 = 0x135;
pub const BTN_TL: u16 = 0x136;
pub const BTN_TR: u16 = 0x137;
pub const BTN_TL2: u16 = 0x138;
pub const BTN_TR2: u16 = 0x139;
pub const BTN_SELECT: u16 = 0x13a;
pub const BTN_START: u16 = 0x13b;
pub const BTN_MODE: u16 = 0x13c;
pub const BTN_THUMBL: u16 = 0x13d;
pub const BTN_THUMBR: u16 = 0x13e;
pub const BTN_DIGI: u16 = 0x140;
pub const BTN_TOOL_PEN: u16 = 0x140;
pub const BTN_TOOL_RUBBER: u16 = 0x141;
pub const BTN_TOOL_BRUSH: u16 = 0x142;
pub const BTN_TOOL_PENCIL: u16 = 0x143;
pub const BTN_TOOL_AIRBRUSH: u16 = 0x144;
pub const BTN_TOOL_FINGER: u16 = 0x145;
pub const BTN_TOOL_MOUSE: u16 = 0x146;
pub const BTN_TOOL_LENS: u16 = 0x147;
pub const BTN_TOOL_QUINTTAP: u16 = 0x148;
pub const BTN_STYLUS3: u16 = 0x149;
pub const BTN_TOUCH: u16 = 0x14a;
pub const BTN_STYLUS: u16 = 0x14b;
pub const BTN_STYLUS2: u16 = 0x14c;
pub const BTN_TOOL_DOUBLETAP: u16 = 0x14d;
pub const BTN_TOOL_TRIPLETAP: u16 = 0x14e;
pub const BTN_TOOL_QUADTAP: u16 = 0x14f;
pub const BTN_WHEEL: u16 = 0x150;
pub const BTN_GEAR_DOWN: u16 = 0x150;
pub const BTN_GEAR_UP: u16 = 0x151;
pub const KEY_OK: u16 = 0x160;
pub const KEY_SELECT: u16 = 0x161;
pub const KEY_GOTO: u16 = 0x162;
pub const KEY_CLEAR: u16 = 0x163;
pub const KEY_POWER2: u16 = 0x164;
pub const KEY_OPTION: u16 = 0x165;
pub const KEY_INFO: u16 = 0x166;
pub const KEY_TIME: u16 = 0x167;
pub const KEY_VENDOR: u16 = 0x168;
pub const KEY_ARCHIVE: u16 = 0x169;
pub const KEY_PROGRAM: u16 = 0x16a;
pub const KEY_CHANNEL: u16 = 0x16b;
pub const KEY_FAVORITES: u16 = 0x16c;
pub const KEY_EPG: u16 = 0x16d;
pub const KEY_PVR: u16 = 0x16e;
pub const KEY_MHP: u16 = 0x16f;
pub const KEY_LANGUAGE: u16 = 0x170;
pub const KEY_TITLE: u16 = 0x171;
pub const KEY_SUBTITLE: u16 = 0x172;
pub const KEY_ANGLE: u16 = 0x173;
pub const KEY_FULL_SCREEN: u16 = 0x174;
pub const KEY_ZOOM: u16 = KEY_FULL_SCREEN;
pub const KEY_MODE: u16 = 0x175;
pub const KEY_KEYBOARD: u16 = 0x176;
pub const KEY_ASPECT_RATIO: u16 = 0x177;
pub const KEY_SCREEN: u16 = KEY_ASPECT_RATIO;
pub const KEY_PC: u16 = 0x178;
pub const KEY_TV: u16 = 0x179;
pub const KEY_TV2: u16 = 0x17a;
pub const KEY_VCR: u16 = 0x17b;
pub const KEY_VCR2: u16 = 0x17c;
pub const KEY_SAT: u16 = 0x17d;
pub const KEY_SAT2: u16 = 0x17e;
pub const KEY_CD: u16 = 0x17f;
pub const KEY_TAPE: u16 = 0x180;
pub const KEY_RADIO: u16 = 0x181;
pub const KEY_TUNER: u16 = 0x182;
pub const KEY_PLAYER: u16 = 0x183;
pub const KEY_TEXT: u16 = 0x184;
pub const KEY_DVD: u16 = 0x185;
pub const KEY_AUX: u16 = 0x186;
pub const KEY_MP3: u16 = 0x187;
pub const KEY_AUDIO: u16 = 0x188;
pub const KEY_VIDEO: u16 = 0x189;
pub const KEY_DIRECTORY: u16 = 0x18a;
pub const KEY_LIST: u16 = 0x18b;
pub const KEY_MEMO: u16 = 0x18c;
pub const KEY_CALENDAR: u16 = 0x18d;
pub const KEY_RED: u16 = 0x18e;
pub const KEY_GREEN: u16 = 0x18f;
pub const KEY_YELLOW: u16 = 0x190;
pub const KEY_BLUE: u16 = 0x191;
pub const KEY_CHANNELUP: u16 = 0x192;
pub const KEY_CHANNELDOWN: u16 = 0x193;
pub const KEY_FIRST: u16 = 0x194;
pub const KEY_LAST: u16 = 0x195;
pub const KEY_AB: u16 = 0x196;
pub const KEY_NEXT: u16 = 0x197;
pub const KEY_RESTART: u16 = 0x198;
pub const KEY_SLOW: u16 = 0x199;
pub const KEY_SHUFFLE: u16 = 0x19a;
pub const KEY_BREAK: u16 = 0x19b;
pub const KEY_PREVIOUS: u16 = 0x19c;
pub const KEY_DIGITS: u16 = 0x19d;
pub const KEY_TEEN: u16 = 0x19e;
pub const KEY_TWEN: u16 = 0x19f;
pub const KEY_VIDEOPHONE: u16 = 0x1a0;
pub const KEY_GAMES: u16 = 0x1a1;
pub const KEY_ZOOMIN: u16 = 0x1a2;
pub const KEY_ZOOMOUT: u16 = 0x1a3;
pub const KEY_ZOOMRESET: u16 = 0x1a4;
pub const KEY_WORDPROCESSOR: u16 = 0x1a5;
pub const KEY_EDITOR: u16 = 0x1a6;
pub const KEY_SPREADSHEET: u16 = 0x1a7;
pub const KEY_GRAPHICSEDITOR: u16 = 0x1a8;
pub const KEY_PRESENTATION: u16 = 0x1a9;
pub const KEY_DATABASE: u16 = 0x1aa;
pub const KEY_NEWS: u16 = 0x1ab;
pub const KEY_VOICEMAIL: u16 = 0x1ac;
pub const KEY_ADDRESSBOOK: u16 = 0x1ad;
pub const KEY_MESSENGER: u16 = 0x1ae;
pub const KEY_DISPLAYTOGGLE: u16 = 0x1af;
pub const KEY_BRIGHTNESS_TOGGLE: u16 = KEY_DISPLAYTOGGLE;
pub const KEY_SPELLCHECK: u16 = 0x1b0;
pub const KEY_LOGOFF: u16 = 0x1b1;
pub const KEY_DOLLAR: u16 = 0x1b2;
pub const KEY_EURO: u16 = 0x1b3;
pub const KEY_FRAMEBACK: u16 = 0x1b4;
pub const KEY_FRAMEFORWARD: u16 = 0x1b5;
pub const KEY_CONTEXT_MENU: u16 = 0x1b6;
pub const KEY_MEDIA_REPEAT: u16 = 0x1b7;
pub const KEY_10CHANNELSUP: u16 = 0x1b8;
pub const KEY_10CHANNELSDOWN: u16 = 0x1b9;
pub const KEY_IMAGES: u16 = 0x1ba;
pub const KEY_DEL_EOL: u16 = 0x1c0;
pub const KEY_DEL_EOS: u16 = 0x1c1;
pub const KEY_INS_LINE: u16 = 0x1c2;
pub const KEY_DEL_LINE: u16 = 0x1c3;
pub const KEY_FN: u16 = 0x1d0;
pub const KEY_FN_ESC: u16 = 0x1d1;
pub const KEY_FN_F1: u16 = 0x1d2;
pub const KEY_FN_F2: u16 = 0x1d3;
pub const KEY_FN_F3: u16 = 0x1d4;
pub const KEY_FN_F4: u16 = 0x1d5;
pub const KEY_FN_F5: u16 = 0x1d6;
pub const KEY_FN_F6: u16 = 0x1d7;
pub const KEY_FN_F7: u16 = 0x1d8;
pub const KEY_FN_F8: u16 = 0x1d9;
pub const KEY_FN_F9: u16 = 0x1da;
pub const KEY_FN_F10: u16 = 0x1db;
pub const KEY_FN_F11: u16 = 0x1dc;
pub const KEY_FN_F12: u16 = 0x1dd;
pub const KEY_FN_1: u16 = 0x1de;
pub const KEY_FN_2: u16 = 0x1df;
pub const KEY_FN_D: u16 = 0x1e0;
pub const KEY_FN_E: u16 = 0x1e1;
pub const KEY_FN_F: u16 = 0x1e2;
pub const KEY_FN_S: u16 = 0x1e3;
pub const KEY_FN_B: u16 = 0x1e4;
pub const KEY_BRL_DOT1: u16 = 0x1f1;
pub const KEY_BRL_DOT2: u16 = 0x1f2;
pub const KEY_BRL_DOT3: u16 = 0x1f3;
pub const KEY_BRL_DOT4: u16 = 0x1f4;
pub const KEY_BRL_DOT5: u16 = 0x1f5;
pub const KEY_BRL_DOT6: u16 = 0x1f6;
pub const KEY_BRL_DOT7: u16 = 0x1f7;
pub const KEY_BRL_DOT8: u16 = 0x1f8;
pub const KEY_BRL_DOT9: u16 = 0x1f9;
pub const KEY_BRL_DOT10: u16 = 0x1fa;
pub const KEY_NUMERIC_0: u16 = 0x200;
pub const KEY_NUMERIC_1: u16 = 0x201;
pub const KEY_NUMERIC_2: u16 = 0x202;
pub const KEY_NUMERIC_3: u16 = 0x203;
pub const KEY_NUMERIC_4: u16 = 0x204;
pub const KEY_NUMERIC_5: u16 = 0x205;
pub const KEY_NUMERIC_6: u16 = 0x206;
pub const KEY_NUMERIC_7: u16 = 0x207;
pub const KEY_NUMERIC_8: u16 = 0x208;
pub const KEY_NUMERIC_9: u16 = 0x209;
pub const KEY_NUMERIC_STAR: u16 = 0x20a;
pub const KEY_NUMERIC_POUND: u16 = 0x20b;
pub const KEY_NUMERIC_A: u16 = 0x20c;
pub const KEY_NUMERIC_B: u16 = 0x20d;
pub const KEY_NUMERIC_C: u16 = 0x20e;
pub const KEY_NUMERIC_D: u16 = 0x20f;
pub const KEY_CAMERA_FOCUS: u16 = 0x210;
pub const KEY_WPS_BUTTON: u16 = 0x211;
pub const KEY_TOUCHPAD_TOGGLE: u16 = 0x212;
pub const KEY_TOUCHPAD_ON: u16 = 0x213;
pub const KEY_TOUCHPAD_OFF: u16 = 0x214;
pub const KEY_CAMERA_ZOOMIN: u16 = 0x215;
pub const KEY_CAMERA_ZOOMOUT: u16 = 0x216;
pub const KEY_CAMERA_UP: u16 = 0x217;
pub const KEY_CAMERA_DOWN: u16 = 0x218;
pub const KEY_CAMERA_LEFT: u16 = 0x219;
pub const KEY_CAMERA_RIGHT: u16 = 0x21a;
pub const KEY_ATTENDANT_ON: u16 = 0x21b;
pub const KEY_ATTENDANT_OFF: u16 = 0x21c;
pub const KEY_ATTENDANT_TOGGLE: u16 = 0x21d;
pub const KEY_LIGHTS_TOGGLE: u16 = 0x21e;
pub const BTN_DPAD_UP: u16 = 0x220;
pub const BTN_DPAD_DOWN: u16 = 0x221;
pub const BTN_DPAD_LEFT: u16 = 0x222;
pub const BTN_DPAD_RIGHT: u16 = 0x223;
pub const KEY_ALS_TOGGLE: u16 = 0x230;
pub const KEY_ROTATE_LOCK_TOGGLE: u16 = 0x231;
pub const KEY_BUTTONCONFIG: u16 = 0x240;
pub const KEY_TASKMANAGER: u16 = 0x241;
pub const KEY_JOURNAL: u16 = 0x242;
pub const KEY_CONTROLPANEL: u16 = 0x243;
pub const KEY_APPSELECT: u16 = 0x244;
pub const KEY_SCREENSAVER: u16 = 0x245;
pub const KEY_VOICECOMMAND: u16 = 0x246;
pub const KEY_ASSISTANT: u16 = 0x247;
pub const KEY_BRIGHTNESS_MIN: u16 = 0x250;
pub const KEY_BRIGHTNESS_MAX: u16 = 0x251;
pub const KEY_KBDINPUTASSIST_PREV: u16 = 0x260;
pub const KEY_KBDINPUTASSIST_NEXT: u16 = 0x261;
pub const KEY_KBDINPUTASSIST_PREVGROUP: u16 = 0x262;
pub const KEY_KBDINPUTASSIST_NEXTGROUP: u16 = 0x263;
pub const KEY_KBDINPUTASSIST_ACCEPT: u16 = 0x264;
pub const KEY_KBDINPUTASSIST_CANCEL: u16 = 0x265;
pub const KEY_RIGHT_UP: u16 = 0x266;
pub const KEY_RIGHT_DOWN: u16 = 0x267;
pub const KEY_LEFT_UP: u16 = 0x268;
pub const KEY_LEFT_DOWN: u16 = 0x269;
pub const KEY_ROOT_MENU: u16 = 0x26a;
pub const KEY_MEDIA_TOP_MENU: u16 = 0x26b;
pub const KEY_NUMERIC_11: u16 = 0x26c;
pub const KEY_NUMERIC_12: u16 = 0x26d;
pub const KEY_AUDIO_DESC: u16 = 0x26e;
pub const KEY_3D_MODE: u16 = 0x26f;
pub const KEY_NEXT_FAVORITE: u16 = 0x270;
pub const KEY_STOP_RECORD: u16 = 0x271;
pub const KEY_PAUSE_RECORD: u16 = 0x272;
pub const KEY_VOD: u16 = 0x273;
pub const KEY_UNMUTE: u16 = 0x274;
pub const KEY_FASTREVERSE: u16 = 0x275;
pub const KEY_SLOWREVERSE: u16 = 0x276;
pub const KEY_DATA: u16 = 0x277;
pub const KEY_ONSCREEN_KEYBOARD: u16 = 0x278;
pub const BTN_TRIGGER_HAPPY: u16 = 0x2c0;
pub const BTN_TRIGGER_HAPPY1: u16 = 0x2c0;
pub const BTN_TRIGGER_HAPPY2: u16 = 0x2c1;
pub const BTN_TRIGGER_HAPPY3: u16 = 0x2c2;
pub const BTN_TRIGGER_HAPPY4: u16 = 0x2c3;
pub const BTN_TRIGGER_HAPPY5: u16 = 0x2c4;
pub const BTN_TRIGGER_HAPPY6: u16 = 0x2c5;
pub const BTN_TRIGGER_HAPPY7: u16 = 0x2c6;
pub const BTN_TRIGGER_HAPPY8: u16 = 0x2c7;
pub const BTN_TRIGGER_HAPPY9: u16 = 0x2c8;
pub const BTN_TRIGGER_HAPPY10: u16 = 0x2c9;
pub const BTN_TRIGGER_HAPPY11: u16 = 0x2ca;
pub const BTN_TRIGGER_HAPPY12: u16 = 0x2cb;
pub const BTN_TRIGGER_HAPPY13: u16 = 0x2cc;
pub const BTN_TRIGGER_HAPPY14: u16 = 0x2cd;
pub const BTN_TRIGGER_HAPPY15: u16 = 0x2ce;
pub const BTN_TRIGGER_HAPPY16: u16 = 0x2cf;
pub const BTN_TRIGGER_HAPPY17: u16 = 0x2d0;
pub const BTN_TRIGGER_HAPPY18: u16 = 0x2d1;
pub const BTN_TRIGGER_HAPPY19: u16 = 0x2d2;
pub const BTN_TRIGGER_HAPPY20: u16 = 0x2d3;
pub const BTN_TRIGGER_HAPPY21: u16 = 0x2d4;
pub const BTN_TRIGGER_HAPPY22: u16 = 0x2d5;
pub const BTN_TRIGGER_HAPPY23: u16 = 0x2d6;
pub const BTN_TRIGGER_HAPPY24: u16 = 0x2d7;
pub const BTN_TRIGGER_HAPPY25: u16 = 0x2d8;
pub const BTN_TRIGGER_HAPPY26: u16 = 0x2d9;
pub const BTN_TRIGGER_HAPPY27: u16 = 0x2da;
pub const BTN_TRIGGER_HAPPY28: u16 = 0x2db;
pub const BTN_TRIGGER_HAPPY29: u16 = 0x2dc;
pub const BTN_TRIGGER_HAPPY30: u16 = 0x2dd;
pub const BTN_TRIGGER_HAPPY31: u16 = 0x2de;
pub const BTN_TRIGGER_HAPPY32: u16 = 0x2df;
pub const BTN_TRIGGER_HAPPY33: u16 = 0x2e0;
pub const BTN_TRIGGER_HAPPY34: u16 = 0x2e1;
pub const BTN_TRIGGER_HAPPY35: u16 = 0x2e2;
pub const BTN_TRIGGER_HAPPY36: u16 = 0x2e3;
pub const BTN_TRIGGER_HAPPY37: u16 = 0x2e4;
pub const BTN_TRIGGER_HAPPY38: u16 = 0x2e5;
pub const BTN_TRIGGER_HAPPY39: u16 = 0x2e6;
pub const BTN_TRIGGER_HAPPY40: u16 = 0x2e7;
pub const KEY_MIN_INTERESTING: u16 = KEY_MUTE;
pub const KEY_MAX: u16 = 0x2ff;
pub const KEY_CNT: u16 = (KEY_MAX + 1);
}
/// Relative axes
pub mod rel {
pub const REL_X: u16 = 0x00;
pub const REL_Y: u16 = 0x01;
pub const REL_Z: u16 = 0x02;
pub const REL_RX: u16 = 0x03;
pub const REL_RY: u16 = 0x04;
pub const REL_RZ: u16 = 0x05;
pub const REL_HWHEEL: u16 = 0x06;
pub const REL_DIAL: u16 = 0x07;
pub const REL_WHEEL: u16 = 0x08;
pub const REL_MISC: u16 = 0x09;
pub const REL_RESERVED: u16 = 0x0a;
pub const REL_WHEEL_HI_RES: u16 = 0x0b;
pub const REL_HWHEEL_HI_RES: u16 = 0x0c;
pub const REL_MAX: u16 = 0x0f;
pub const REL_CNT: u16 = (REL_MAX + 1);
}
/// Absolute axes
pub mod abs {
pub const ABS_X: u16 = 0x00;
pub const ABS_Y: u16 = 0x01;
pub const ABS_Z: u16 = 0x02;
pub const ABS_RX: u16 = 0x03;
pub const ABS_RY: u16 = 0x04;
pub const ABS_RZ: u16 = 0x05;
pub const ABS_THROTTLE: u16 = 0x06;
pub const ABS_RUDDER: u16 = 0x07;
pub const ABS_WHEEL: u16 = 0x08;
pub const ABS_GAS: u16 = 0x09;
pub const ABS_BRAKE: u16 = 0x0a;
pub const ABS_HAT0X: u16 = 0x10;
pub const ABS_HAT0Y: u16 = 0x11;
pub const ABS_HAT1X: u16 = 0x12;
pub const ABS_HAT1Y: u16 = 0x13;
pub const ABS_HAT2X: u16 = 0x14;
pub const ABS_HAT2Y: u16 = 0x15;
pub const ABS_HAT3X: u16 = 0x16;
pub const ABS_HAT3Y: u16 = 0x17;
pub const ABS_PRESSURE: u16 = 0x18;
pub const ABS_DISTANCE: u16 = 0x19;
pub const ABS_TILT_X: u16 = 0x1a;
pub const ABS_TILT_Y: u16 = 0x1b;
pub const ABS_TOOL_WIDTH: u16 = 0x1c;
pub const ABS_VOLUME: u16 = 0x20;
pub const ABS_MISC: u16 = 0x28;
pub const ABS_RESERVED: u16 = 0x2e;
pub const ABS_MT_SLOT: u16 = 0x2f;
pub const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
pub const ABS_MT_TOUCH_MINOR: u16 = 0x31;
pub const ABS_MT_WIDTH_MAJOR: u16 = 0x32;
pub const ABS_MT_WIDTH_MINOR: u16 = 0x33;
pub const ABS_MT_ORIENTATION: u16 = 0x34;
pub const ABS_MT_POSITION_X: u16 = 0x35;
pub const ABS_MT_POSITION_Y: u16 = 0x36;
pub const ABS_MT_TOOL_TYPE: u16 = 0x37;
pub const ABS_MT_BLOB_ID: u16 = 0x38;
pub const ABS_MT_TRACKING_ID: u16 = 0x39;
pub const ABS_MT_PRESSURE: u16 = 0x3a;
pub const ABS_MT_DISTANCE: u16 = 0x3b;
pub const ABS_MT_TOOL_X: u16 = 0x3c;
pub const ABS_MT_TOOL_Y: u16 = 0x3d;
pub const ABS_MAX: u16 = 0x3f;
pub const ABS_CNT: u16 = (ABS_MAX + 1);
}
/// Switch events
pub mod sw {
pub const SW_LID: u16 = 0x00;
pub const SW_TABLET_MODE: u16 = 0x01;
pub const SW_HEADPHONE_INSERT: u16 = 0x02;
pub const SW_RFKILL_ALL: u16 = 0x03;
pub const SW_RADIO: u16 = SW_RFKILL_ALL;
pub const SW_MICROPHONE_INSERT: u16 = 0x04;
pub const SW_DOCK: u16 = 0x05;
pub const SW_LINEOUT_INSERT: u16 = 0x06;
pub const SW_JACK_PHYSICAL_INSERT: u16 = 0x07;
pub const SW_VIDEOOUT_INSERT: u16 = 0x08;
pub const SW_CAMERA_LENS_COVER: u16 = 0x09;
pub const SW_KEYPAD_SLIDE: u16 = 0x0a;
pub const SW_FRONT_PROXIMITY: u16 = 0x0b;
pub const SW_ROTATE_LOCK: u16 = 0x0c;
pub const SW_LINEIN_INSERT: u16 = 0x0d;
pub const SW_MUTE_DEVICE: u16 = 0x0e;
pub const SW_PEN_INSERTED: u16 = 0x0f;
pub const SW_MAX: u16 = 0x0f;
pub const SW_CNT: u16 = (SW_MAX + 1);
}
/// Misc events
pub mod msc {
pub const MSC_SERIAL: u16 = 0x00;
pub const MSC_PULSELED: u16 = 0x01;
pub const MSC_GESTURE: u16 = 0x02;
pub const MSC_RAW: u16 = 0x03;
pub const MSC_SCAN: u16 = 0x04;
pub const MSC_TIMESTAMP: u16 = 0x05;
pub const MSC_MAX: u16 = 0x07;
pub const MSC_CNT: u16 = (MSC_MAX + 1);
}
/// LEDs
pub mod led {
pub const LED_NUML: u16 = 0x00;
pub const LED_CAPSL: u16 = 0x01;
pub const LED_SCROLLL: u16 = 0x02;
pub const LED_COMPOSE: u16 = 0x03;
pub const LED_KANA: u16 = 0x04;
pub const LED_SLEEP: u16 = 0x05;
pub const LED_SUSPEND: u16 = 0x06;
pub const LED_MUTE: u16 = 0x07;
pub const LED_MISC: u16 = 0x08;
pub const LED_MAIL: u16 = 0x09;
pub const LED_CHARGING: u16 = 0x0a;
pub const LED_MAX: u16 = 0x0f;
pub const LED_CNT: u16 = (LED_MAX + 1);
}
/// Autorepeat values
pub mod rep {
pub const REP_DELAY: u16 = 0x00;
pub const REP_PERIOD: u16 = 0x01;
pub const REP_MAX: u16 = 0x01;
pub const REP_CNT: u16 = (REP_MAX + 1);
}
/// Sounds
pub mod snd {
pub const SND_CLICK: u16 = 0x00;
pub const SND_BELL: u16 = 0x01;
pub const SND_TONE: u16 = 0x02;
pub const SND_MAX: u16 = 0x07;
pub const SND_CNT: u16 = (SND_MAX + 1);
}

View File

@ -1,7 +0,0 @@
//! Only Mouse currently.
mod mouse;
pub mod input_event_codes;
pub use mouse::{Mouse, MouseFlags, MouseState};

View File

@ -1,133 +0,0 @@
use alloc::{boxed::Box, sync::Arc};
use lock::Mutex;
use crate::prelude::{CapabilityType, InputEvent, InputEventType};
use crate::scheme::{impl_event_scheme, InputScheme};
use crate::utils::EventListener;
bitflags::bitflags! {
#[derive(Default)]
pub struct MouseFlags: u8 {
/// Whether or not the left mouse button is pressed.
const LEFT_BTN = 1 << 0;
/// Whether or not the right mouse button is pressed.
const RIGHT_BTN = 1 << 1;
/// Whether or not the middle mouse button is pressed.
const MIDDLE_BTN = 1 << 2;
/// Whether or not the packet is valid or not.
const ALWAYS_ONE = 1 << 3;
/// Whether or not the x delta is negative.
const X_SIGN = 1 << 4;
/// Whether or not the y delta is negative.
const Y_SIGN = 1 << 5;
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct MouseState {
pub dx: i32,
pub dy: i32,
pub dz: i32,
pub buttons: MouseFlags,
}
impl MouseState {
pub fn as_ps2_buf(&self) -> [u8; 3] {
let mut flags = self.buttons | MouseFlags::ALWAYS_ONE;
let dx = self.dx.max(-127).min(127);
let dy = self.dy.max(-127).min(127);
if dx < 0 {
flags |= MouseFlags::X_SIGN;
}
if dy < 0 {
flags |= MouseFlags::Y_SIGN;
}
[flags.bits(), dx as u8, dy as u8]
}
}
impl MouseState {
fn update(&mut self, e: &InputEvent) -> Option<MouseState> {
match e.event_type {
InputEventType::Syn => {
use super::input_event_codes::syn::*;
if e.code == SYN_REPORT {
let saved = *self;
self.dx = 0;
self.dy = 0;
self.dz = 0;
return Some(saved);
}
}
InputEventType::Key => {
use super::input_event_codes::key::*;
let btn = match e.code {
BTN_LEFT => MouseFlags::LEFT_BTN,
BTN_RIGHT => MouseFlags::RIGHT_BTN,
BTN_MIDDLE => MouseFlags::MIDDLE_BTN,
_ => return None,
};
if e.value == 0 {
self.buttons -= btn;
} else {
self.buttons |= btn;
}
}
InputEventType::RelAxis => {
use super::input_event_codes::rel::*;
match e.code {
REL_X => self.dx += e.value,
REL_Y => self.dy -= e.value,
REL_WHEEL => self.dz -= e.value,
_ => {}
}
}
_ => {}
}
None
}
}
pub struct Mouse {
listener: EventListener<MouseState>,
state: Mutex<MouseState>,
}
impl_event_scheme!(Mouse, MouseState);
impl Mouse {
pub fn new(input: Arc<dyn InputScheme>) -> Arc<Self> {
let ret = Arc::new(Self {
listener: EventListener::new(),
state: Mutex::new(MouseState::default()),
});
let cloned = ret.clone();
input.subscribe(Box::new(move |e| cloned.handle_input_event(e)), false);
ret
}
fn handle_input_event(&self, e: &InputEvent) {
if let Some(p) = self.state.lock().update(e) {
self.listener.trigger(p);
}
}
pub fn compatible_with(input: &Arc<dyn InputScheme>) -> bool {
// A mouse like device, at least one button, two relative axes.
use super::input_event_codes::{ev::*, key::*, rel::*};
let ev = input.capability(CapabilityType::Event);
let key = input.capability(CapabilityType::Key);
let rel = input.capability(CapabilityType::RelAxis);
if !ev.contains_all(&[EV_KEY, EV_REL]) {
return false;
}
if !key.contains(BTN_LEFT) {
return false;
}
if !rel.contains_all(&[REL_X, REL_Y]) {
return false;
}
true
}
}

View File

@ -1,53 +0,0 @@
use super::Io;
use core::ops::{BitAnd, BitOr, Not};
// 主存映射 I/O。
/// Memory-mapped I/O.
#[repr(transparent)]
pub struct Mmio<T>(T);
impl<T> Mmio<T> {
/// # Safety
///
/// This function is unsafe because `base_addr` may be an arbitrary address.
pub unsafe fn from_base_as<'a, R>(base_addr: usize) -> &'a mut R {
assert_eq!(base_addr % core::mem::size_of::<T>(), 0);
&mut *(base_addr as *mut R)
}
/// # Safety
///
/// This function is unsafe because `base_addr` may be an arbitrary address.
pub unsafe fn from_base<'a>(base_addr: usize) -> &'a mut Self {
Self::from_base_as(base_addr)
}
pub fn add<'a>(&self, offset: usize) -> &'a mut Self {
unsafe { Self::from_base((&self.0 as *const T).add(offset) as _) }
}
}
impl<T> Io for Mmio<T>
where
T: Copy + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T>,
{
type Value = T;
fn read(&self) -> T {
#[allow(clippy::let_and_return)]
unsafe {
let val = core::ptr::read_volatile(&self.0 as *const _);
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
core::arch::asm!("fence i,r");
val
}
}
fn write(&mut self, value: T) {
unsafe {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
core::arch::asm!("fence w,o");
core::ptr::write_volatile(&mut self.0 as *mut _, value)
};
}
}

View File

@ -1,79 +0,0 @@
// 封装对外设地址空间的访问,包括内存映射 IO 和端口映射 IO。
//
// 要了解这两种访问外设的方式,查看[维基百科](https://en.wikipedia.org/wiki/Memory-mapped_I/O)。
//! Peripheral address space access, including memory-mapped IO and port-mapped IO.
//!
//! About these two methods of performing I/O, see [wikipedia](https://en.wikipedia.org/wiki/Memory-mapped_I/O).
use core::ops::{BitAnd, BitOr, Not};
mod mmio;
#[cfg(target_arch = "x86_64")]
mod pmio;
pub use mmio::Mmio;
#[cfg(target_arch = "x86_64")]
pub use pmio::Pmio;
// 用于处理外设地址空间访问的接口。
/// An interface for dealing with device address space access.
pub trait Io {
// 可访问的对象的类型。
/// The type of object to access.
type Value: Copy
+ BitAnd<Output = Self::Value>
+ BitOr<Output = Self::Value>
+ Not<Output = Self::Value>;
// 从外设读取值。
/// Reads value from device.
fn read(&self) -> Self::Value;
// 向外设写入值。
/// Writes `value` to device.
fn write(&mut self, value: Self::Value);
}
// 外设地址空间的一个只读单元。
/// A readonly unit in device address space.
#[repr(transparent)]
pub struct ReadOnly<I>(I);
impl<I> ReadOnly<I> {
// 构造外设地址空间的一个只读单元。
/// Constructs a readonly unit in device address space.
pub const fn new(inner: I) -> Self {
Self(inner)
}
}
impl<I: Io> ReadOnly<I> {
// 从外设读取值。
/// Reads value from device.
#[inline(always)]
pub fn read(&self) -> I::Value {
self.0.read()
}
}
// 外设地址空间的一个只写单元。
/// A write-only unit in device address space.
#[repr(transparent)]
pub struct WriteOnly<I>(I);
impl<I> WriteOnly<I> {
// 构造外设地址空间的一个只写单元。
/// Constructs a write-only unit in device address space.
pub const fn new(inner: I) -> Self {
Self(inner)
}
}
impl<I: Io> WriteOnly<I> {
// 向外设写入值。
/// Writes `value` to device.
#[inline(always)]
pub fn write(&mut self, value: I::Value) {
self.0.write(value);
}
}

View File

@ -1,102 +0,0 @@
// 端口映射 I/O。
//! Port-mapped I/O.
use super::Io;
use core::{arch::asm, marker::PhantomData};
// 端口映射 I/O。
/// Port-mapped I/O.
#[derive(Copy, Clone)]
pub struct Pmio<T> {
port: u16,
_phantom: PhantomData<T>,
}
impl<T> Pmio<T> {
// 映射指定端口进行外设访问。
/// Maps a given port to assess device.
pub const fn new(port: u16) -> Self {
Self {
port,
_phantom: PhantomData,
}
}
}
// 逐字节端口映射读写。
/// Read/Write for byte PMIO.
impl Io for Pmio<u8> {
type Value = u8;
// 读。
/// Read.
#[inline(always)]
fn read(&self) -> u8 {
let value: u8;
unsafe {
asm!("in al, dx", out("al") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
value
}
// 写。
/// Write.
#[inline(always)]
fn write(&mut self, value: u8) {
unsafe {
asm!("out dx, al", in("al") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
}
}
// 逐字端口映射读写。
/// Read/Write for word PMIO.
impl Io for Pmio<u16> {
type Value = u16;
// 读。
/// Read.
#[inline(always)]
fn read(&self) -> u16 {
let value: u16;
unsafe {
asm!("in ax, dx", out("ax") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
value
}
// 写。
/// Write.
#[inline(always)]
fn write(&mut self, value: u16) {
unsafe {
asm!("out dx, ax", in("ax") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
}
}
// 逐双字端口映射读写。
/// Read/Write for double-word PMIO.
impl Io for Pmio<u32> {
type Value = u32;
// 读。
/// Read.
#[inline(always)]
fn read(&self) -> u32 {
let value: u32;
unsafe {
asm!("in eax, dx", out("eax") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
value
}
// 写。
/// Write.
#[inline(always)]
fn write(&mut self, value: u32) {
unsafe {
asm!("out dx, eax", in("eax") value, in("dx") self.port, options(nomem, nostack, preserves_flags));
}
}
}

View File

@ -1,22 +0,0 @@
//! External interrupt request and handle.
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] {
mod riscv_intc;
mod riscv_plic;
/// Implementation of risc-v interrupt controller.
#[doc(cfg(any(target_arch = "riscv32", target_arch = "riscv64")))]
pub mod riscv {
pub use super::riscv_intc::{Intc, ScauseIntCode};
pub use super::riscv_plic::Plic;
}
} 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 {
pub use super::x86_apic::Apic;
}
}
}

View File

@ -1,131 +0,0 @@
use lock::Mutex;
use riscv::register::sie;
use crate::prelude::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
use crate::{DeviceError, DeviceResult};
use alloc::format;
use alloc::string::String;
use core::sync::atomic::{AtomicU8, Ordering};
const S_SOFT: usize = 1;
const S_TIMER: usize = 5;
const S_EXT: usize = 9;
static INTC_NUM: AtomicU8 = AtomicU8::new(0);
#[repr(usize)]
pub enum ScauseIntCode {
SupervisorSoft = S_SOFT,
SupervisorTimer = S_TIMER,
SupervisorExternal = S_EXT,
}
pub struct Intc {
name: String,
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: 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(&mut Option<IrqHandler>) -> DeviceResult,
{
match cause {
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)
}
}
}
}
impl Default for Intc {
fn default() -> Self {
Self::new()
}
}
impl Scheme for Intc {
fn name(&self) -> &str {
self.name.as_str()
}
fn handle_irq(&self, cause: usize) {
self.with_handler(cause, |opt| {
if let Some(h) = opt {
h();
} else {
warn!("no registered handler for SCAUSE {}!", cause);
}
Ok(())
})
.unwrap();
}
}
impl IrqScheme for Intc {
fn is_valid_irq(&self, cause: usize) -> bool {
matches!(cause, S_SOFT | S_TIMER | S_EXT)
}
fn mask(&self, cause: usize) -> DeviceResult {
unsafe {
match cause {
S_SOFT => sie::clear_ssoft(),
S_TIMER => sie::clear_stimer(),
S_EXT => sie::clear_sext(),
_ => return Err(DeviceError::InvalidParam),
}
}
Ok(())
}
fn unmask(&self, cause: usize) -> DeviceResult {
unsafe {
match cause {
S_SOFT => sie::set_ssoft(),
S_TIMER => sie::set_stimer(),
S_EXT => sie::set_sext(),
_ => return Err(DeviceError::InvalidParam),
}
}
Ok(())
}
fn register_handler(&self, cause: usize, handler: IrqHandler) -> DeviceResult {
self.with_handler(cause, |opt| {
if opt.is_some() {
Err(DeviceError::AlreadyExists)
} else {
*opt = Some(handler);
Ok(())
}
})
}
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,171 +0,0 @@
use core::arch::asm;
use core::ops::Range;
use lock::Mutex;
use crate::io::{Io, Mmio};
use crate::prelude::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult};
const IRQ_RANGE: Range<usize> = 1..1024;
const PLIC_PRIORITY_BASE: usize = 0x0;
const PLIC_ENABLE_BASE: usize = 0x2080;
const PLIC_CONTEXT_BASE: usize = 0x20_1000;
const PLIC_CONTEXT_THRESHOLD: usize = 0x0;
const PLIC_CONTEXT_CLAIM: usize = 0x4 / core::mem::size_of::<u32>();
const PLIC_ENABLE_HART_OFFSET: usize = 0x100 / core::mem::size_of::<u32>();
const PLIC_PRIORITY_HART_OFFSET: usize = 0x2000 / core::mem::size_of::<u32>();
const PLIC_CONTEXT_CLAIM_HART_OFFSET: usize = 0x2000 / core::mem::size_of::<u32>();
struct PlicUnlocked {
priority_base: &'static mut Mmio<u32>,
enable_base: &'static mut Mmio<u32>,
context_base: &'static mut Mmio<u32>,
manager: IrqManager<1024>,
}
pub struct Plic {
inner: Mutex<PlicUnlocked>,
}
impl PlicUnlocked {
/// Toggle irq enable on the current hart.
fn toggle(&mut self, irq_num: usize, enable: bool) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
let hart_id = cpu_id() as usize;
let mmio = self
.enable_base
.add(PLIC_ENABLE_HART_OFFSET * hart_id + irq_num / 32);
let mask = 1 << (irq_num % 32);
if enable {
mmio.write(mmio.read() | mask);
} else {
mmio.write(mmio.read() & !mask);
}
}
/// Ask the PLIC what type of interrupt is occurred on the current hart.
fn pending_irq(&mut self) -> Option<usize> {
let hart_id = cpu_id() as usize;
let irq_num = self
.context_base
.add(PLIC_CONTEXT_CLAIM_HART_OFFSET * hart_id + PLIC_CONTEXT_CLAIM)
.read() as usize;
if irq_num == 0 {
None
} else {
Some(irq_num)
}
}
/// Tell the PLIC we've served this IRQ.
fn eoi(&mut self, irq_num: usize) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
let hart_id = cpu_id() as usize;
self.context_base
.add(PLIC_CONTEXT_CLAIM + PLIC_CONTEXT_CLAIM_HART_OFFSET * hart_id)
.write(irq_num as _);
}
/// Set the priority for the irq_num.
fn set_priority(&mut self, irq_num: usize, priority: u8) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
self.priority_base.add(irq_num).write(priority as _);
}
/// Set current hart's priority threshold to 0.
fn set_threshold(&mut self, threshold: u8) {
let hart_id = cpu_id() as usize;
self.context_base
.add(PLIC_PRIORITY_HART_OFFSET * hart_id + PLIC_CONTEXT_THRESHOLD)
.write(threshold as _);
}
fn init_hart(&mut self) {
self.set_threshold(0);
}
}
impl Plic {
pub fn new(base: usize) -> Self {
let mut inner = PlicUnlocked {
priority_base: unsafe { Mmio::<u32>::from_base(base + PLIC_PRIORITY_BASE) },
enable_base: unsafe { Mmio::<u32>::from_base(base + PLIC_ENABLE_BASE) },
context_base: unsafe { Mmio::<u32>::from_base(base + PLIC_CONTEXT_BASE) },
manager: IrqManager::new(IRQ_RANGE),
};
inner.init_hart();
Self {
inner: Mutex::new(inner),
}
}
}
impl Scheme for Plic {
fn name(&self) -> &str {
"riscv-plic"
}
fn handle_irq(&self, _unused: usize) {
let mut inner = self.inner.lock();
while let Some(irq_num) = inner.pending_irq() {
if inner.manager.handle(irq_num).is_err() {
warn!("no registered handler for IRQ {}!", irq_num);
}
trace!("riscv plic handle irq: {}", irq_num);
inner.eoi(irq_num);
}
}
}
impl IrqScheme for Plic {
fn is_valid_irq(&self, irq_num: usize) -> bool {
IRQ_RANGE.contains(&irq_num)
}
fn mask(&self, irq_num: usize) -> DeviceResult {
if self.is_valid_irq(irq_num) {
self.inner.lock().toggle(irq_num, false);
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
}
fn unmask(&self, irq_num: usize) -> DeviceResult {
if self.is_valid_irq(irq_num) {
self.inner.lock().toggle(irq_num, true);
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
}
fn register_handler(&self, irq_num: usize, handler: IrqHandler) -> DeviceResult {
let mut inner = self.inner.lock();
inner.manager.register_handler(irq_num, handler).map(|_| {
inner.set_priority(irq_num, 7);
})
}
fn unregister(&self, irq_num: usize) -> DeviceResult {
self.inner.lock().manager.unregister_handler(irq_num)
}
fn init_hart(&self) {
self.inner.lock().init_hart();
}
}
fn cpu_id() -> u8 {
let mut cpu_id;
unsafe {
asm!("mv {0}, tp", out(reg) cpu_id);
}
cpu_id
}

View File

@ -1,8 +0,0 @@
// TODO: configurable
pub const X86_INT_BASE: usize = 0x20;
pub const X86_INT_LOCAL_APIC_BASE: usize = 0xf0;
pub const X86_INT_APIC_SPURIOUS: usize = X86_INT_LOCAL_APIC_BASE;
pub const X86_INT_APIC_TIMER: usize = X86_INT_LOCAL_APIC_BASE + 0x1;
pub const X86_INT_APIC_ERROR: usize = X86_INT_LOCAL_APIC_BASE + 0x2;

View File

@ -1,204 +0,0 @@
use alloc::vec::Vec;
use core::{fmt, ptr::NonNull};
use acpi::platform::interrupt::InterruptModel;
use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
use lock::Mutex;
use x2apic::ioapic::{IoApic as IoApicInner, IrqFlags, IrqMode};
use super::{IrqPolarity, IrqTriggerMode, Phys2VirtFn};
const PAGE_SIZE: usize = 4096;
#[derive(Clone)]
struct AcpiMapHandler {
phys_to_virt: Phys2VirtFn,
}
impl AcpiHandler for AcpiMapHandler {
/// we just impl this function, `rdsp` crate will use it, and we not.
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
// address maybe not aligned, so we align it mannualy
let aligned_start = physical_address & !(PAGE_SIZE - 1);
let aligned_end = (physical_address + size + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked((self.phys_to_virt)(physical_address) as *mut T),
size,
aligned_end - aligned_start,
self.clone(),
)
}
/// we do nothing here
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
/// An I/O APIC structure.
///
/// For local APIC and I/O APIC, we can learn something from here: <https://wiki.osdev.org/APIC>.
pub struct IoApic {
/// I/O APIC id.
id: u8,
/// GSI means Global System Interrupt, `gsi_start` is the base number of GSI
/// in this I/O APIC.
gsi_start: u32,
/// Max entry num of the interrupt redirection table.
max_entry: u8,
/// Use `x2apic` crate to help us manipulate IOAPIC.
inner: Mutex<IoApicInner>,
}
/// A list of I/O-APICs for systems have multiple I/O subsystems.
#[derive(Debug)]
pub struct IoApicList {
io_apics: Vec<IoApic>,
}
impl IoApic {
/// Create a new [`IoApic`] from fields parsed from the ACPI table, and
/// initialize it by disabling all interrupts.
pub fn new(id: u8, base_vaddr: usize, gsi_start: u32) -> Self {
let mut inner = unsafe { IoApicInner::new(base_vaddr as u64) };
let max_entry = unsafe { inner.max_table_entry() };
unsafe { assert_eq!(id, inner.id()) };
unsafe {
inner.init(super::X86_INT_BASE as u8);
}
for i in 0..max_entry + 1 {
unsafe {
// disable all interrupts
inner.disable_irq(i);
// Clean the redirection table
let mut entry = inner.table_entry(i);
entry.set_vector(0);
entry.set_dest(0);
entry.set_mode(IrqMode::Fixed);
entry.set_flags(IrqFlags::MASKED);
inner.set_table_entry(i, entry);
}
}
Self {
id,
gsi_start,
max_entry,
inner: Mutex::new(inner),
}
}
/// Set apic entry IRQ state by `gsi`.
pub fn toggle(&self, gsi: u32, enabled: bool) {
let idx = (gsi - self.gsi_start) as u8;
unsafe {
if enabled {
self.inner.lock().enable_irq(idx);
} else {
self.inner.lock().disable_irq(idx);
}
}
}
/// Get the IDT vector of the `gsi` from redirection table.
pub fn get_vector(&self, gsi: u32) -> u8 {
let idx = (gsi - self.gsi_start) as u8;
unsafe { self.inner.lock().table_entry(idx).vector() }
}
/// Set the IDT vector of the `gsi` in redirection table.
pub fn map_vector(&self, gsi: u32, vector: u8) {
let idx = (gsi - self.gsi_start) as u8;
let mut inner = self.inner.lock();
unsafe {
let mut entry = inner.table_entry(idx);
entry.set_vector(vector);
inner.set_table_entry(idx, entry);
}
}
/// Set the interrupt triggle mode, polarity and other fields of the `gsi`
/// in redirection table.
pub fn configure(&self, gsi: u32, tm: IrqTriggerMode, pol: IrqPolarity, dest: u8, vector: u8) {
let idx = (gsi - self.gsi_start) as u8;
let mut inner = self.inner.lock();
let mut entry = unsafe { inner.table_entry(idx) };
entry.set_vector(vector);
entry.set_mode(IrqMode::Fixed);
entry.set_dest(dest);
let mut flags = IrqFlags::MASKED; // destination mode: physical
if matches!(tm, IrqTriggerMode::Edge) {
flags |= IrqFlags::LEVEL_TRIGGERED;
}
if matches!(pol, IrqPolarity::ActiveLow) {
flags |= IrqFlags::LOW_ACTIVE;
}
entry.set_flags(flags);
unsafe { inner.set_table_entry(idx, entry) };
}
}
impl IoApicList {
/// Probe all I/O APICs from the ACPI table represented by `acpi_rsdp`.
pub fn new(acpi_rsdp: usize, phys_to_virt: Phys2VirtFn) -> Self {
let handler = AcpiMapHandler { phys_to_virt };
// parse ACPI table by the physical address of the RSDP.
let tables = unsafe { AcpiTables::from_rsdp(handler, acpi_rsdp).unwrap() };
let io_apics =
if let InterruptModel::Apic(apic) = tables.platform_info().unwrap().interrupt_model {
apic.io_apics
.iter()
.map(|i| {
IoApic::new(
i.id,
phys_to_virt(i.address as usize),
i.global_system_interrupt_base,
)
})
.collect()
} else {
// only legacy i8259 PIC is present
Vec::new()
};
Self { io_apics }
}
/// Get the corresponding I/O APIC of the `gsi`, each I/O-APIC have a range
/// of GSI number.
pub fn find(&self, gsi: u32) -> Option<&IoApic> {
self.io_apics
.iter()
.find(|i| i.gsi_start <= gsi && gsi <= i.gsi_start + i.max_entry as u32)
}
}
impl fmt::Debug for IoApic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct RedirTable<'a>(&'a IoApic);
impl<'a> fmt::Debug for RedirTable<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut inner = self.0.inner.lock();
let count = self.0.max_entry + 1;
f.debug_list()
.entries((0..count).map(|i| unsafe { inner.table_entry(i) }))
.finish()
}
}
let version = unsafe { self.inner.lock().version() };
f.debug_struct("IoApic")
.field("id", &self.id)
.field("version", &version)
.field("gsi_start", &self.gsi_start)
.field("max_entry", &self.max_entry)
.field("redir_table", &RedirTable(self))
.finish()
}
}

View File

@ -1,72 +0,0 @@
use x2apic::lapic::{
xapic_base, LocalApic as LocalApicInner, LocalApicBuilder, TimerDivide, TimerMode,
};
use super::{consts, Phys2VirtFn};
static mut LOCAL_APIC: Option<LocalApic> = None;
static mut BSP_ID: Option<u8> = None;
pub struct LocalApic {
inner: LocalApicInner,
}
impl LocalApic {
pub unsafe fn get<'a>() -> &'a mut LocalApic {
LOCAL_APIC
.as_mut()
.expect("Local APIC is not initialized by BSP")
}
pub unsafe fn init_bsp(phys_to_virt: Phys2VirtFn) {
let base_vaddr = phys_to_virt(xapic_base() as usize);
let mut inner = LocalApicBuilder::new()
.timer_vector(consts::X86_INT_APIC_TIMER)
.error_vector(consts::X86_INT_APIC_ERROR)
.spurious_vector(consts::X86_INT_APIC_SPURIOUS)
.set_xapic_base(base_vaddr as u64)
.build()
.unwrap_or_else(|err| panic!("{}", err));
inner.enable();
assert!(inner.is_bsp());
BSP_ID = Some((inner.id() >> 24) as u8);
LOCAL_APIC = Some(LocalApic { inner });
}
pub unsafe fn init_ap() {
Self::get().inner.enable();
}
pub fn bsp_id() -> u8 {
unsafe { BSP_ID.unwrap() }
}
pub fn id(&mut self) -> u8 {
unsafe { (self.inner.id() >> 24) as u8 }
}
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

@ -1,176 +0,0 @@
mod consts;
mod ioapic;
mod lapic;
use self::consts::{X86_INT_BASE, X86_INT_LOCAL_APIC_BASE};
use self::ioapic::{IoApic, IoApicList};
use self::lapic::LocalApic;
use crate::prelude::{IrqHandler, IrqPolarity, IrqTriggerMode};
use crate::scheme::{IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult, PhysAddr, VirtAddr};
use core::ops::Range;
use lock::Mutex;
const IOAPIC_IRQ_RANGE: Range<usize> = X86_INT_BASE..X86_INT_LOCAL_APIC_BASE;
const LAPIC_IRQ_RANGE: Range<usize> = 0..16;
type Phys2VirtFn = fn(paddr: PhysAddr) -> VirtAddr;
/// Advanced Programmable Interrupt Controller
pub struct Apic {
ioapic_list: IoApicList,
manager_ioapic: Mutex<IrqManager<256>>,
manager_lapic: Mutex<IrqManager<16>>,
}
impl Apic {
/// Construct a new `Apic`.
pub fn new(acpi_rsdp: usize, phys_to_virt: Phys2VirtFn) -> Self {
Self {
ioapic_list: IoApicList::new(acpi_rsdp, phys_to_virt),
manager_ioapic: Mutex::new(IrqManager::new(IOAPIC_IRQ_RANGE)),
manager_lapic: Mutex::new(IrqManager::new(LAPIC_IRQ_RANGE)),
}
}
fn with_ioapic<F>(&self, gsi: u32, op: F) -> DeviceResult
where
F: FnOnce(&IoApic) -> DeviceResult,
{
if let Some(apic) = self.ioapic_list.find(gsi) {
op(apic)
} else {
error!(
"cannot find IOAPIC for global system interrupt number {}",
gsi
);
Err(DeviceError::InvalidParam)
}
}
pub fn init_local_apic_bsp(phys_to_virt: Phys2VirtFn) {
unsafe { LocalApic::init_bsp(phys_to_virt) }
}
pub fn init_local_apic_ap() {
unsafe { LocalApic::init_ap() }
}
pub fn local_apic<'a>() -> &'a mut LocalApic {
unsafe { LocalApic::get() }
}
pub fn register_local_apic_handler(&self, vector: usize, handler: IrqHandler) -> DeviceResult {
if vector >= X86_INT_LOCAL_APIC_BASE {
self.manager_lapic
.lock()
.register_handler(vector - X86_INT_LOCAL_APIC_BASE, handler)?;
Ok(())
} else {
error!("invalid local APIC interrupt vector {}", vector);
Err(DeviceError::InvalidParam)
}
}
}
impl Scheme for Apic {
fn name(&self) -> &str {
"x86-apic"
}
fn handle_irq(&self, vector: usize) {
Self::local_apic().eoi();
let res = if vector >= X86_INT_LOCAL_APIC_BASE {
let handler = self.manager_lapic.lock();
handler.handle(vector - X86_INT_LOCAL_APIC_BASE)
} else {
self.manager_ioapic.lock().handle(vector)
};
if res.is_err() {
warn!("no registered handler for interrupt vector {}!", vector);
}
}
}
impl IrqScheme for Apic {
fn is_valid_irq(&self, gsi: usize) -> bool {
self.ioapic_list.find(gsi as _).is_some()
}
fn mask(&self, gsi: usize) -> DeviceResult {
self.with_ioapic(gsi as _, |apic| {
apic.toggle(gsi as _, false);
Ok(())
})
}
fn unmask(&self, gsi: usize) -> DeviceResult {
self.with_ioapic(gsi as _, |apic| {
apic.toggle(gsi as _, true);
Ok(())
})
}
fn configure(&self, gsi: usize, tm: IrqTriggerMode, pol: IrqPolarity) -> DeviceResult {
let gsi = gsi as u32;
self.with_ioapic(gsi, |apic| {
apic.configure(gsi, tm, pol, LocalApic::bsp_id(), 0);
Ok(())
})
}
fn register_handler(&self, gsi: usize, handler: IrqHandler) -> DeviceResult {
let gsi = gsi as u32;
self.with_ioapic(gsi, |apic| {
let vector = apic.get_vector(gsi) as _; // if not mapped, allocate an available vector by `register_handler()`.
let vector = self
.manager_ioapic
.lock()
.register_handler(vector, handler)? as u8;
apic.map_vector(gsi, vector);
Ok(())
})
}
fn unregister(&self, gsi: usize) -> DeviceResult {
let gsi = gsi as u32;
self.with_ioapic(gsi, |apic| {
let vector = apic.get_vector(gsi) as _;
self.manager_ioapic.lock().unregister_handler(vector)?;
apic.map_vector(gsi, 0);
Ok(())
})
}
fn msi_alloc_block(&self, requested_irqs: usize) -> DeviceResult<Range<usize>> {
let alloc_size = requested_irqs.next_power_of_two();
let start = self.manager_ioapic.lock().alloc_block(alloc_size)?;
Ok(start..start + alloc_size)
}
fn msi_free_block(&self, block: Range<usize>) -> DeviceResult {
self.manager_lapic
.lock()
.free_block(block.start, block.len())
}
fn msi_register_handler(
&self,
block: Range<usize>,
msi_id: usize,
handler: IrqHandler,
) -> DeviceResult {
if msi_id < block.len() {
self.manager_ioapic
.lock()
.overwrite_handler(block.start + msi_id, handler)
} else {
Err(DeviceError::InvalidParam)
}
}
fn apic_timer_enable(&self) {
// SAFETY: this will called only once for every core
Apic::local_apic().enable_timer();
}
}

View File

@ -1,103 +0,0 @@
//! Device drivers of zCore.
#![cfg_attr(not(feature = "mock"), no_std)]
#![feature(doc_cfg)]
extern crate alloc;
#[macro_use]
extern crate log;
use alloc::sync::Arc;
use core::fmt;
#[cfg(any(feature = "mock", doc))]
#[doc(cfg(feature = "mock"))]
pub mod mock;
#[cfg(any(feature = "virtio", doc))]
#[doc(cfg(feature = "virtio"))]
pub mod virtio;
pub mod builder;
pub mod bus;
pub mod display;
pub mod input;
pub mod io;
pub mod irq;
pub mod net;
pub mod prelude;
pub mod scheme;
pub mod uart;
pub mod utils;
/// The error type for external device.
#[derive(Debug)]
pub enum DeviceError {
/// The buffer is too small.
BufferTooSmall,
/// The device is not ready.
NotReady,
/// Invalid parameter.
InvalidParam,
/// Failed to alloc DMA memory.
DmaError,
/// I/O Error
IoError,
/// A resource with the specified identifier already exists.
AlreadyExists,
/// No resource to allocate.
NoResources,
/// The device driver is not implemented, supported, or enabled.
NotSupported,
}
/// A type alias for the result of a device operation.
pub type DeviceResult<T = ()> = core::result::Result<T, DeviceError>;
/// Static shell of shared dynamic device [`Scheme`](crate::scheme::Scheme) types.
#[derive(Clone)]
pub enum Device {
/// Block device
Block(Arc<dyn scheme::BlockScheme>),
/// Display device
Display(Arc<dyn scheme::DisplayScheme>),
/// Input device
Input(Arc<dyn scheme::InputScheme>),
/// Interrupt request and handle
Irq(Arc<dyn scheme::IrqScheme>),
/// Network device
Net(Arc<dyn scheme::NetScheme>),
/// Uart port
Uart(Arc<dyn scheme::UartScheme>),
}
impl Device {
/// Get a general [`Scheme`](scheme::Scheme) from the device.
pub fn inner(&self) -> Arc<dyn scheme::Scheme> {
match self {
Self::Block(d) => d.clone().upcast(),
Self::Display(d) => d.clone().upcast(),
Self::Input(d) => d.clone().upcast(),
Self::Irq(d) => d.clone().upcast(),
Self::Net(d) => d.clone().upcast(),
Self::Uart(d) => d.clone().upcast(),
}
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Block(d) => write!(f, "BlockDevice({:?})", d.name()),
Self::Display(d) => write!(f, "DisplayDevice({:?})", d.name()),
Self::Input(d) => write!(f, "InputDevice({:?})", d.name()),
Self::Irq(d) => write!(f, "IrqDevice({:?})", d.name()),
Self::Net(d) => write!(f, "NetDevice({:?})", d.name()),
Self::Uart(d) => write!(f, "UartDevice({:?})", d.name()),
}
}
}
type PhysAddr = usize;
type VirtAddr = usize;

View File

@ -1,64 +0,0 @@
use alloc::vec::Vec;
use crate::prelude::{ColorFormat, DisplayInfo, FrameBuffer};
use crate::scheme::{DisplayScheme, Scheme};
pub struct MockDisplay {
info: DisplayInfo,
fb: Vec<u8>,
}
impl MockDisplay {
pub fn new(width: u32, height: u32, format: ColorFormat) -> Self {
let fb_size = (width * height * format.bytes() as u32) as usize;
let fb = vec![0; fb_size];
let info = DisplayInfo {
width,
height,
format,
fb_base_vaddr: fb.as_ptr() as usize,
fb_size,
};
Self { info, fb }
}
/// # Safety
///
/// This function is unsafe, the caller must ensure the `ptr` points to the
/// start of a valid frame buffer.
pub unsafe fn from_raw_parts(
width: u32,
height: u32,
format: ColorFormat,
ptr: *mut u8,
) -> Self {
let fb_size = (width * height * format.bytes() as u32) as usize;
let fb = Vec::from_raw_parts(ptr, fb_size, fb_size);
let info = DisplayInfo {
width,
height,
format,
fb_base_vaddr: fb.as_ptr() as usize,
fb_size,
};
Self { info, fb }
}
}
impl Scheme for MockDisplay {
fn name(&self) -> &str {
"mock-display"
}
}
impl DisplayScheme for MockDisplay {
#[inline]
fn info(&self) -> DisplayInfo {
self.info
}
#[inline]
fn fb(&self) -> FrameBuffer {
unsafe { FrameBuffer::from_raw_parts_mut(self.fb.as_ptr() as _, self.info.fb_size) }
}
}

View File

@ -1 +0,0 @@
pub mod sdl;

View File

@ -1,423 +0,0 @@
use alloc::sync::Arc;
use sdl2::{event::Event, EventPump};
use sdl2::{keyboard::Scancode, mouse::MouseButton};
use sdl2::{pixels::PixelFormatEnum, render::Canvas, video::Window};
use crate::input::input_event_codes::{key::*, rel::*, syn::*};
use crate::prelude::{ColorFormat, InputEvent, InputEventType};
use crate::scheme::{DisplayScheme, InputScheme};
pub struct SdlWindow {
canvas: Canvas<Window>,
event_pump: EventPump,
display: Arc<dyn DisplayScheme>,
handler: EventHandler,
is_quit: bool,
}
impl SdlWindow {
pub fn new(title: &str, display: Arc<dyn DisplayScheme>) -> Self {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window(title, display.info().width, display.info().height)
.position_centered()
.build()
.unwrap();
let event_pump = sdl_context.event_pump().unwrap();
let canvas = window.into_canvas().build().unwrap();
let mut ret = Self {
canvas,
event_pump,
display,
handler: EventHandler::default(),
is_quit: false,
};
ret.flush();
ret
}
pub fn is_quit(&self) -> bool {
self.is_quit
}
pub fn register_mouse(&mut self, mouse: Arc<dyn InputScheme>) {
self.handler.mouse = Some(mouse);
}
pub fn register_keyboard(&mut self, keyboard: Arc<dyn InputScheme>) {
self.handler.keyboard = Some(keyboard);
}
pub fn flush(&mut self) {
let info = self.display.info();
let texture_creator = self.canvas.texture_creator();
let format: PixelFormatEnum = info.format.into();
let mut texture = texture_creator
.create_texture_streaming(format, info.width, info.height)
.unwrap();
texture
.update(None, &self.display.fb(), info.pitch() as usize)
.unwrap();
self.canvas.copy(&texture, None, None).unwrap();
self.canvas.present();
}
pub fn handle_events(&mut self) {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. } => self.is_quit = true,
Event::MouseMotion { xrel, yrel, .. } => self.handler.mouse_move(xrel, yrel),
Event::MouseButtonDown { mouse_btn, .. } => {
self.handler.mouse_button(mouse_btn, true)
}
Event::MouseButtonUp { mouse_btn, .. } => {
self.handler.mouse_button(mouse_btn, false)
}
Event::KeyDown {
scancode: Some(code),
..
} => {
self.handler.key(code, true);
if code == Scancode::Escape {
self.is_quit = true;
}
}
Event::KeyUp {
scancode: Some(code),
..
} => self.handler.key(code, false),
_ => {}
}
}
}
}
#[derive(Default)]
struct EventHandler {
mouse: Option<Arc<dyn InputScheme>>,
keyboard: Option<Arc<dyn InputScheme>>,
}
impl EventHandler {
fn mouse_move(&self, rel_x: i32, rel_y: i32) {
if let Some(ref m) = self.mouse {
m.trigger(InputEvent {
event_type: InputEventType::RelAxis,
code: REL_X,
value: rel_x,
});
m.trigger(InputEvent {
event_type: InputEventType::RelAxis,
code: REL_Y,
value: rel_y,
});
m.trigger(InputEvent {
event_type: InputEventType::Syn,
code: SYN_REPORT,
value: 0,
});
}
}
fn mouse_button(&self, btn: MouseButton, down: bool) {
if let Some(ref m) = self.mouse {
let code = match btn {
MouseButton::Left => BTN_LEFT,
MouseButton::Right => BTN_RIGHT,
MouseButton::Middle => BTN_MIDDLE,
_ => return,
};
let value = if down { 1 } else { 0 };
m.trigger(InputEvent {
event_type: InputEventType::Key,
code,
value,
});
m.trigger(InputEvent {
event_type: InputEventType::Syn,
code: SYN_REPORT,
value: 0,
});
}
}
fn key(&self, scancode: Scancode, down: bool) {
if let Some(ref m) = self.keyboard {
if let Some(code) = scancode_2_eventcode(scancode) {
let value = if down { 1 } else { 0 };
m.trigger(InputEvent {
event_type: InputEventType::Key,
code,
value,
});
m.trigger(InputEvent {
event_type: InputEventType::Syn,
code: SYN_REPORT,
value: 0,
});
}
}
}
}
impl core::convert::From<ColorFormat> for PixelFormatEnum {
fn from(format: ColorFormat) -> Self {
match format {
ColorFormat::RGB332 => Self::RGB332,
ColorFormat::RGB565 => Self::RGB565,
ColorFormat::RGB888 => Self::BGR24, // notice: BGR24 means R at the highest address, B at the lowest address.
ColorFormat::ARGB8888 => Self::ARGB8888,
}
}
}
fn scancode_2_eventcode(code: Scancode) -> Option<u16> {
use Scancode::*;
Some(match code {
A => KEY_A,
B => KEY_B,
C => KEY_C,
D => KEY_D,
E => KEY_E,
F => KEY_F,
G => KEY_G,
H => KEY_H,
I => KEY_I,
J => KEY_J,
K => KEY_K,
L => KEY_L,
M => KEY_M,
N => KEY_N,
O => KEY_O,
P => KEY_P,
Q => KEY_Q,
R => KEY_R,
S => KEY_S,
T => KEY_T,
U => KEY_U,
V => KEY_V,
W => KEY_W,
X => KEY_X,
Y => KEY_Y,
Z => KEY_Z,
Num1 => KEY_1,
Num2 => KEY_2,
Num3 => KEY_3,
Num4 => KEY_4,
Num5 => KEY_5,
Num6 => KEY_6,
Num7 => KEY_7,
Num8 => KEY_8,
Num9 => KEY_9,
Num0 => KEY_0,
Return => KEY_ENTER,
Escape => KEY_ESC,
Backspace => KEY_BACKSPACE,
Tab => KEY_TAB,
Space => KEY_SPACE,
Minus => KEY_MINUS,
Equals => KEY_EQUAL,
LeftBracket => KEY_LEFTBRACE,
RightBracket => KEY_RIGHTBRACE,
Backslash => KEY_BACKSLASH,
NonUsHash => return None,
Semicolon => KEY_SEMICOLON,
Apostrophe => KEY_APOSTROPHE,
Grave => KEY_GRAVE,
Comma => KEY_COMMA,
Period => KEY_DOT,
Slash => KEY_SLASH,
CapsLock => KEY_CAPSLOCK,
F1 => KEY_F1,
F2 => KEY_F2,
F3 => KEY_F3,
F4 => KEY_F4,
F5 => KEY_F5,
F6 => KEY_F6,
F7 => KEY_F7,
F8 => KEY_F8,
F9 => KEY_F9,
F10 => KEY_F10,
F11 => KEY_F11,
F12 => KEY_F12,
PrintScreen => return None,
ScrollLock => KEY_SCROLLLOCK,
Pause => KEY_PAUSE,
Insert => KEY_INSERT,
Home => KEY_HOME,
PageUp => KEY_PAGEUP,
Delete => KEY_DELETE,
End => KEY_END,
PageDown => KEY_PAGEDOWN,
Right => KEY_RIGHT,
Left => KEY_LEFT,
Down => KEY_DOWN,
Up => KEY_UP,
NumLockClear => KEY_NUMLOCK,
KpDivide => KEY_KPSLASH,
KpMultiply => KEY_KPASTERISK,
KpMinus => KEY_KPMINUS,
KpPlus => KEY_KPPLUS,
KpEnter => KEY_KPENTER,
Kp1 => KEY_KP1,
Kp2 => KEY_KP2,
Kp3 => KEY_KP3,
Kp4 => KEY_KP4,
Kp5 => KEY_KP5,
Kp6 => KEY_KP6,
Kp7 => KEY_KP7,
Kp8 => KEY_KP8,
Kp9 => KEY_KP9,
Kp0 => KEY_KP0,
KpPeriod => KEY_KPDOT,
NonUsBackslash => KEY_102ND,
Application => return None,
Power => KEY_POWER,
KpEquals => KEY_KPEQUAL,
F13 => KEY_F13,
F14 => KEY_F14,
F15 => KEY_F15,
F16 => KEY_F16,
F17 => KEY_F17,
F18 => KEY_F18,
F19 => KEY_F19,
F20 => KEY_F20,
F21 => KEY_F21,
F22 => KEY_F22,
F23 => KEY_F23,
F24 => KEY_F24,
Execute => return None,
Help => KEY_HELP,
Menu => KEY_MENU,
Select => return None,
Stop => KEY_STOP,
Again => KEY_AGAIN,
Undo => KEY_UNDO,
Cut => KEY_CUT,
Copy => KEY_COPY,
Paste => KEY_PASTE,
Find => KEY_FIND,
Mute => KEY_MUTE,
VolumeUp => KEY_VOLUMEUP,
VolumeDown => KEY_VOLUMEDOWN,
KpComma => KEY_KPCOMMA,
KpEqualsAS400 => return None,
International1 => KEY_RO,
International2 => KEY_KATAKANAHIRAGANA,
International3 => KEY_YEN,
International4 => KEY_HENKAN,
International5 => KEY_MUHENKAN,
International6 => return None,
International7 => return None,
International8 => return None,
International9 => return None,
Lang1 => KEY_HANGEUL,
Lang2 => KEY_HANJA,
Lang3 => KEY_KATAKANA,
Lang4 => KEY_HIRAGANA,
Lang5 => return None,
Lang6 => return None,
Lang7 => return None,
Lang8 => return None,
Lang9 => return None,
AltErase => KEY_ALTERASE,
SysReq => KEY_SYSRQ,
Cancel => KEY_CANCEL,
Clear => return None,
Prior => return None,
Return2 => return None,
Separator => return None,
Out => return None,
Oper => return None,
ClearAgain => return None,
CrSel => return None,
ExSel => return None,
Kp00 => return None,
Kp000 => return None,
ThousandsSeparator => return None,
DecimalSeparator => return None,
CurrencyUnit => return None,
CurrencySubUnit => return None,
KpLeftParen => KEY_KPLEFTPAREN,
KpRightParen => KEY_KPRIGHTPAREN,
KpLeftBrace => return None,
KpRightBrace => return None,
KpTab => return None,
KpBackspace => return None,
KpA => return None,
KpB => return None,
KpC => return None,
KpD => return None,
KpE => return None,
KpF => return None,
KpXor => return None,
KpPower => return None,
KpPercent => return None,
KpLess => return None,
KpGreater => return None,
KpAmpersand => return None,
KpDblAmpersand => return None,
KpVerticalBar => return None,
KpDblVerticalBar => return None,
KpColon => return None,
KpHash => return None,
KpSpace => return None,
KpAt => return None,
KpExclam => return None,
KpMemStore => return None,
KpMemRecall => return None,
KpMemClear => return None,
KpMemAdd => return None,
KpMemSubtract => return None,
KpMemMultiply => return None,
KpMemDivide => return None,
KpPlusMinus => KEY_KPPLUSMINUS,
KpClear => return None,
KpClearEntry => return None,
KpBinary => return None,
KpOctal => return None,
KpDecimal => return None,
KpHexadecimal => return None,
LCtrl => KEY_LEFTCTRL,
LShift => KEY_LEFTSHIFT,
LAlt => KEY_LEFTALT,
LGui => KEY_LEFTMETA,
RCtrl => KEY_RIGHTCTRL,
RShift => KEY_RIGHTSHIFT,
RAlt => KEY_RIGHTALT,
RGui => KEY_RIGHTMETA,
Mode => return None,
AudioNext => KEY_NEXTSONG,
AudioPrev => KEY_PREVIOUSSONG,
AudioStop => return None,
AudioPlay => KEY_PLAYPAUSE,
AudioMute => return None,
MediaSelect => return None,
Www => return None,
Mail => KEY_MAIL,
Calculator => KEY_CALC,
Computer => KEY_COMPUTER,
AcSearch => KEY_SEARCH,
AcHome => KEY_HOMEPAGE,
AcBack => KEY_BACK,
AcForward => KEY_FORWARD,
AcStop => return None,
AcRefresh => KEY_REFRESH,
AcBookmarks => KEY_BOOKMARKS,
BrightnessDown => KEY_BRIGHTNESSDOWN,
BrightnessUp => KEY_BRIGHTNESSUP,
DisplaySwitch => KEY_SWITCHVIDEOMODE,
KbdIllumToggle => KEY_KBDILLUMTOGGLE,
KbdIllumDown => KEY_KBDILLUMDOWN,
KbdIllumUp => KEY_KBDILLUMUP,
Eject => KEY_EJECTCD,
Sleep => KEY_SLEEP,
App1 => return None,
App2 => return None,
Num => return None,
})
}

View File

@ -1,57 +0,0 @@
use crate::input::input_event_codes::{ev::*, key::*, rel::*};
use crate::prelude::{CapabilityType, InputCapability, InputEvent};
use crate::scheme::{impl_event_scheme, InputScheme, Scheme};
use crate::utils::EventListener;
#[derive(Default)]
pub struct MockMouse {
listener: EventListener<InputEvent>,
}
impl_event_scheme!(MockMouse, InputEvent);
impl Scheme for MockMouse {
fn name(&self) -> &str {
"mock-mouse-input"
}
}
impl InputScheme for MockMouse {
fn capability(&self, cap_type: CapabilityType) -> InputCapability {
let mut cap = InputCapability::empty();
match cap_type {
CapabilityType::Event => cap.set_all(&[EV_KEY, EV_REL]),
CapabilityType::Key => cap.set_all(&[BTN_LEFT, BTN_RIGHT, BTN_MIDDLE]),
CapabilityType::RelAxis => cap.set_all(&[REL_X, REL_Y, REL_HWHEEL]),
_ => {}
}
cap
}
}
#[derive(Default)]
pub struct MockKeyboard {
listener: EventListener<InputEvent>,
}
impl_event_scheme!(MockKeyboard, InputEvent);
impl Scheme for MockKeyboard {
fn name(&self) -> &str {
"mock-keyboard-input"
}
}
impl InputScheme for MockKeyboard {
fn capability(&self, cap_type: CapabilityType) -> InputCapability {
let mut cap = InputCapability::empty();
match cap_type {
CapabilityType::Event => cap.set(EV_KEY),
CapabilityType::Key => {
// TODO
}
_ => {}
}
cap
}
}

View File

@ -1,9 +0,0 @@
//! Mock devices, including display, input, uart and graphic.
pub mod display;
pub mod input;
pub mod uart;
#[cfg(any(feature = "graphic", doc))]
#[doc(cfg(feature = "graphic"))]
pub mod graphic;

View File

@ -1,110 +0,0 @@
use std::collections::VecDeque;
use async_std::{io, io::prelude::*, task};
use lock::Mutex;
use crate::scheme::{impl_event_scheme, Scheme, UartScheme};
use crate::utils::EventListener;
use crate::DeviceResult;
const UART_BUF_LEN: usize = 256;
lazy_static::lazy_static! {
static ref UART_BUF: Mutex<VecDeque<u8>> = Mutex::new(VecDeque::with_capacity(UART_BUF_LEN));
}
pub struct MockUart {
listener: EventListener,
}
impl_event_scheme!(MockUart);
impl MockUart {
pub fn new() -> Self {
Self {
listener: EventListener::new(),
}
}
pub fn start_irq_service(irq_handler: impl Fn() + Send + Sync + 'static) {
task::spawn(async move {
loop {
let mut buf = [0; UART_BUF_LEN];
let remains = UART_BUF_LEN - UART_BUF.lock().len();
if remains > 0 {
if let Ok(n) = io::stdin().read(&mut buf[..remains]).await {
{
let mut uart_buf = UART_BUF.lock();
for c in &buf[..n] {
uart_buf.push_back(*c);
}
}
irq_handler();
}
}
task::yield_now().await;
}
});
}
}
impl Default for MockUart {
fn default() -> Self {
Self::new()
}
}
impl Scheme for MockUart {
fn name(&self) -> &str {
"mock-uart"
}
fn handle_irq(&self, _irq_num: usize) {
self.listener.trigger(());
}
}
impl UartScheme for MockUart {
fn try_recv(&self) -> DeviceResult<Option<u8>> {
if let Some(c) = UART_BUF.lock().pop_front() {
Ok(Some(c))
} else {
Ok(None)
}
}
fn send(&self, ch: u8) -> DeviceResult {
eprint!("{}", ch as char);
Ok(())
}
fn write_str(&self, s: &str) -> DeviceResult {
eprint!("{}", s);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::Arc;
#[test]
fn test_mock_uart() {
let uart = Arc::new(MockUart::new());
let u = uart.clone();
MockUart::start_irq_service(move || u.handle_irq(0));
uart.write_str("Hello, World!\n").unwrap();
uart.write_str(format!("{} + {} = {}\n", 1, 2, 1 + 2).as_str())
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(100));
if let Some(ch) = uart.try_recv().unwrap() {
uart.write_str(format!("received data: {:?}({:#x})\n", ch as char, ch).as_str())
.unwrap();
} else {
uart.write_str("no data to receive\n").unwrap();
}
}
}

View File

@ -1,60 +0,0 @@
// smoltcp
use smoltcp::{iface::Interface, phy::Loopback, time::Instant};
use crate::net::get_sockets;
use alloc::sync::Arc;
use alloc::string::String;
use lock::Mutex;
use crate::scheme::{NetScheme, Scheme};
use crate::{DeviceError, DeviceResult};
use alloc::vec::Vec;
use smoltcp::wire::EthernetAddress;
use smoltcp::wire::IpCidr;
#[derive(Clone)]
pub struct LoopbackInterface {
pub iface: Arc<Mutex<Interface<'static, Loopback>>>,
pub name: String,
}
impl Scheme for LoopbackInterface {
fn name(&self) -> &str {
"loopback"
}
fn handle_irq(&self, _cause: usize) {}
}
impl NetScheme for LoopbackInterface {
fn recv(&self, _buf: &mut [u8]) -> DeviceResult<usize> {
unimplemented!()
}
fn send(&self, _buf: &[u8]) -> DeviceResult<usize> {
unimplemented!()
}
fn poll(&self) -> DeviceResult {
let timestamp = Instant::from_millis(0);
let sockets = get_sockets();
let mut sockets = sockets.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => Ok(()),
Err(err) => {
debug!("poll got err {}", err);
Err(DeviceError::IoError)
}
}
}
fn get_mac(&self) -> EthernetAddress {
unimplemented!()
}
fn get_ifname(&self) -> String {
unimplemented!()
}
fn get_ip_address(&self) -> Vec<IpCidr> {
unimplemented!()
}
}

View File

@ -1,83 +0,0 @@
//! LAN driver, only for Realtek currently.
pub mod e1000;
cfg_if::cfg_if! {
if #[cfg(target_arch = "riscv64")] {
mod realtek;
mod rtlx;
pub use rtlx::*;
}
}
/*
/// External functions that drivers must use
pub trait Provider {
/// Page size (usually 4K)
const PAGE_SIZE: usize;
/// Allocate consequent physical memory for DMA.
/// Return (`virtual address`, `physical address`).
/// The address is page aligned.
fn alloc_dma(size: usize) -> (usize, usize);
/// Deallocate DMA
fn dealloc_dma(vaddr: usize, size: usize);
}
*/
pub use isomorphic_drivers::provider::Provider;
pub struct ProviderImpl;
impl Provider for ProviderImpl {
const PAGE_SIZE: usize = PAGE_SIZE;
fn alloc_dma(size: usize) -> (usize, usize) {
let paddr = unsafe { drivers_dma_alloc(size / PAGE_SIZE) };
let vaddr = phys_to_virt(paddr);
(vaddr, paddr)
}
fn dealloc_dma(vaddr: usize, size: usize) {
let paddr = virt_to_phys(vaddr);
unsafe { drivers_dma_dealloc(paddr, size / PAGE_SIZE) };
}
}
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
unsafe { drivers_phys_to_virt(paddr) }
}
pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
unsafe { drivers_virt_to_phys(vaddr) }
}
extern "C" {
fn drivers_dma_alloc(pages: usize) -> PhysAddr;
fn drivers_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32;
fn drivers_phys_to_virt(paddr: PhysAddr) -> VirtAddr;
fn drivers_virt_to_phys(vaddr: VirtAddr) -> PhysAddr;
}
pub const PAGE_SIZE: usize = 4096;
type VirtAddr = usize;
type PhysAddr = usize;
pub mod loopback;
pub use loopback::LoopbackInterface;
use alloc::sync::Arc;
use alloc::vec;
use lock::Mutex;
use smoltcp::socket::SocketSet;
lazy_static::lazy_static! {
pub static ref SOCKETS: Arc<Mutex<SocketSet<'static>>> =
Arc::new(Mutex::new(SocketSet::new(vec![])));
}
pub fn get_sockets() -> Arc<Mutex<SocketSet<'static>>> {
SOCKETS.clone()
}

View File

@ -1,28 +0,0 @@
#![allow(unused)]
#![allow(non_camel_case_types)]
use super::Provider;
use super::{phys_to_virt, virt_to_phys};
#[macro_use]
mod log {
macro_rules! trace {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
macro_rules! debug {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
macro_rules! info {
($($arg:expr),*) => { $( let _ = $arg; )*};
}
macro_rules! warn {
($($arg:expr),*) => { $( let _ = $arg; )*};
}
macro_rules! error {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
}
pub mod mii;
pub mod rtl8211f;
mod utils;

View File

@ -1,224 +0,0 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
// use alloc::vec;
use alloc::vec::Vec;
use lock::Mutex;
use smoltcp::iface::*;
use smoltcp::phy::{self, Device, DeviceCapabilities, Medium};
// use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
use smoltcp::wire::*;
use smoltcp::Result;
use super::realtek::rtl8211f;
use super::realtek::rtl8211f::RTL8211F;
use super::ProviderImpl;
use super::PAGE_SIZE;
//use kernel_hal::drivers::{Driver, DeviceType, NetDriver, DRIVERS, NET_DRIVERS, SOCKETS};
use crate::net::get_sockets;
use crate::scheme::{NetScheme, Scheme};
use crate::{DeviceError, DeviceResult};
#[derive(Clone)]
pub struct RTLxDriver(Arc<Mutex<RTL8211F<ProviderImpl>>>);
#[derive(Clone)]
pub struct RTLxInterface {
pub iface: Arc<Mutex<Interface<'static, RTLxDriver>>>,
pub driver: RTLxDriver,
pub name: String,
pub irq: usize,
}
impl Scheme for RTLxInterface {
fn name(&self) -> &str {
"rtl8211f"
}
fn handle_irq(&self, irq: usize) {
if irq != self.irq {
// not ours, skip it
return;
}
let status = self.driver.0.lock().interrupt_status();
let handle_tx_rx = 3;
if status == handle_tx_rx {
let timestamp = Instant::from_millis(0);
let sockets = get_sockets();
let mut sockets = sockets.lock();
self.driver.0.lock().int_disable();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(b) => {
debug!("nic poll, is changed ?: {}", b);
}
Err(err) => {
error!("poll got err {}", err);
}
}
self.driver.0.lock().int_enable();
//return true;
}
}
}
impl NetScheme for RTLxInterface {
fn get_mac(&self) -> EthernetAddress {
self.iface.lock().ethernet_addr()
}
fn get_ifname(&self) -> String {
self.name.clone()
}
fn get_ip_address(&self) -> Vec<IpCidr> {
Vec::from(self.iface.lock().ip_addrs())
}
fn poll(&self) -> DeviceResult {
let timestamp = Instant::from_millis(0);
let sockets = get_sockets();
let mut sockets = sockets.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(b) => {
debug!("nic poll, is changed ?: {}", b);
Ok(())
}
Err(err) => {
error!("poll got err {}", err);
Err(DeviceError::IoError)
}
}
}
fn recv(&self, buf: &mut [u8]) -> DeviceResult<usize> {
if self.driver.0.lock().can_recv() {
let (vec_recv, rxcount) = self.driver.0.lock().geth_recv(1);
buf.copy_from_slice(&vec_recv);
Ok(rxcount as usize)
} else {
Err(DeviceError::NotReady)
}
}
fn send(&self, data: &[u8]) -> DeviceResult<usize> {
if self.driver.0.lock().can_send() {
self.driver.0.lock().geth_send(data).unwrap();
Ok(data.len())
} else {
Err(DeviceError::NotReady)
}
}
}
pub struct RTLxRxToken(Vec<u8>);
pub struct RTLxTxToken(RTLxDriver);
impl<'a> Device<'a> for RTLxDriver {
type RxToken = RTLxRxToken;
type TxToken = RTLxTxToken;
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(64);
caps.medium = Medium::Ethernet;
caps
}
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
if self.0.lock().can_recv() {
//这里每次只接收一个网络包
let (vec_recv, _rxcount) = self.0.lock().geth_recv(1);
Some((RTLxRxToken(vec_recv), RTLxTxToken(self.clone())))
} else {
None
}
}
fn transmit(&mut self) -> Option<Self::TxToken> {
if self.0.lock().can_send() {
Some(RTLxTxToken(self.clone()))
} else {
None
}
}
}
impl phy::RxToken for RTLxRxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
f(&mut self.0)
}
}
impl phy::TxToken for RTLxTxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let mut buffer = [0u8; 1536];
let result = f(&mut buffer[..len]);
if result.is_ok() {
(self.0).0.lock().geth_send(&buffer[..len]).unwrap();
}
result
}
}
pub fn rtlx_init<F: Fn(usize, usize) -> Option<usize>>(
irq: usize,
mapper: F,
) -> DeviceResult<RTLxInterface> {
mapper(rtl8211f::PINCTRL_GPIO_BASE as usize, PAGE_SIZE * 2);
mapper(rtl8211f::SYS_CFG_BASE as usize, PAGE_SIZE * 2);
let mut rtl8211f = RTL8211F::<ProviderImpl>::new(&[0u8; 6]);
let mac = rtl8211f.get_umac();
//启动前请为D1插上网线
warn!("Please plug in the Ethernet cable");
rtl8211f.open().unwrap();
rtl8211f.set_rx_mode();
rtl8211f.adjust_link().unwrap();
let net_driver = RTLxDriver(Arc::new(Mutex::new(rtl8211f)));
let ethernet_addr = EthernetAddress::from_bytes(&mac);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 0, 123), 24)];
let default_gateway = Ipv4Address::new(192, 168, 0, 1);
static mut ROUTES_STORAGE: [Option<(IpCidr, Route)>; 1] = [None; 1];
let mut routes = unsafe { Routes::new(&mut ROUTES_STORAGE[..]) };
routes.add_default_ipv4_route(default_gateway).unwrap();
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let iface = InterfaceBuilder::new(net_driver.clone())
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
info!("rtl8211f interface up with addr 192.168.0.123/24");
info!("rtl8211f interface up with route 192.168.0.1/24");
let rtl8211f_iface = RTLxInterface {
iface: Arc::new(Mutex::new(iface)),
driver: net_driver,
name: String::from("rtl8211f"),
irq,
};
Ok(rtl8211f_iface)
}
//TODO: Global SocketSet
// lazy_static::lazy_static! {
// pub static ref SOCKETS: Mutex<SocketSet<'static>> =
// Mutex::new(SocketSet::new(vec![]));
// }

View File

@ -1,11 +0,0 @@
//! Re-export most commonly used driver types.
pub use crate::scheme::display::{ColorFormat, DisplayInfo, FrameBuffer, Rectangle, RgbColor};
pub use crate::scheme::input::{CapabilityType, InputCapability, InputEvent, InputEventType};
pub use crate::scheme::irq::{IrqHandler, IrqPolarity, IrqTriggerMode};
pub use crate::{Device, DeviceError, DeviceResult};
/// Re-export types from [`input`](crate::input).
pub mod input {
pub use crate::input::{Mouse, MouseFlags, MouseState};
}

View File

@ -1,8 +0,0 @@
use super::Scheme;
use crate::DeviceResult;
pub trait BlockScheme: Scheme {
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> DeviceResult;
fn write_block(&self, block_id: usize, buf: &[u8]) -> DeviceResult;
fn flush(&self) -> DeviceResult;
}

View File

@ -1,218 +0,0 @@
use super::Scheme;
use crate::DeviceResult;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RgbColor(u32);
/// Color format for one pixel. `RGB888` means R in bits 16-23, G in bits 8-15 and B in bits 0-7.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorFormat {
RGB332,
RGB565,
RGB888,
ARGB8888,
}
#[derive(Debug)]
pub struct Rectangle {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
pub struct FrameBuffer<'a> {
raw: &'a mut [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct DisplayInfo {
/// visible width
pub width: u32,
/// visible height
pub height: u32,
/// color encoding format of RGBA
pub format: ColorFormat,
/// frame buffer base virtual address
pub fb_base_vaddr: usize,
/// frame buffer size
pub fb_size: usize,
}
impl RgbColor {
#[inline]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self(((r as u32) << 16) | ((g as u32) << 8) | b as u32)
}
#[inline]
pub const fn r(self) -> u8 {
(self.0 >> 16) as u8
}
#[inline]
pub const fn g(self) -> u8 {
(self.0 >> 8) as u8
}
#[inline]
pub const fn b(self) -> u8 {
self.0 as u8
}
#[inline]
pub const fn raw_value(self) -> u32 {
self.0
}
}
impl ColorFormat {
/// Number of bits per pixel.
#[inline]
pub const fn depth(self) -> u8 {
match self {
Self::RGB332 => 8,
Self::RGB565 => 16,
Self::RGB888 => 24,
Self::ARGB8888 => 32,
}
}
/// Number of bytes per pixel.
#[inline]
pub const fn bytes(self) -> u8 {
self.depth() / 8
}
}
impl<'a> FrameBuffer<'a> {
/// # Safety
///
/// This function is unsafe because it created the `FrameBuffer` structure
/// from the raw pointer.
pub unsafe fn from_raw_parts_mut(ptr: *mut u8, len: usize) -> Self {
Self {
raw: core::slice::from_raw_parts_mut(ptr, len),
}
}
pub fn from_slice(slice: &'a mut [u8]) -> Self {
Self { raw: slice }
}
/// # Safety
///
/// This function is unsafe because the caller must ensure `offset` does
/// not exceed the frame buffer size.
pub unsafe fn write_color(&mut self, offset: usize, color: RgbColor, format: ColorFormat) {
const fn pack_channel(
r_val: u8,
_r_bits: u8,
g_val: u8,
g_bits: u8,
b_val: u8,
b_bits: u8,
) -> u32 {
((r_val as u32) << (g_bits + b_bits)) | ((g_val as u32) << b_bits) | b_val as u32
}
let (r, g, b) = (color.r(), color.g(), color.b());
let ptr = self.raw.as_mut_ptr().add(offset);
let dst = core::slice::from_raw_parts_mut(ptr, 4);
match format {
ColorFormat::RGB332 => {
*ptr = pack_channel(r >> (8 - 3), 3, g >> (8 - 3), 3, b >> (8 - 2), 2) as u8
}
ColorFormat::RGB565 => {
*(ptr as *mut u16) =
pack_channel(r >> (8 - 5), 5, g >> (8 - 6), 6, b >> (8 - 5), 5) as u16
}
ColorFormat::RGB888 => {
dst[2] = r;
dst[1] = g;
dst[0] = b;
}
ColorFormat::ARGB8888 => *(ptr as *mut u32) = color.raw_value(),
}
}
}
impl<'a> core::ops::Deref for FrameBuffer<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.raw
}
}
impl<'a> core::ops::DerefMut for FrameBuffer<'a> {
#[allow(clippy::needless_borrow)]
fn deref_mut(&mut self) -> &mut Self::Target {
self.raw
}
}
impl DisplayInfo {
/// Number of bytes between each row of the frame buffer.
#[inline]
pub const fn pitch(self) -> u32 {
self.width * self.format.bytes() as u32
}
}
pub trait DisplayScheme: Scheme {
fn info(&self) -> DisplayInfo;
/// Returns the framebuffer.
fn fb(&self) -> FrameBuffer;
/// Write pixel color.
#[inline]
fn draw_pixel(&self, x: u32, y: u32, color: RgbColor) {
let info = self.info();
let offset = (x + y * info.width) as usize * info.format.bytes() as usize;
if offset < info.fb_size {
unsafe { self.fb().write_color(offset, color, info.format) };
}
}
/// Fill a given rectangle with `color`.
fn fill_rect(&self, rect: &Rectangle, color: RgbColor) {
let info = self.info();
let left = rect.x.min(info.width);
let right = (left + rect.width).min(info.width);
let top = rect.y.min(info.height);
let bottom = (top + rect.height).min(info.height);
for j in top..bottom {
for i in left..right {
self.draw_pixel(i, j, color);
}
}
}
/// Clear the screen with `color`.
fn clear(&self, color: RgbColor) {
let info = self.info();
self.fill_rect(
&Rectangle {
x: 0,
y: 0,
width: info.width,
height: info.height,
},
color,
)
}
/// Whether need to flush the frambuffer to screen.
#[inline]
fn need_flush(&self) -> bool {
false
}
/// Flush framebuffer to screen.
#[inline]
fn flush(&self) -> DeviceResult {
Ok(())
}
}

View File

@ -1,62 +0,0 @@
use crate::utils::EventHandler;
pub trait EventScheme {
type Event;
/// Trigger the event manually and call its handler immediately.
fn trigger(&self, event: Self::Event);
/// Subscribe events, call the `handler` when an input event occurs.
/// If `once` is ture, unsubscribe automatically after handling.
fn subscribe(&self, handler: EventHandler<Self::Event>, once: bool);
}
macro_rules! impl_event_scheme {
($struct:ident $(, $event_ty:ty)?) => {
impl_event_scheme!(@impl_base $struct $(, $event_ty)?);
};
($struct:ident<'_> $(, $event_ty:ty)?) => {
impl_event_scheme!(@impl_base $struct<'_> $(, $event_ty)?);
};
($struct:ident < $($types:ident),* > $(where $($preds:tt)+)? $(, $event_ty:ty)?) => {
impl_event_scheme!(@impl_base $struct < $($types),* > $(where $($preds)+)? $(, $event_ty)?);
};
(@impl_base $struct:ident $(, $event_ty:ty)?) => {
impl $crate::scheme::EventScheme for $struct {
impl_event_scheme!(@impl_body $(, $event_ty)?);
}
};
(@impl_base $struct:ident<'_> $(, $event_ty:ty)?) => {
impl $crate::scheme::EventScheme for $struct<'_> {
impl_event_scheme!(@impl_body $(, $event_ty)?);
}
};
(@impl_base $struct:ident < $($types:ident),* > $(where $($preds:tt)+)? $(, $event_ty:ty)?) => {
impl < $($types),* > $crate::scheme::EventScheme for $struct < $($types),* >
$(where $($preds)+)?
{
impl_event_scheme!(@impl_body $(, $event_ty)?);
}
};
(@impl_assoc_type) => {
type Event = ();
};
(@impl_assoc_type, $event_ty:ty) => {
type Event = $event_ty;
};
(@impl_body $(, $event_ty:ty)?) => {
impl_event_scheme!(@impl_assoc_type $(, $event_ty)?);
#[inline]
fn trigger(&self, event: Self::Event) {
self.listener.trigger(event);
}
#[inline]
fn subscribe(&self, handler: $crate::utils::EventHandler<Self::Event>, once: bool) {
self.listener.subscribe(handler, once);
}
};
}

View File

@ -1,131 +0,0 @@
use core::fmt;
use super::{event::EventScheme, Scheme};
use crate::input::input_event_codes::ev::*;
numeric_enum_macro::numeric_enum! {
#[repr(u16)]
#[derive(Clone, Copy, Debug)]
/// Linux input event codes.
///
/// Reference: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h>
pub enum InputEventType {
/// Used as markers to separate events. Events may be separated in time or in space,
/// such as with the multitouch protocol.
Syn = EV_SYN,
/// Used to describe state changes of keyboards, buttons, or other key-like devices.
Key = EV_KEY,
/// Used to describe relative axis value changes, e.g. moving the mouse 5 units
/// to the left.
RelAxis = EV_REL,
/// Used to describe absolute axis value changes, e.g. describing the coordinates
/// of a touch on a touchscreen.
AbsAxis = EV_ABS,
/// Used to describe miscellaneous input data that do not fit into other types.
Misc = EV_MSC,
/// Used to describe binary state input switches.
Switch = EV_SW,
/// Used to turn LEDs on devices on and off.
Led = EV_LED,
/// Used to output sound to devices.
Sound = EV_SND,
/// Used for autorepeating devices.
Repeat = EV_REP,
/// Used to send force feedback commands to an input device.
FeedBack = EV_FF,
/// A special type for power button and switch input.
Power = EV_PWR,
/// Used to receive force feedback device status.
FeedBackStatus = EV_FF_STATUS,
}
}
#[derive(Clone, Copy, Debug)]
pub struct InputEvent {
pub event_type: InputEventType,
pub code: u16,
pub value: i32,
}
#[repr(u16)]
#[derive(Clone, Copy, Debug)]
pub enum CapabilityType {
Key = EV_KEY,
RelAxis = EV_REL,
AbsAxis = EV_ABS,
Misc = EV_MSC,
Switch = EV_SW,
Led = EV_LED,
Sound = EV_SND,
FeedBack = EV_FF,
Event,
InputProp,
}
pub struct InputCapability {
/// bitmap to support up to 1024 bits.
bitmap: [u64; 16],
}
impl InputCapability {
pub fn empty() -> Self {
Self { bitmap: [0; 16] }
}
pub fn from_bitmap(bitmap: &[u8]) -> Self {
let mut cap = Self::empty();
let bitcount = bitmap.len() as u16 * 8;
for i in 0..bitcount as usize {
if bitmap[i / 8] & (1 << (i % 64)) != 0 {
cap.set(i as u16);
}
}
cap
}
pub fn set(&mut self, code: u16) {
self.bitmap[code as usize / 64] |= 1 << (code % 64);
}
pub fn set_all(&mut self, codes: &[u16]) {
for &c in codes {
self.set(c);
}
}
pub fn contains(&self, code: u16) -> bool {
self.bitmap[code as usize / 64] & (1 << (code % 64)) != 0
}
pub fn contains_all(&self, codes: &[u16]) -> bool {
for &c in codes {
if !self.contains(c) {
return false;
}
}
true
}
}
impl fmt::Debug for InputCapability {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut skip_empty = true;
write!(f, "[")?;
for i in (0..16).rev() {
if self.bitmap[i] > 0 || !skip_empty {
write!(f, "{:#016x}", self.bitmap[i])?;
if i > 0 {
write!(f, ", ")?;
}
skip_empty = false;
}
}
write!(f, "]")?;
Ok(())
}
}
pub trait InputScheme: Scheme + EventScheme<Event = InputEvent> {
/// Returns the capability bitmap of the specific kind of event.
fn capability(&self, cap_type: CapabilityType) -> InputCapability;
}

View File

@ -1,84 +0,0 @@
use alloc::boxed::Box;
use alloc::sync::Arc;
use core::ops::Range;
use super::Scheme;
use crate::DeviceResult;
/// A type alias for
pub type IrqHandler = Box<dyn Fn() + Send + Sync>;
#[derive(Debug)]
pub enum IrqTriggerMode {
Edge,
Level,
}
#[derive(Debug)]
pub enum IrqPolarity {
ActiveHigh,
ActiveLow,
}
pub trait IrqScheme: Scheme {
/// Is a valid IRQ number.
fn is_valid_irq(&self, irq_num: usize) -> bool;
/// Disable IRQ.
fn mask(&self, irq_num: usize) -> DeviceResult;
/// Enable IRQ.
fn unmask(&self, irq_num: usize) -> DeviceResult;
/// Configure the specified interrupt vector. If it is invoked, it must be
/// invoked prior to interrupt registration.
fn configure(&self, _irq_num: usize, _tm: IrqTriggerMode, _pol: IrqPolarity) -> DeviceResult {
unimplemented!()
}
/// Add an interrupt handler to an IRQ.
fn register_handler(&self, irq_num: usize, handler: IrqHandler) -> DeviceResult;
/// Register the device to delivery an IRQ.
fn register_device(&self, irq_num: usize, dev: Arc<dyn Scheme>) -> DeviceResult {
self.register_handler(irq_num, Box::new(move || dev.handle_irq(irq_num)))
}
/// Remove the interrupt handler to an IRQ.
fn unregister(&self, irq_num: usize) -> DeviceResult;
/// Method used for platform allocation of blocks of MSI and MSI-X compatible
/// IRQ targets.
fn msi_alloc_block(&self, _requested_irqs: usize) -> DeviceResult<Range<usize>> {
unimplemented!()
}
/// Method used to free a block of MSI IRQs previously allocated by msi_alloc_block().
/// This does not unregister IRQ handlers.
fn msi_free_block(&self, _block: Range<usize>) -> DeviceResult {
unimplemented!()
}
/// Register a handler function for a given msi_id within an msi_block_t. Passing a
/// NULL handler will effectively unregister a handler for a given msi_id within the
/// block.
fn msi_register_handler(
&self,
_block: Range<usize>,
_msi_id: usize,
_handler: IrqHandler,
) -> DeviceResult {
unimplemented!()
}
/// Init irq for current cpu.
/// Some IRQ hardware requires per-CPU initialization.
fn init_hart(&self) {
unimplemented!()
}
/// [for x86_64] enable apic timer
fn apic_timer_enable(&self) {
unimplemented!()
}
}

View File

@ -1,55 +0,0 @@
//! The [`Scheme`] describe some functions must be implemented for different type of devices,
//! there are many [`Scheme`] traits in this mod.
//!
//! If you need to develop a new device, just implement the corresponding trait.
//!
//! The [`Scheme`] trait is suitable for any architecture.
pub(super) mod block;
pub(super) mod display;
pub(super) mod input;
pub(super) mod irq;
pub(super) mod net;
pub(super) mod uart;
#[macro_use]
pub(super) mod event;
pub(super) use impl_event_scheme;
use alloc::sync::Arc;
pub use block::BlockScheme;
pub use display::DisplayScheme;
pub use event::EventScheme;
pub use input::InputScheme;
pub use irq::IrqScheme;
pub use net::NetScheme;
pub use uart::UartScheme;
/// Common of all device drivers.
///
/// Every device must says its name and handles interrupts.
pub trait Scheme: SchemeUpcast + Send + Sync {
/// Returns name of the driver.
fn name(&self) -> &str;
/// Handles an interrupt.
fn handle_irq(&self, _irq_num: usize) {}
}
/// Used to convert a concrete type pointer to a general [`Scheme`] pointer.
pub trait SchemeUpcast {
/// Performs the conversion.
fn upcast<'a>(self: Arc<Self>) -> Arc<dyn Scheme + 'a>
where
Self: 'a;
}
impl<T: Scheme + Sized> SchemeUpcast for T {
fn upcast<'a>(self: Arc<Self>) -> Arc<dyn Scheme + 'a>
where
Self: 'a,
{
self
}
}

View File

@ -1,14 +0,0 @@
use super::Scheme;
use crate::DeviceResult;
use alloc::string::String;
use alloc::vec::Vec;
use smoltcp::wire::{EthernetAddress, IpCidr};
pub trait NetScheme: Scheme {
fn recv(&self, buf: &mut [u8]) -> DeviceResult<usize>;
fn send(&self, buf: &[u8]) -> DeviceResult<usize>;
fn get_mac(&self) -> EthernetAddress;
fn get_ifname(&self) -> String;
fn get_ip_address(&self) -> Vec<IpCidr>;
fn poll(&self) -> DeviceResult;
}

View File

@ -1,13 +0,0 @@
use super::{event::EventScheme, Scheme};
use crate::DeviceResult;
pub trait UartScheme: Scheme + EventScheme<Event = ()> {
fn try_recv(&self) -> DeviceResult<Option<u8>>;
fn send(&self, ch: u8) -> DeviceResult;
fn write_str(&self, s: &str) -> DeviceResult {
for c in s.bytes() {
self.send(c)?;
}
Ok(())
}
}

View File

@ -1,63 +0,0 @@
use alloc::{boxed::Box, collections::VecDeque, string::String, sync::Arc};
use lock::Mutex;
use crate::scheme::{impl_event_scheme, Scheme, UartScheme};
use crate::utils::EventListener;
use crate::DeviceResult;
const BUF_CAPACITY: usize = 4096;
pub struct BufferedUart {
inner: Arc<dyn UartScheme>,
buf: Mutex<VecDeque<u8>>,
listener: EventListener,
name: String,
}
impl_event_scheme!(BufferedUart);
impl BufferedUart {
pub fn new(uart: Arc<dyn UartScheme>) -> Arc<Self> {
let ret = Arc::new(Self {
inner: uart.clone(),
name: alloc::format!("{}-buffered", uart.name()),
buf: Mutex::new(VecDeque::with_capacity(BUF_CAPACITY)),
listener: EventListener::new(),
});
let cloned = ret.clone();
uart.subscribe(Box::new(move |_| cloned.handle_irq(0)), false);
ret
}
}
impl Scheme for BufferedUart {
fn name(&self) -> &str {
self.name.as_str()
}
fn handle_irq(&self, _unused: usize) {
while let Some(c) = self.inner.try_recv().unwrap_or(None) {
let mut buf = self.buf.lock();
if buf.len() < BUF_CAPACITY {
let c = if c == b'\r' { b'\n' } else { c };
buf.push_back(c);
}
}
if self.buf.lock().len() > 0 {
self.listener.trigger(());
}
}
}
impl UartScheme for BufferedUart {
fn try_recv(&self) -> DeviceResult<Option<u8>> {
Ok(self.buf.lock().pop_front())
}
fn send(&self, ch: u8) -> DeviceResult {
self.inner.send(ch)
}
fn write_str(&self, s: &str) -> DeviceResult {
self.inner.write_str(s)
}
}

View File

@ -1,10 +0,0 @@
//! Uart device driver.
mod buffered;
mod uart_16550;
pub use buffered::BufferedUart;
pub use uart_16550::Uart16550Mmio;
#[cfg(target_arch = "x86_64")]
pub use uart_16550::Uart16550Pmio;

View File

@ -1,262 +0,0 @@
use core::convert::TryInto;
use core::ops::{BitAnd, BitOr, Not};
use bitflags::bitflags;
use lock::Mutex;
use crate::io::{Io, Mmio, ReadOnly};
use crate::scheme::{impl_event_scheme, Scheme, UartScheme};
use crate::utils::EventListener;
use crate::DeviceResult;
bitflags! {
/// Interrupt enable flags
struct IntEnFlags: u8 {
const RECEIVED = 1;
const SENT = 1 << 1;
const ERRORED = 1 << 2;
const STATUS_CHANGE = 1 << 3;
// 4 to 7 are unused
}
}
bitflags! {
/// Line status flags
struct LineStsFlags: u8 {
const INPUT_FULL = 1;
// 1 to 4 unknown
const OUTPUT_EMPTY = 1 << 5;
// 6 and 7 unknown
}
}
#[repr(C)]
struct Uart16550Inner<T: Io> {
/// Data register, read to receive, write to send
data: T,
/// Interrupt enable
int_en: T,
/// FIFO control
fifo_ctrl: T,
/// Line control
line_ctrl: T,
/// Modem control
modem_ctrl: T,
/// Line status
line_sts: ReadOnly<T>,
/// Modem status
modem_sts: ReadOnly<T>,
}
impl<T: Io> Uart16550Inner<T>
where
T::Value: From<u8> + TryInto<u8>,
{
fn init(&mut self) {
// Disable interrupts
self.int_en.write(0x00.into());
// Enable FIFO, clear TX/RX queues and
// set interrupt watermark at 14 bytes
self.fifo_ctrl.write(0xC7.into());
// Mark data terminal ready, signal request to send
// and enable auxilliary output #2 (used as interrupt line for CPU)
self.modem_ctrl.write(0x0B.into());
// Enable interrupts
self.int_en.write(0x01.into());
}
fn line_sts(&self) -> LineStsFlags {
LineStsFlags::from_bits_truncate(
(self.line_sts.read() & 0xFF.into()).try_into().unwrap_or(0),
)
}
fn try_recv(&mut self) -> DeviceResult<Option<u8>> {
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
Ok(Some(
(self.data.read() & 0xFF.into()).try_into().unwrap_or(0),
))
} else {
Ok(None)
}
}
fn send(&mut self, ch: u8) -> DeviceResult {
while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
self.data.write(ch.into());
Ok(())
}
fn write_str(&mut self, s: &str) -> DeviceResult {
for b in s.bytes() {
match b {
b'\n' => {
self.send(b'\r')?;
self.send(b'\n')?;
}
_ => {
self.send(b)?;
}
}
}
Ok(())
}
}
/// MMIO driver for UART 16550
pub struct Uart16550Mmio<V: 'static>
where
V: Copy + BitAnd<Output = V> + BitOr<Output = V> + Not<Output = V>,
{
inner: Mutex<&'static mut Uart16550Inner<Mmio<V>>>,
listener: EventListener,
}
impl_event_scheme!(Uart16550Mmio<V>
where
V: Copy
+ BitAnd<Output = V>
+ BitOr<Output = V>
+ Not<Output = V>
+ From<u8>
+ TryInto<u8>
+ Send
);
impl<V> Scheme for Uart16550Mmio<V>
where
V: Copy + BitAnd<Output = V> + BitOr<Output = V> + Not<Output = V> + Send,
{
fn name(&self) -> &str {
"uart16550-mmio"
}
fn handle_irq(&self, _irq_num: usize) {
self.listener.trigger(());
}
}
impl<V> UartScheme for Uart16550Mmio<V>
where
V: Copy
+ BitAnd<Output = V>
+ BitOr<Output = V>
+ Not<Output = V>
+ From<u8>
+ TryInto<u8>
+ Send,
{
fn try_recv(&self) -> DeviceResult<Option<u8>> {
self.inner.lock().try_recv()
}
fn send(&self, ch: u8) -> DeviceResult {
self.inner.lock().send(ch)
}
fn write_str(&self, s: &str) -> DeviceResult {
self.inner.lock().write_str(s)
}
}
impl<V> Uart16550Mmio<V>
where
V: Copy
+ BitAnd<Output = V>
+ BitOr<Output = V>
+ Not<Output = V>
+ From<u8>
+ TryInto<u8>
+ Send,
{
unsafe fn new_common(base: usize) -> Self {
let uart: &mut Uart16550Inner<Mmio<V>> = Mmio::<V>::from_base_as(base);
uart.init();
Self {
inner: Mutex::new(uart),
listener: EventListener::new(),
}
}
}
impl Uart16550Mmio<u8> {
/// # Safety
///
/// This function is unsafe because `base_addr` may be an arbitrary address.
pub unsafe fn new(base: usize) -> Self {
Self::new_common(base)
}
}
impl Uart16550Mmio<u32> {
/// # Safety
///
/// This function is unsafe because `base_addr` may be an arbitrary address.
pub unsafe fn new(base: usize) -> Self {
Self::new_common(base)
}
}
#[cfg(target_arch = "x86_64")]
mod pmio {
use super::*;
use crate::io::Pmio;
/// Pmio driver for UART 16550
pub struct Uart16550Pmio {
inner: Mutex<Uart16550Inner<Pmio<u8>>>,
listener: EventListener,
}
impl_event_scheme!(Uart16550Pmio);
impl Scheme for Uart16550Pmio {
fn name(&self) -> &str {
"uart16550-Pmio"
}
fn handle_irq(&self, _irq_num: usize) {
self.listener.trigger(());
}
}
impl UartScheme for Uart16550Pmio {
fn try_recv(&self) -> DeviceResult<Option<u8>> {
self.inner.lock().try_recv()
}
fn send(&self, ch: u8) -> DeviceResult {
self.inner.lock().send(ch)
}
fn write_str(&self, s: &str) -> DeviceResult {
self.inner.lock().write_str(s)
}
}
impl Uart16550Pmio {
/// Construct a `Uart16550Pmio` whose address starts at `base`.
pub fn new(base: u16) -> Self {
let mut uart = Uart16550Inner::<Pmio<u8>> {
data: Pmio::new(base),
int_en: Pmio::new(base + 1),
fifo_ctrl: Pmio::new(base + 2),
line_ctrl: Pmio::new(base + 3),
modem_ctrl: Pmio::new(base + 4),
line_sts: ReadOnly::new(Pmio::new(base + 5)),
modem_sts: ReadOnly::new(Pmio::new(base + 6)),
};
uart.init();
Self {
inner: Mutex::new(uart),
listener: EventListener::new(),
}
}
}
}
#[cfg(target_arch = "x86_64")]
pub use pmio::Uart16550Pmio;

View File

@ -1,158 +0,0 @@
//! Package of [`device_tree`].
use crate::{DeviceError, DeviceResult, PhysAddr, VirtAddr};
use alloc::vec::Vec;
use core::ops::Range;
use device_tree::{DeviceTree as DeviceTreeInner, PropError};
pub use device_tree::{util::StringList, Node};
/// A unified representation of the `interrupts` and `interrupts_extended`
/// properties for any interrupt generating device.
pub type InterruptsProp = Vec<u32>;
/// A wrapper structure of `device_tree::DeviceTree`.
pub struct Devicetree(DeviceTreeInner);
/// Some properties inherited from ancestor nodes.
///
/// About the notion: cell, see <https://elinux.org/Device_Tree_Usage#How_Addressing_Works>.
#[derive(Clone, Copy, Debug, Default)]
pub struct InheritProps {
/// The `#address-cells` property of its parent node.
pub parent_address_cells: u32,
/// The `#size-cells` property of its parent node.
pub parent_size_cells: u32,
/// The `interrupt-parent` property of the node. If don't have, inherit from
/// its parent node.
pub interrupt_parent: u32,
}
impl Devicetree {
/// Load the device tree blob from the given virtual address.
pub fn from(dtb_base_vaddr: VirtAddr) -> DeviceResult<Self> {
match unsafe { DeviceTreeInner::load_from_raw_pointer(dtb_base_vaddr as *const _) } {
Ok(dt) => Ok(Self(dt)),
Err(err) => {
warn!(
"device-tree: failed to load DTB @ {:#x}: {:?}",
dtb_base_vaddr, err
);
Err(DeviceError::InvalidParam)
}
}
}
fn walk_inner<F>(&self, node: &Node, props: InheritProps, device_node_op: &mut F)
where
F: FnMut(&Node, &StringList, &InheritProps),
{
let mut props = props;
if let Ok(num) = node.prop_u32("interrupt-parent") {
props.interrupt_parent = num;
}
if let Ok(comp) = node.prop_str_list("compatible") {
device_node_op(node, &comp, &props);
}
props.parent_address_cells = node.prop_u32("#address-cells").unwrap_or(0);
props.parent_size_cells = node.prop_u32("#size-cells").unwrap_or(0);
// DFS
for child in node.children.iter() {
self.walk_inner(child, props, device_node_op);
}
}
/// Traverse the tree from root by DFS, collect necessary properties, and
/// apply the `device_node_op` to each node.
pub fn walk<F>(&self, device_node_op: &mut F)
where
F: FnMut(&Node, &StringList, &InheritProps),
{
self.walk_inner(&self.0.root, InheritProps::default(), device_node_op)
}
/// Returns the `bootargs` property in the `/chosen` node, as the kernel
/// command line.
pub fn bootargs(&self) -> Option<&str> {
self.0.find("/chosen")?.prop_str("bootargs").ok()
}
/// Returns the `timebase-frequency` property in the `/cpus` node, as timer
pub fn timebase_frequency(&self) -> Option<u32> {
self.0.find("/cpus")?.prop_u32("timebase-frequency").ok()
}
/// Returns the `linux,initrd-start` and `linux,initrd-end` properties in
/// the `/chosen` node, as the init RAM disk address region.
pub fn initrd_region(&self) -> Option<Range<PhysAddr>> {
let chosen = self.0.find("/chosen")?;
let start = chosen.prop_u32("linux,initrd-start").ok()? as _;
let end = chosen.prop_u32("linux,initrd-end").ok()? as _;
Some(start..end)
}
/// Returns the physical memory regions specified in the `/memory` nodes.
pub fn memory_regions(&self) -> DeviceResult<Vec<Range<PhysAddr>>> {
let props = InheritProps {
parent_address_cells: self.0.root.prop_u32("#address-cells").unwrap_or(0),
parent_size_cells: self.0.root.prop_u32("#size-cells").unwrap_or(0),
..Default::default()
};
let mut regions = Vec::new();
for node in &self.0.root.children {
if node.name.starts_with("memory@")
|| node.prop_str("device_type").unwrap_or_default() == "memory"
{
let (addr, size) = parse_reg(node, &props)?;
regions.push(addr as usize..addr as usize + size as usize)
}
}
Ok(regions)
}
}
/// Combine `cell_num` of 32-bit integers from `cells` into a 64-bit integer.
fn from_cells(cells: &[u32], cell_num: u32) -> DeviceResult<u64> {
if cell_num as usize > cells.len() {
return Err(DeviceError::InvalidParam);
}
let mut value = 0;
for &c in &cells[..cell_num as usize] {
value = value << 32 | c as u64;
}
Ok(value)
}
/// Parse the `reg` property, about `reg`: <https://elinux.org/Device_Tree_Usage#How_Addressing_Works>.
pub fn parse_reg(node: &Node, props: &InheritProps) -> DeviceResult<(u64, u64)> {
let cells = node.prop_cells("reg")?;
let addr = from_cells(&cells, props.parent_address_cells)?;
let size = from_cells(
&cells[props.parent_address_cells as usize..],
props.parent_size_cells,
)?;
Ok((addr, size))
}
/// Returns a `Vec<u32>` according to the `interrupts` or `interrupts-extended`
/// property, the first element is the interrupt parent.
pub fn parse_interrupts(node: &Node, props: &InheritProps) -> DeviceResult<InterruptsProp> {
if node.has_prop("interrupts-extended") {
Ok(node.prop_cells("interrupts-extended")?)
} else if node.has_prop("interrupts") && props.interrupt_parent > 0 {
let mut ret = node.prop_cells("interrupts")?;
ret.insert(0, props.interrupt_parent);
Ok(ret)
} else {
Ok(Vec::new())
}
}
impl From<PropError> for DeviceError {
fn from(_err: PropError) -> Self {
Self::InvalidParam
}
}

View File

@ -1,45 +0,0 @@
use alloc::{boxed::Box, vec::Vec};
use lock::Mutex;
/// A type alias for the closure to handle device event.
pub type EventHandler<T = ()> = Box<dyn Fn(&T) + Send + Sync>;
/// Device event listener.
///
/// It keeps a series of [`EventHandler`]s that handle events of one single type.
pub struct EventListener<T = ()> {
events: Mutex<Vec<(EventHandler<T>, bool)>>,
}
impl<T> EventListener<T> {
/// Construct a new, empty `EventListener`.
pub fn new() -> Self {
Self {
events: Mutex::new(Vec::new()),
}
}
/// Register a new `handler` into this `EventListener`.
///
/// If `once` is `true`, the `handler` will be removed once it handles an event.
pub fn subscribe(&self, handler: EventHandler<T>, once: bool) {
self.events.lock().push((handler, once));
}
/// Send an event to the `EventListener`.
///
/// All the handlers handle the event, and those marked `once` will be removed immediately.
pub fn trigger(&self, event: T) {
self.events.lock().retain(|(f, once)| {
f(&event);
!once
});
}
}
impl<T> Default for EventListener<T> {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,58 +0,0 @@
use alloc::sync::Arc;
use core::convert::Infallible;
use core::ops::{Deref, DerefMut};
use rcore_console::{Console, ConsoleOnGraphic, DrawTarget, OriginDimensions, Pixel, Rgb888, Size};
use crate::scheme::DisplayScheme;
pub struct DisplayWrapper(Arc<dyn DisplayScheme>);
pub struct GraphicConsole {
inner: ConsoleOnGraphic<DisplayWrapper>,
}
impl GraphicConsole {
pub fn new(display: Arc<dyn DisplayScheme>) -> Self {
Self {
inner: Console::on_frame_buffer(DisplayWrapper(display)),
}
}
}
impl DrawTarget for DisplayWrapper {
type Color = Rgb888;
type Error = Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for p in pixels {
let color = unsafe { core::mem::transmute(p.1) };
self.0.draw_pixel(p.0.x as u32, p.0.y as u32, color);
}
Ok(())
}
}
impl OriginDimensions for DisplayWrapper {
fn size(&self) -> Size {
let info = self.0.info();
Size::new(info.width, info.height)
}
}
impl Deref for GraphicConsole {
type Target = ConsoleOnGraphic<DisplayWrapper>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for GraphicConsole {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

View File

@ -1,105 +0,0 @@
use alloc::boxed::Box;
use core::ops::{Deref, DerefMut, Range};
use bitmap_allocator::{BitAlloc, BitAlloc16, BitAlloc256, BitAlloc4K, BitAlloc64K};
use crate::{DeviceError, DeviceResult};
pub trait IdAllocatorWrapper: Send + Sync {
fn new(range: Range<usize>) -> Self
where
Self: Sized;
fn alloc(&mut self) -> DeviceResult<usize>;
fn alloc_fixed(&mut self, id: usize) -> DeviceResult;
fn alloc_contiguous(&mut self, count: usize, align_log2: usize) -> DeviceResult<usize>;
fn free(&mut self, start_id: usize, count: usize) -> DeviceResult;
fn is_alloced(&self, id: usize) -> bool;
}
pub struct IdAllocator(Box<dyn IdAllocatorWrapper>);
impl IdAllocator {
pub fn new(range: Range<usize>) -> DeviceResult<Self> {
Ok(match range.end {
0..=0x10 => Self(Box::new(IdAllocator16::new(range))),
0x11..=0x100 => Self(Box::new(IdAllocator256::new(range))),
0x101..=0x1000 => Self(Box::new(IdAllocator4K::new(range))),
0x1001..=0x10000 => Self(Box::new(IdAllocator64K::new(range))),
_ => {
warn!("out of range in IdAllocator::new(): {:#x?}", range);
return Err(DeviceError::InvalidParam);
}
})
}
}
impl Deref for IdAllocator {
type Target = Box<dyn IdAllocatorWrapper>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for IdAllocator {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
macro_rules! define_allocator {
($name: ident, $inner: ty) => {
struct $name($inner);
impl IdAllocatorWrapper for $name {
fn new(range: Range<usize>) -> Self {
let mut inner = <$inner>::DEFAULT;
inner.insert(range);
Self(inner)
}
fn alloc(&mut self) -> DeviceResult<usize> {
self.0.alloc().ok_or(DeviceError::NoResources)
}
fn alloc_fixed(&mut self, id: usize) -> DeviceResult {
if self.0.test(id) {
self.0.remove(id..id + 1);
Ok(())
} else {
Err(DeviceError::AlreadyExists)
}
}
fn alloc_contiguous(&mut self, count: usize, align_log2: usize) -> DeviceResult<usize> {
self.0
.alloc_contiguous(count, align_log2)
.ok_or(DeviceError::InvalidParam)
}
fn free(&mut self, start_id: usize, count: usize) -> DeviceResult {
if count == 0 {
Err(DeviceError::InvalidParam)
} else if count == 1 {
if !self.is_alloced(start_id) {
Err(DeviceError::InvalidParam)
} else {
self.0.dealloc(start_id);
Ok(())
}
} else {
self.0.insert(start_id..start_id + count);
Ok(())
}
}
fn is_alloced(&self, id: usize) -> bool {
!self.0.test(id)
}
}
};
}
define_allocator!(IdAllocator16, BitAlloc16);
define_allocator!(IdAllocator256, BitAlloc256);
define_allocator!(IdAllocator4K, BitAlloc4K);
define_allocator!(IdAllocator64K, BitAlloc64K);

View File

@ -1,84 +0,0 @@
#![allow(dead_code)]
use core::ops::Range;
use super::IdAllocator;
use crate::{prelude::IrqHandler, DeviceError, DeviceResult};
pub struct IrqManager<const IRQ_COUNT: usize> {
irq_range: Range<usize>,
table: [Option<IrqHandler>; IRQ_COUNT],
allocator: IdAllocator,
}
impl<const IRQ_COUNT: usize> IrqManager<IRQ_COUNT> {
pub fn new(irq_range: Range<usize>) -> Self {
assert!(irq_range.end <= IRQ_COUNT);
const EMPTY_HANDLER: Option<IrqHandler> = None;
let allocator = IdAllocator::new(irq_range.clone()).unwrap();
Self {
irq_range,
table: [EMPTY_HANDLER; IRQ_COUNT],
allocator,
}
}
pub fn alloc_block(&mut self, count: usize) -> DeviceResult<usize> {
info!("IRQ alloc_block {}", count);
debug_assert!(count.is_power_of_two());
let align_log2 = 31 - (count as u32).leading_zeros();
self.allocator.alloc_contiguous(count, align_log2 as _)
}
pub fn free_block(&mut self, start: usize, count: usize) -> DeviceResult {
info!("IRQ free_block {:#x?}", start..start + count);
self.allocator.free(start, count)
}
/// Add a handler to IRQ table. if `irq_num == 0`, we need to allocate one.
/// Returns the specified IRQ number or an allocated IRQ on success.
pub fn register_handler(&mut self, irq_num: usize, handler: IrqHandler) -> DeviceResult<usize> {
info!("IRQ register handler {}", irq_num);
let irq_num = if irq_num == 0 {
// allocate a valid IRQ number
self.allocator.alloc()?
} else if self.irq_range.contains(&irq_num) {
self.allocator.alloc_fixed(irq_num)?;
irq_num
} else {
return Err(DeviceError::InvalidParam);
};
self.table[irq_num] = Some(handler);
Ok(irq_num)
}
pub fn unregister_handler(&mut self, irq_num: usize) -> DeviceResult {
info!("IRQ unregister handler {}", irq_num);
if !self.allocator.is_alloced(irq_num) {
Err(DeviceError::InvalidParam)
} else {
self.allocator.free(irq_num, 1)?;
self.table[irq_num] = None;
Ok(())
}
}
pub fn overwrite_handler(&mut self, irq_num: usize, handler: IrqHandler) -> DeviceResult {
info!("IRQ overwrite handle {}", irq_num);
if !self.allocator.is_alloced(irq_num) {
Err(DeviceError::InvalidParam)
} else {
self.table[irq_num] = Some(handler);
Ok(())
}
}
pub fn handle(&self, irq_num: usize) -> DeviceResult {
if let Some(f) = &self.table[irq_num] {
f();
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
}
}

View File

@ -1,20 +0,0 @@
//! Event handler and device tree.
#![allow(unused_imports)]
mod event_listener;
mod id_allocator;
mod irq_manager;
#[cfg(feature = "graphic")]
mod graphic_console;
pub mod devicetree;
pub(super) use id_allocator::IdAllocator;
pub(super) use irq_manager::IrqManager;
pub use event_listener::{EventHandler, EventListener};
#[cfg(feature = "graphic")]
pub use graphic_console::GraphicConsole;

View File

@ -1,43 +0,0 @@
use lock::Mutex;
use virtio_drivers::{VirtIOBlk as InnerDriver, VirtIOHeader};
use crate::scheme::{BlockScheme, Scheme};
use crate::DeviceResult;
pub struct VirtIoBlk<'a> {
inner: Mutex<InnerDriver<'a>>,
}
impl<'a> VirtIoBlk<'a> {
pub fn new(header: &'static mut VirtIOHeader) -> DeviceResult<Self> {
Ok(Self {
inner: Mutex::new(InnerDriver::new(header)?),
})
}
}
impl<'a> Scheme for VirtIoBlk<'a> {
fn name(&self) -> &str {
"virtio-blk"
}
fn handle_irq(&self, _irq_num: usize) {
self.inner.lock().ack_interrupt();
}
}
impl<'a> BlockScheme for VirtIoBlk<'a> {
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> DeviceResult {
self.inner.lock().read_block(block_id, buf)?;
Ok(())
}
fn write_block(&self, block_id: usize, buf: &[u8]) -> DeviceResult {
self.inner.lock().write_block(block_id, buf)?;
Ok(())
}
fn flush(&self) -> DeviceResult {
Ok(())
}
}

View File

@ -1,55 +0,0 @@
use core::fmt::{Result, Write};
use lock::Mutex;
use virtio_drivers::{VirtIOConsole as InnerDriver, VirtIOHeader};
use crate::prelude::DeviceResult;
use crate::scheme::{impl_event_scheme, Scheme, UartScheme};
use crate::utils::EventListener;
pub struct VirtIoConsole<'a> {
inner: Mutex<InnerDriver<'a>>,
listener: EventListener,
}
impl_event_scheme!(VirtIoConsole<'_>);
impl<'a> VirtIoConsole<'a> {
pub fn new(header: &'static mut VirtIOHeader) -> DeviceResult<Self> {
Ok(Self {
inner: Mutex::new(InnerDriver::new(header)?),
listener: EventListener::new(),
})
}
}
impl<'a> Scheme for VirtIoConsole<'a> {
fn name(&self) -> &str {
"virtio-console"
}
fn handle_irq(&self, _irq_num: usize) {
self.inner.lock().ack_interrupt().unwrap();
self.listener.trigger(());
}
}
impl<'a> UartScheme for VirtIoConsole<'a> {
fn try_recv(&self) -> DeviceResult<Option<u8>> {
Ok(self.inner.lock().recv(true)?)
}
fn send(&self, ch: u8) -> DeviceResult {
self.inner.lock().send(ch)?;
Ok(())
}
}
impl<'a> Write for VirtIoConsole<'a> {
fn write_str(&mut self, s: &str) -> Result {
for b in s.bytes() {
self.send(b).unwrap()
}
Ok(())
}
}

View File

@ -1,77 +0,0 @@
use lock::Mutex;
use virtio_drivers::{VirtIOGpu as InnerDriver, VirtIOHeader};
use crate::prelude::{ColorFormat, DisplayInfo, FrameBuffer};
use crate::scheme::{DisplayScheme, Scheme};
use crate::DeviceResult;
pub struct VirtIoGpu<'a> {
info: DisplayInfo,
inner: Mutex<InnerDriver<'a>>,
}
const CURSOR_HOT_X: u32 = 13;
const CURSOR_HOT_Y: u32 = 11;
static CURSOR_IMG: &[u8] = include_bytes!("../display/resource/cursor.bin"); // 64 x 64 x 4
impl<'a> VirtIoGpu<'a> {
pub fn new(header: &'static mut VirtIOHeader) -> DeviceResult<Self> {
let mut gpu = InnerDriver::new(header)?;
let fb = gpu.setup_framebuffer()?;
let fb_base_vaddr = fb.as_ptr() as usize;
let fb_size = fb.len();
let (width, height) = gpu.resolution();
let info = DisplayInfo {
width,
height,
format: ColorFormat::ARGB8888,
fb_base_vaddr,
fb_size,
};
gpu.setup_cursor(
CURSOR_IMG,
width / 2,
height / 2,
CURSOR_HOT_X,
CURSOR_HOT_Y,
)?;
Ok(Self {
info,
inner: Mutex::new(gpu),
})
}
}
impl<'a> Scheme for VirtIoGpu<'a> {
fn name(&self) -> &str {
"virtio-gpu"
}
fn handle_irq(&self, _irq_num: usize) {
self.inner.lock().ack_interrupt();
}
}
impl<'a> DisplayScheme for VirtIoGpu<'a> {
#[inline]
fn info(&self) -> DisplayInfo {
self.info
}
#[inline]
fn fb(&self) -> FrameBuffer {
unsafe {
FrameBuffer::from_raw_parts_mut(self.info.fb_base_vaddr as *mut u8, self.info.fb_size)
}
}
#[inline]
fn need_flush(&self) -> bool {
true
}
fn flush(&self) -> DeviceResult {
self.inner.lock().flush()?;
Ok(())
}
}

View File

@ -1,78 +0,0 @@
use core::convert::TryFrom;
use lock::Mutex;
use virtio_drivers::{InputConfigSelect, VirtIOHeader, VirtIOInput as InnerDriver};
use crate::prelude::{CapabilityType, InputCapability, InputEvent, InputEventType};
use crate::scheme::{impl_event_scheme, InputScheme, Scheme};
use crate::utils::EventListener;
use crate::DeviceResult;
pub struct VirtIoInput<'a> {
inner: Mutex<InnerDriver<'a>>,
listener: EventListener<InputEvent>,
}
impl<'a> VirtIoInput<'a> {
pub fn new(header: &'static mut VirtIOHeader) -> DeviceResult<Self> {
let inner = Mutex::new(InnerDriver::new(header)?);
Ok(Self {
inner,
listener: EventListener::new(),
})
}
}
impl_event_scheme!(VirtIoInput<'_>, InputEvent);
impl<'a> Scheme for VirtIoInput<'a> {
fn name(&self) -> &str {
"virtio-input"
}
fn handle_irq(&self, _irq_num: usize) {
let mut inner = self.inner.lock();
inner.ack_interrupt();
while let Some(e) = inner.pop_pending_event() {
if let Ok(event_type) = InputEventType::try_from(e.event_type) {
self.listener.trigger(InputEvent {
event_type,
code: e.code,
value: e.value as i32,
});
}
}
}
}
impl<'a> InputScheme for VirtIoInput<'a> {
fn capability(&self, cap_type: CapabilityType) -> InputCapability {
let mut inner = self.inner.lock();
let mut bitmap = [0u8; 128];
match cap_type {
CapabilityType::InputProp => {
let size = inner.query_config_select(InputConfigSelect::PropBits, 0, &mut bitmap);
InputCapability::from_bitmap(&bitmap[..size as usize])
}
CapabilityType::Event => {
let mut cap = InputCapability::empty();
for i in 0..crate::input::input_event_codes::ev::EV_CNT {
let size =
inner.query_config_select(InputConfigSelect::EvBits, i as u8, &mut bitmap);
if size > 0 {
cap.set(i);
}
}
cap
}
_ => {
let size = inner.query_config_select(
InputConfigSelect::EvBits,
cap_type as u8,
&mut bitmap,
);
InputCapability::from_bitmap(&bitmap[..size as usize])
}
}
}
}

View File

@ -1,28 +0,0 @@
//! Packaging of [`virtio-drivers` library](https://github.com/rcore-os/virtio-drivers).
mod blk;
mod console;
mod gpu;
mod input;
pub use blk::VirtIoBlk;
pub use console::VirtIoConsole;
pub use gpu::VirtIoGpu;
pub use input::VirtIoInput;
use crate::DeviceError;
use core::convert::From;
use virtio_drivers::Error;
impl From<Error> for DeviceError {
fn from(err: Error) -> Self {
match err {
Error::BufferTooSmall => Self::BufferTooSmall,
Error::NotReady => Self::NotReady,
Error::InvalidParam => Self::InvalidParam,
Error::DmaError => Self::DmaError,
Error::AlreadyUsed => Self::AlreadyExists,
Error::IoError => Self::IoError,
}
}
}

View File

@ -0,0 +1,49 @@
[package]
name = "kernel-hal-bare"
version = "0.1.0"
authors = ["Runji Wang <wangrunji0408@163.com>"]
edition = "2018"
description = "Kernel HAL implementation for bare metal environment."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
board_qemu = []
board_d1 = []
loopback = []
rtl8x = []
[dependencies]
log = "0.4"
spin = "0.7"
git-version = "0.3"
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" }
trapframe = "0.8.0"
kernel-hal = { path = "../kernel-hal" }
naive-timer = "0.1.0"
lazy_static = { version = "1.4", features = ["spin_no_std"] }
uart_16550 = "=0.2.15"
rcore-fs = { git = "https://github.com/rcore-os/rcore-fs", rev = "6df6cd2" }
device_tree = { git = "https://github.com/rcore-os/device_tree-rs" }
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "568276" }
isomorphic_drivers = { git = "https://github.com/rcore-os/isomorphic_drivers", rev = "fcf694d2", features = ["log"] }
#isomorphic_drivers = { path = "/home/xly/rust/arch-lib/isomorphic_drivers", features = ["log"] }
smoltcp = { git = "https://gitee.com/gcyyfun/smoltcp", rev="043eb60", default-features = false, features = ["alloc","log", "async", "medium-ethernet","proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp", "socket-raw"] }
[target.'cfg(target_arch = "x86_64")'.dependencies]
x86_64 = "0.14"
raw-cpuid = "9.0"
pc-keyboard = "0.5"
apic = { git = "https://github.com/rcore-os/apic-rs", rev = "fb86bd7" }
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" }
rcore-console = { git = "https://github.com/rcore-os/rcore-console", default-features = false, rev = "a980897b" }
acpi = "1.1"
ps2-mouse = { git = "https://github.com/YXL76/ps2-mouse", branch = "feat" }
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { git = "https://github.com/rcore-os/riscv", features = [
"inline-asm",
], rev = "0074cbc" }
# 注意rev版本号必须与其他组件的完全一致不可多字符
bitflags = "1.0"
volatile = "0.2"

View File

@ -0,0 +1,9 @@
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
mod riscv;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
pub use self::riscv::*;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;

View File

@ -0,0 +1,53 @@
// RISCV
// Linear mapping
#[cfg(feature = "board_qemu")]
pub const KERNEL_OFFSET: usize = 0xFFFF_FFFF_8000_0000;
#[cfg(feature = "board_qemu")]
pub const MEMORY_OFFSET: usize = 0x8000_0000;
#[cfg(feature = "board_qemu")]
pub const MEMORY_END: usize = 0x8800_0000; // TODO: get memory end from device tree
#[cfg(feature = "board_d1")]
pub const KERNEL_OFFSET: usize = 0xFFFFFFFF_C0000000;
#[cfg(feature = "board_d1")]
pub const MEMORY_OFFSET: usize = 0x40000000;
#[cfg(feature = "board_d1")]
pub const MEMORY_END: usize = 0x60000000; // 512M
pub const PHYSICAL_MEMORY_OFFSET: usize = KERNEL_OFFSET - MEMORY_OFFSET;
pub const MAX_DTB_SIZE: usize = 0x2000;
pub const MMIO_MTIMECMP0: *mut u64 = 0x0200_4000usize as *mut u64;
pub const MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;
#[cfg(feature = "board_qemu")]
pub const UART_BASE: usize = 0x10000000;
#[cfg(feature = "board_qemu")]
pub const UART0_INT_NUM: u32 = 10;
#[cfg(feature = "board_qemu")]
pub const PLIC_PRIORITY: usize = 0x0c000000;
#[cfg(feature = "board_qemu")]
pub const PLIC_PENDING: usize = 0x0c001000;
#[cfg(feature = "board_qemu")]
pub const PLIC_INT_ENABLE: usize = 0x0c002080;
#[cfg(feature = "board_qemu")]
pub const PLIC_THRESHOLD: usize = 0x0c201000;
#[cfg(feature = "board_qemu")]
pub const PLIC_CLAIM: usize = 0x0c201004;
#[cfg(feature = "board_d1")]
pub const UART_BASE: usize = 0x02500000;
#[cfg(feature = "board_d1")]
pub const UART0_INT_NUM: u32 = 18;
#[cfg(feature = "board_d1")]
pub const PLIC_PRIORITY: usize = 0x1000_0000;
#[cfg(feature = "board_d1")]
pub const PLIC_PENDING: usize = 0x1000_1000;
#[cfg(feature = "board_d1")]
pub const PLIC_INT_ENABLE: usize = 0x1000_2080;
#[cfg(feature = "board_d1")]
pub const PLIC_THRESHOLD: usize = 0x1020_1000;
#[cfg(feature = "board_d1")]
pub const PLIC_CLAIM: usize = 0x1020_1004;

View File

@ -1,10 +1,4 @@
// c906
use core::arch::asm;
const FREQUENCY: u64 = 24_000_000; // C906: 24_000_000, Qemu: 10_000_000
const MMIO_MTIMECMP0: *mut u64 = 0x0200_4000usize as *mut u64;
const MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;
const L1_CACHE_BYTES: u64 = 64;
const CACHE_LINE_SIZE: u64 = 64;
@ -64,28 +58,6 @@ pub fn invalidate_dcache_range(start: u64, end: u64) {
pub fn fence_w() {
unsafe {
//llvm_asm!("fence ow, ow" ::: "memory");
asm!("fence ow, ow");
llvm_asm!("fence ow, ow" ::: "memory");
}
}
pub fn get_cycle() -> u64 {
unsafe { MMIO_MTIME.read_volatile() }
}
// Timer, Freq = 24000000Hz
// TIMER_CLOCK = (24 * 1000 * 1000)
// 微秒(us)
pub fn usdelay(us: u64) {
let mut t1: u64 = get_cycle();
let t2 = t1 + us * 24;
while t2 >= t1 {
t1 = get_cycle();
}
}
// 毫秒(ms)
pub fn msdelay(ms: u64) {
usdelay(ms * 1000);
}

View File

@ -0,0 +1,377 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use riscv::register::{
satp,
scause::{self, Exception, Interrupt, Trap},
sie, sstatus, stval,
};
use spin::Mutex;
use trapframe::{TrapFrame, UserContext};
use super::consts::{PHYSICAL_MEMORY_OFFSET, UART0_INT_NUM, UART_BASE};
use super::{plic, sbi, timer_set_next, uart};
use crate::drivers::IRQ_MANAGER;
use crate::{map_range, phys_to_virt, putfmt};
const TABLE_SIZE: usize = 256;
pub type InterruptHandle = Box<dyn Fn() + Send + Sync>;
lazy_static! {
static ref IRQ_TABLE: Mutex<Vec<Option<InterruptHandle>>> = Default::default();
}
fn init_irq() {
init_irq_table();
irq_add_handle(Timer, Box::new(super_timer));
//模拟参照了x86_64,把timer处理函数也放进去了
//irq_add_handle(Keyboard, Box::new(keyboard));
irq_add_handle(S_PLIC, Box::new(external));
}
pub fn init() {
unsafe {
sstatus::set_sie();
//init_uart();
sie::set_sext();
//init_ext();
}
init_irq();
bare_println!("+++ setup interrupt +++");
}
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
let sepc = tf.sepc;
let scause = scause::read();
let stval = stval::read();
let is_int = scause.bits() >> 63;
let code = scause.bits() & !(1 << 63);
match scause.cause() {
Trap::Exception(Exception::Breakpoint) => breakpoint(&mut tf.sepc),
Trap::Exception(Exception::IllegalInstruction) => {
panic!("IllegalInstruction: {:#x}->{:#x}", sepc, stval)
}
Trap::Exception(Exception::LoadFault) => {
panic!("Load access fault: {:#x}->{:#x}", sepc, stval)
}
Trap::Exception(Exception::StoreFault) => {
panic!("Store access fault: {:#x}->{:#x}", sepc, stval)
}
Trap::Exception(Exception::LoadPageFault) => page_fault(stval, tf),
Trap::Exception(Exception::StorePageFault) => page_fault(stval, tf),
Trap::Exception(Exception::InstructionPageFault) => page_fault(stval, tf),
Trap::Interrupt(Interrupt::SupervisorTimer) => super_timer(),
Trap::Interrupt(Interrupt::SupervisorSoft) => super_soft(),
Trap::Interrupt(Interrupt::SupervisorExternal) => external(),
//Trap::Interrupt(Interrupt::SupervisorExternal) => irq_handle(code as u8),
_ => panic!("Undefined Trap: {:#x} {:#x}", is_int, code),
}
}
fn init_irq_table() {
let mut table = IRQ_TABLE.lock();
for _ in 0..TABLE_SIZE {
table.push(None);
}
}
// Drivers IrqManager
pub fn enable_irq(irq: usize) {
// Handled in PLIC driver
}
fn external() {
IRQ_MANAGER
.read()
.try_handle_interrupt(Some(SupervisorExternal));
}
#[export_name = "hal_irq_handle"]
pub fn irq_handle(irq: u8) {
trace!("hal_irq_handle: {:#x}", irq);
let table = IRQ_TABLE.lock();
match &table[irq as usize] {
Some(f) => f(),
None => panic!("unhandled U-mode external IRQ number: {}", irq),
}
}
/// Add a handle to IRQ table. Return the specified irq or an allocated irq on success
#[export_name = "hal_irq_add_handle"]
pub fn irq_add_handle(irq: u8, handle: InterruptHandle) -> Option<u8> {
info!("IRQ add handle {:#x?}", irq);
let mut table = IRQ_TABLE.lock();
// allocate a valid irq number
// why?
if irq == 0 {
let mut id = 0x20;
while id < table.len() {
if table[id].is_none() {
table[id] = Some(handle);
return Some(id as u8);
}
id += 1;
}
return None;
}
match table[irq as usize] {
Some(_) => None,
None => {
table[irq as usize] = Some(handle);
Some(irq)
}
}
}
#[export_name = "hal_irq_remove_handle"]
pub fn irq_remove_handle(irq: u8) -> bool {
info!("IRQ remove handle {:#x?}", irq);
let irq = irq as usize;
let mut table = IRQ_TABLE.lock();
match table[irq] {
Some(_) => {
table[irq] = None;
false
}
None => true,
}
}
/*
#[export_name = "hal_irq_allocate_block"]
pub fn allocate_block(irq_num: u32) -> Option<(usize, usize)> {
info!("hal_irq_allocate_block: count={:#x?}", irq_num);
let irq_num = u32::next_power_of_two(irq_num) as usize;
let mut irq_start = 0x20;
let mut irq_cur = irq_start;
let mut table = IRQ_TABLE.lock();
while irq_cur < TABLE_SIZE && irq_cur < irq_start + irq_num {
if table[irq_cur].is_none() {
irq_cur += 1;
} else {
irq_start = (irq_cur - irq_cur % irq_num) + irq_num;
irq_cur = irq_start;
}
}
for i in irq_start..irq_start + irq_num {
table[i] = Some(Box::new(|| {}));
}
info!(
"hal_irq_allocate_block: start={:#x?} num={:#x?}",
irq_start, irq_num
);
Some((irq_start, irq_num))
}
#[export_name = "hal_irq_free_block"]
pub fn free_block(irq_start: u32, irq_num: u32) {
let mut table = IRQ_TABLE.lock();
for i in irq_start..irq_start + irq_num {
table[i as usize] = None;
}
}
*/
#[export_name = "hal_irq_overwrite_handler"]
pub fn overwrite_handler(msi_id: u32, handle: Box<dyn Fn() + Send + Sync>) -> bool {
info!("IRQ overwrite handle {:#x?}", msi_id);
let mut table = IRQ_TABLE.lock();
let set = table[msi_id as usize].is_none();
table[msi_id as usize] = Some(handle);
set
}
fn breakpoint(sepc: &mut usize) {
bare_println!("Exception::Breakpoint: A breakpoint set @0x{:x} ", sepc);
//sepc为触发中断指令ebreak的地址
//防止无限循环中断让sret返回时跳转到sepc的下一条指令地址
*sepc += 2
}
fn page_fault(stval: usize, tf: &mut TrapFrame) {
let this_scause = scause::read();
info!(
"EXCEPTION Page Fault: {:?} @ {:#x}->{:#x}",
this_scause.cause(),
tf.sepc,
stval
);
let vaddr = stval;
use crate::PageTableImpl;
use kernel_hal::{MMUFlags, PageTableTrait};
use riscv::addr::{Page, PhysAddr, VirtAddr};
use riscv::paging::{PageTableFlags as PTF, Rv39PageTable, *};
//let mut flags = PTF::VALID;
let code = this_scause.code();
let mut flags = if code == 15 {
//MMUFlags::WRITE ???
MMUFlags::READ | MMUFlags::WRITE
} else if code == 12 {
MMUFlags::EXECUTE
} else {
MMUFlags::READ
};
let linear_offset = if stval >= PHYSICAL_MEMORY_OFFSET {
// Kernel
PHYSICAL_MEMORY_OFFSET
} else {
// User
0
};
/*
let current =
unsafe { &mut *(phys_to_virt(satp::read().frame().start_address().as_usize()) as *mut PageTable) };
let mut pt = Rv39PageTable::new(current, PHYSICAL_MEMORY_OFFSET);
map_range(&mut pt, vaddr, vaddr, linear_offset, flags);
*/
let mut pti = PageTableImpl {
root_paddr: satp::read().frame().start_address().as_usize(),
};
// 这个pagefault handler还有些问题噢
let page = Page::of_addr(VirtAddr::new(vaddr));
if let Ok(pte) = pti.get().ref_entry(page) {
let pte = unsafe { &mut *(pte as *mut PageTableEntry) };
if !pte.is_unused() {
debug!(
"PageAlreadyMapped -> {:#x?}, {:?}",
pte.addr().as_usize(),
pte.flags()
);
//TODO update flags
pti.unmap(vaddr).unwrap();
}
};
pti.map(vaddr, vaddr - linear_offset, flags).unwrap();
}
fn super_timer() {
timer_set_next();
super::timer_tick();
//bare_print!(".");
//发生外界中断时epc的指令还没有执行故无需修改epc到下一条
}
fn init_uart() {
uart::Uart::new(phys_to_virt(UART_BASE)).simple_init();
//但当没有SBI_CONSOLE_PUTCHAR时却为什么不行
super::putfmt_uart(format_args!("{}", "UART output testing\n\r"));
bare_println!("+++ Setting up UART interrupts +++");
}
//被plic串口中断调用
pub fn try_process_serial() -> bool {
match super::getchar_option() {
Some(ch) => {
super::serial_put(ch);
true
}
None => false,
}
}
pub fn init_ext() {
// Qemu virt UART0 = 10
// ALLWINNER D1 UART0 = 18
plic::set_priority(UART0_INT_NUM, 7);
plic::set_threshold(0);
plic::enable(UART0_INT_NUM);
bare_println!("+++ Setting up PLIC +++");
}
fn super_soft() {
sbi::clear_ipi();
bare_println!("Interrupt::SupervisorSoft!");
}
pub fn init_soft() {
unsafe {
sie::set_ssoft();
}
bare_println!("+++ setup soft int! +++");
}
#[export_name = "fetch_trap_num"]
pub fn fetch_trap_num(_context: &UserContext) -> usize {
scause::read().bits()
}
pub fn wait_for_interrupt() {
unsafe {
// enable interrupt and disable
let sie = riscv::register::sstatus::read().sie();
riscv::register::sstatus::set_sie();
riscv::asm::wfi();
if !sie {
riscv::register::sstatus::clear_sie();
}
}
}
fn timer() {
super::timer_tick();
}
/*
* uart::handle_interrupt()
*
fn com1() {
let c = super::COM1.lock().receive();
super::serial_put(c);
}
*/
/*
fn keyboard() {
use pc_keyboard::{DecodedKey, KeyCode};
if let Some(key) = super::keyboard::receive() {
match key {
DecodedKey::Unicode(c) => super::serial_put(c as u8),
DecodedKey::RawKey(code) => {
let s = match code {
KeyCode::ArrowUp => "\u{1b}[A",
KeyCode::ArrowDown => "\u{1b}[B",
KeyCode::ArrowRight => "\u{1b}[C",
KeyCode::ArrowLeft => "\u{1b}[D",
_ => "",
};
for c in s.bytes() {
super::serial_put(c);
}
}
}
}
}
*/
const SupervisorExternal: usize = usize::MAX / 2 + 1 + 8;
// IRQ
const Timer: u8 = 5;
const U_PLIC: u8 = 8;
const S_PLIC: u8 = 9;
const M_PLIC: u8 = 11;
//const Keyboard: u8 = 1;
//const COM2: u8 = 3;
const COM1: u8 = 0;
//const IDE: u8 = 14;

View File

@ -0,0 +1,730 @@
use super::super::*;
use alloc::{collections::VecDeque, string::String, vec, vec::Vec};
use core::fmt::{self, Write};
use kernel_hal::{
ColorDepth, ColorFormat, FramebufferInfo, HalError, PageTableTrait, PhysAddr, VirtAddr,
FRAME_BUFFER,
};
use riscv::addr::Page;
use riscv::asm::sfence_vma_all;
use riscv::paging::{PageTableFlags as PTF, *};
use riscv::register::{satp, sie, stval, time};
use crate::drivers::{
device_tree::{self, Node},
irq, net, serial, virtio,
};
mod consts;
pub mod cpu_C906;
mod sbi;
use consts::*;
// First core stores its SATP here.
static mut SATP: usize = 0;
/// remap kernel with 4K page
pub fn remap_the_kernel(dtb: usize) {
let root_frame = Frame::alloc().expect("failed to alloc frame");
let root_vaddr = phys_to_virt(root_frame.paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
root.zero();
let mut pt = Rv39PageTable::new(root, PHYSICAL_MEMORY_OFFSET);
let linear_offset = PHYSICAL_MEMORY_OFFSET;
//let mut flags = PTF::VALID | PTF::READABLE | PTF::WRITABLE | PTF::EXECUTABLE | PTF::USER;
map_range(
&mut pt,
stext as usize,
etext as usize - 1,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::EXECUTABLE,
)
.unwrap();
map_range(
&mut pt,
srodata as usize,
erodata as usize,
linear_offset,
PTF::VALID | PTF::READABLE,
)
.unwrap();
map_range(
&mut pt,
sdata as usize,
edata as usize,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
// Stack
map_range(
&mut pt,
bootstack as usize,
bootstacktop as usize - 1,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
map_range(
&mut pt,
sbss as usize,
ebss as usize - 1,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
info!("map Heap ...");
// Heap
map_range(
&mut pt,
end as usize,
end as usize + PAGE_SIZE * 5120,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
info!("... Heap");
// Device Tree
#[cfg(feature = "board_qemu")]
map_range(
&mut pt,
dtb,
dtb + consts::MAX_DTB_SIZE,
linear_offset,
PTF::VALID | PTF::READABLE,
)
.unwrap();
// GPIO/CCU
#[cfg(feature = "board_d1")]
map_range(
&mut pt,
phys_to_virt(0x0200_0000),
phys_to_virt(0x0200_0000) + PAGE_SIZE,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
//SYS_CFG
#[cfg(feature = "board_d1")]
map_range(
&mut pt,
phys_to_virt(0x0300_0000),
phys_to_virt(0x0300_0000) + PAGE_SIZE,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
//GMAC
#[cfg(feature = "board_d1")]
map_range(
&mut pt,
phys_to_virt(0x0450_0000),
phys_to_virt(0x0450_0000) + PAGE_SIZE,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
// PLIC
map_range(
&mut pt,
phys_to_virt(PLIC_PRIORITY),
phys_to_virt(PLIC_PRIORITY) + PAGE_SIZE * 0xf,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
map_range(
&mut pt,
phys_to_virt(PLIC_THRESHOLD),
phys_to_virt(PLIC_THRESHOLD) + PAGE_SIZE * 0xf,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
// UART0, VIRTIO
map_range(
&mut pt,
phys_to_virt(UART_BASE),
phys_to_virt(UART_BASE) + PAGE_SIZE * 0xf,
linear_offset,
PTF::VALID | PTF::READABLE | PTF::WRITABLE,
)
.unwrap();
//写satp
let token = root_frame.paddr;
unsafe {
set_page_table(token);
SATP = token;
}
//use core::mem;
//mem::forget(pt);
info!("remap the kernel @ {:#x}", token);
}
pub fn map_range(
page_table: &mut Rv39PageTable,
mut start_addr: VirtAddr,
mut end_addr: VirtAddr,
linear_offset: usize,
flags: PageTableFlags,
) -> Result<(), ()> {
trace!("Mapping range addr: {:#x} ~ {:#x}", start_addr, end_addr);
start_addr = start_addr & !(PAGE_SIZE - 1);
let mut start_page = start_addr / PAGE_SIZE;
//end_addr = (end_addr + PAGE_SIZE - 1) & !(PAGE_SIZE -1);
//let end_page = (end_addr - 1) / PAGE_SIZE;
end_addr = end_addr & !(PAGE_SIZE - 1);
let end_page = end_addr / PAGE_SIZE;
while start_page <= end_page {
let vaddr: VirtAddr = start_page * PAGE_SIZE;
let page = riscv::addr::Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
let frame = riscv::addr::Frame::of_addr(riscv::addr::PhysAddr::new(vaddr - linear_offset));
start_page += 1;
trace!(
"map_range: {:#x} -> {:#x}, flags={:?}",
vaddr,
vaddr - linear_offset,
flags
);
page_table
.map_to(page, frame, flags, &mut FrameAllocatorImpl)
.unwrap()
.flush();
}
info!(
"map range from {:#x} to {:#x}, flags: {:?}",
start_addr,
end_page * PAGE_SIZE,
flags
);
Ok(())
}
extern "C" {
fn start();
fn stext();
fn etext();
fn srodata();
fn erodata();
fn sdata();
fn edata();
fn bootstack();
fn bootstacktop();
fn sbss();
fn ebss();
fn end();
}
/// Page Table
#[repr(C)]
pub struct PageTableImpl {
root_paddr: PhysAddr,
}
impl PageTableImpl {
/// Create a new `PageTable`.
#[allow(clippy::new_without_default)]
#[export_name = "hal_pt_new"]
pub fn new() -> Self {
let root_frame = Frame::alloc().expect("failed to alloc frame");
let root_vaddr = phys_to_virt(root_frame.paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
root.zero();
let current =
phys_to_virt(satp::read().frame().start_address().as_usize()) as *const PageTable;
map_kernel(root_vaddr as _, current as _);
info!("create page table @ {:#x}", root_frame.paddr);
PageTableImpl {
root_paddr: root_frame.paddr,
}
}
#[cfg(target_arch = "riscv32")]
fn get(&mut self) -> Rv32PageTable<'_> {
let root_vaddr = phys_to_virt(self.root_paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
Rv32PageTable::new(root, phys_to_virt(0))
}
#[cfg(target_arch = "riscv64")]
fn get(&mut self) -> Rv39PageTable<'_> {
let root_vaddr = phys_to_virt(self.root_paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
Rv39PageTable::new(root, phys_to_virt(0))
}
}
impl PageTableTrait for PageTableImpl {
/// Map the page of `vaddr` to the frame of `paddr` with `flags`.
#[export_name = "hal_pt_map"]
fn map(&mut self, vaddr: VirtAddr, paddr: PhysAddr, flags: MMUFlags) -> Result<(), HalError> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
let frame = riscv::addr::Frame::of_addr(riscv::addr::PhysAddr::new(paddr));
pt.map_to(page, frame, flags.to_ptf(), &mut FrameAllocatorImpl)
.unwrap()
.flush();
trace!(
"PageTable: {:#X}, map: {:x?} -> {:x?}, flags={:?}",
self.table_phys() as usize,
vaddr,
paddr,
flags
);
Ok(())
}
/// Unmap the page of `vaddr`.
#[export_name = "hal_pt_unmap"]
fn unmap(&mut self, vaddr: VirtAddr) -> Result<(), HalError> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
pt.unmap(page).unwrap().1.flush();
trace!(
"PageTable: {:#X}, unmap: {:x?}",
self.table_phys() as usize,
vaddr
);
Ok(())
}
/// Change the `flags` of the page of `vaddr`.
#[export_name = "hal_pt_protect"]
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<(), HalError> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
pt.update_flags(page, flags.to_ptf()).unwrap().flush();
trace!(
"PageTable: {:#X}, protect: {:x?}, flags={:?}",
self.table_phys() as usize,
vaddr,
flags
);
Ok(())
}
/// Query the physical address which the page of `vaddr` maps to.
#[export_name = "hal_pt_query"]
fn query(&mut self, vaddr: VirtAddr) -> Result<PhysAddr, HalError> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
let res = pt.ref_entry(page);
trace!("query: {:x?} => {:#x?}", vaddr, res);
match res {
Ok(entry) => Ok(entry.addr().as_usize()),
Err(_) => Err(HalError),
}
}
/// Get the physical address of root page table.
#[export_name = "hal_pt_table_phys"]
fn table_phys(&self) -> PhysAddr {
self.root_paddr
}
/// Activate this page table
#[export_name = "hal_pt_activate"]
fn activate(&self) {
let now_token = satp::read().bits();
let new_token = self.table_phys();
if now_token != new_token {
debug!("switch table {:x?} -> {:x?}", now_token, new_token);
unsafe {
set_page_table(new_token);
}
}
}
}
pub unsafe fn set_page_table(vmtoken: usize) {
#[cfg(target_arch = "riscv32")]
let mode = satp::Mode::Sv32;
#[cfg(target_arch = "riscv64")]
let mode = satp::Mode::Sv39;
trace!("set user table: {:#x?}", vmtoken);
satp::set(mode, 0, vmtoken >> 12);
//刷TLB好像很重要
sfence_vma_all();
}
trait FlagsExt {
fn to_ptf(self) -> PTF;
}
impl FlagsExt for MMUFlags {
fn to_ptf(self) -> PTF {
let mut flags = PTF::VALID;
if self.contains(MMUFlags::READ) {
flags |= PTF::READABLE;
}
if self.contains(MMUFlags::WRITE) {
flags |= PTF::WRITABLE;
}
if self.contains(MMUFlags::EXECUTE) {
flags |= PTF::EXECUTABLE;
}
if self.contains(MMUFlags::USER) {
flags |= PTF::USER;
}
flags
}
}
struct FrameAllocatorImpl;
impl FrameAllocator for FrameAllocatorImpl {
fn alloc(&mut self) -> Option<riscv::addr::Frame> {
Frame::alloc().map(|f| {
let paddr = riscv::addr::PhysAddr::new(f.paddr);
riscv::addr::Frame::of_addr(paddr)
})
}
}
impl FrameDeallocator for FrameAllocatorImpl {
fn dealloc(&mut self, frame: riscv::addr::Frame) {
Frame {
paddr: frame.start_address().as_usize(),
}
.dealloc()
}
}
lazy_static! {
static ref STDIN: Mutex<VecDeque<u8>> = Mutex::new(VecDeque::new());
static ref STDIN_CALLBACK: Mutex<Vec<Box<dyn Fn() -> bool + Send + Sync>>> =
Mutex::new(Vec::new());
}
//调用这里
/// Put a char by serial interrupt handler.
pub fn serial_put(mut x: u8) {
if (x == b'\r') || (x == b'\n') {
STDIN.lock().push_back(b'\n');
STDIN.lock().push_back(b'\r');
} else {
STDIN.lock().push_back(x);
}
STDIN_CALLBACK.lock().retain(|f| !f());
}
#[export_name = "hal_serial_set_callback"]
pub fn serial_set_callback(callback: Box<dyn Fn() -> bool + Send + Sync>) {
STDIN_CALLBACK.lock().push(callback);
}
#[export_name = "hal_serial_read"]
pub fn serial_read(buf: &mut [u8]) -> usize {
let mut stdin = STDIN.lock();
let len = stdin.len().min(buf.len());
for c in &mut buf[..len] {
*c = stdin.pop_front().unwrap();
}
len
}
#[export_name = "hal_serial_write"]
pub fn serial_write(s: &str) {
//putfmt(format_args!("{}", s));
putfmt_uart(format_args!("{}", s));
}
// Get TSC frequency.
fn tsc_frequency() -> u16 {
const DEFAULT: u16 = 2600;
// FIXME: QEMU, AMD, VirtualBox
DEFAULT
}
#[export_name = "hal_apic_local_id"]
pub fn apic_local_id() -> u8 {
let lapic = 0;
lapic as u8
}
////////////
pub fn getchar_option() -> Option<u8> {
let c = sbi::console_getchar() as isize;
match c {
-1 => None,
c => Some(c as u8),
}
}
////////////
pub fn putchar(ch: char) {
sbi::console_putchar(ch as u8 as usize);
}
pub fn puts(s: &str) {
for ch in s.chars() {
putchar(ch);
}
}
struct Stdout;
impl fmt::Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
puts(s);
Ok(())
}
}
pub fn putfmt(fmt: fmt::Arguments) {
Stdout.write_fmt(fmt).unwrap();
}
////////////
struct Stdout1;
impl fmt::Write for Stdout1 {
fn write_str(&mut self, s: &str) -> fmt::Result {
//每次都创建一个新的Uart ? 内存位置始终相同
write!(uart::Uart::new(phys_to_virt(UART_BASE)), "{}", s).unwrap();
Ok(())
}
}
pub fn putfmt_uart(fmt: fmt::Arguments) {
Stdout1.write_fmt(fmt).unwrap();
}
////////////
#[macro_export]
macro_rules! bare_print {
($($arg:tt)*) => ({
putfmt(format_args!($($arg)*));
});
}
#[macro_export]
macro_rules! bare_println {
() => (bare_print!("\n"));
($($arg:tt)*) => (bare_print!("{}\n", format_args!($($arg)*)));
}
pub fn get_cycle() -> u64 {
time::read() as u64
/*
unsafe {
MMIO_MTIME.read_volatile()
}
*/
}
#[export_name = "hal_timer_now"]
pub fn timer_now() -> Duration {
const FREQUENCY: u64 = 24_000_000; // ???
// 1_000_000_000
// clock / 频率 /
let time = get_cycle();
//bare_println!("timer_now(): {:?}", time);
Duration::from_nanos(((time / FREQUENCY) * 1_000_000_000) as u64)
// Duration::from_nanos(time / as u64)
}
#[export_name = "hal_timer_set_next"]
fn timer_set_next() {
let TIMEBASE: u64 = 240_000;
// let TIMEBASE: u64 = 25_000_000;
sbi::set_timer(get_cycle() + TIMEBASE);
}
fn timer_init() {
unsafe {
sie::set_stimer();
}
timer_set_next();
}
pub fn init(config: Config) {
//interrupt::init();
timer_init();
/*
interrupt::init_soft();
sbi::send_ipi(0);
unsafe {
llvm_asm!("ebreak"::::"volatile");
}
*/
#[cfg(feature = "board_qemu")]
{
bare_println!("Setup virtio @devicetree {:#x}", config.dtb);
irq::plic::driver_init();
//serial::uart16550::driver_init();
serial::uart::driver_init();
virtio::virtio::driver_init();
device_tree::init(config.dtb);
}
#[cfg(feature = "board_d1")]
{
let plic_node = Node {
name: String::from("plic"),
props: vec![
(
String::from("reg"),
vec![00, 00, 00, 00, 0x10, 0x00, 0x00, 0x00],
),
(String::from("phandle"), vec![00, 00, 00, 0x03]),
],
children: Vec::new(),
};
let uart_node = Node {
name: String::from("uart"),
props: vec![
(
String::from("reg"),
vec![00, 00, 00, 00, 0x02, 0x50, 0x00, 0x00],
),
(String::from("interrupts"), vec![00, 00, 00, 0x12]),
(String::from("interrupt-parent"), vec![00, 00, 00, 0x03]),
],
children: Vec::new(),
};
irq::plic::init_dt(&plic_node);
serial::uart::init_dt(&uart_node);
#[cfg(feature = "rtl8x")]
{
let gmacirq = 62;
net::rtl8x::init(String::from("rtl8211f"), Some(gmacirq));
}
#[cfg(feature = "loopback")]
{
net::loopback::init(String::from("loopback"));
}
}
interrupt::init();
}
pub struct Config {
pub mconfig: u64,
pub dtb: usize,
}
#[export_name = "fetch_fault_vaddr"]
pub fn fetch_fault_vaddr() -> VirtAddr {
stval::read() as _
}
static mut CONFIG: Config = Config { mconfig: 0, dtb: 0 };
/// This structure represents the information that the bootloader passes to the kernel.
#[repr(C)]
#[derive(Debug)]
pub struct BootInfo {
pub memory_map: Vec<u64>,
//pub memory_map: Vec<&'static MemoryDescriptor>,
/// The offset into the virtual address space where the physical memory is mapped.
pub physical_memory_offset: u64,
/// The graphic output information
pub graphic_info: GraphicInfo,
/// Physical address of ACPI2 RSDP, 启动的系统信息表的入口指针
//pub acpi2_rsdp_addr: u64,
/// Physical address of SMBIOS, 产品管理信息的结构表
//pub smbios_addr: u64,
pub hartid: u64,
pub dtb_addr: u64,
/// The start physical address of initramfs
pub initramfs_addr: u64,
/// The size of initramfs
pub initramfs_size: u64,
/// Kernel command line
pub cmdline: &'static str,
}
/// Graphic output information
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct GraphicInfo {
/// Graphic mode
//pub mode: ModeInfo,
pub mode: u64,
/// Framebuffer base physical address
pub fb_addr: u64,
/// Framebuffer size
pub fb_size: u64,
}
pub mod interrupt;
mod plic;
mod uart;
#[export_name = "hal_current_pgtable"]
pub fn current_page_table() -> usize {
#[cfg(target_arch = "riscv32")]
let mode = satp::Mode::Sv32;
#[cfg(target_arch = "riscv64")]
let mode = satp::Mode::Sv39;
satp::read().ppn() << 12
}
pub fn init_framebuffer(width: u32, height: u32, addr: usize, size: usize) {
let fb_info = FramebufferInfo {
xres: width,
yres: height,
xres_virtual: width,
yres_virtual: height,
xoffset: 0,
yoffset: 0,
depth: ColorDepth::ColorDepth32,
format: ColorFormat::RGBA8888,
paddr: virt_to_phys(addr),
vaddr: addr,
screen_size: size,
};
*FRAME_BUFFER.write() = Some(fb_info);
}
#[export_name = "hal_mice_set_callback"]
pub fn mice_set_callback(_callback: Box<dyn Fn([u8; 3]) + Send + Sync>) {
//
}
#[export_name = "hal_kbd_set_callback"]
pub fn kbd_set_callback(_callback: Box<dyn Fn(u16, i32) + Send + Sync>) {
//
}

View File

@ -0,0 +1,107 @@
use super::consts::*;
use super::interrupt;
use super::uart;
use crate::{phys_to_virt, putfmt};
//通过MMIO地址对平台级中断控制器PLIC的寄存器进行设置
//基于opensbi后一般运行于Hart0 S态为Target1
//PLIC是async cause 11
//声明claim会清除中断源上的相应pending位。
//即使mip寄存器的MEIP位没有置位, 也可以claim; 声明不被阀值寄存器的设置影响;
//获取按优先级排序后的下一个可用的中断ID
pub fn next() -> Option<u32> {
let claim_reg = phys_to_virt(PLIC_CLAIM) as *const u32;
let claim_no;
unsafe {
claim_no = claim_reg.read_volatile();
}
if claim_no == 0 {
None //没有可用中断待定
} else {
Some(claim_no)
}
}
//claim时PLIC不再从该相同设备监听中断
//写claim寄存器告诉PLIC处理完成该中断
// id 应该来源于next()函数
pub fn complete(id: u32) {
let complete_reg = phys_to_virt(PLIC_CLAIM) as *mut u32; //和claim相同寄存器,只是读或写的区别
unsafe {
complete_reg.write_volatile(id);
}
}
//看的中断ID是否pending
pub fn is_pending(id: u32) -> bool {
let pend = phys_to_virt(PLIC_PENDING) as *const u32;
let actual_id = 1 << id;
let pend_ids;
unsafe {
pend_ids = pend.read_volatile();
}
actual_id & pend_ids != 0
}
//使能target中某个给定ID的中断
//中断ID可查找qemu/include/hw/riscv/virt.h, 如UART0_IRQ = 10
pub fn enable(id: u32) {
let enables = phys_to_virt(PLIC_INT_ENABLE) as *mut u32; //32位的寄存器
let actual_id = 1 << id;
unsafe {
enables.write_volatile(enables.read_volatile() | actual_id);
// 0x0c00_2000 <=~ (1 << 10)
}
}
//设置中断源的优先级分07级7是最高级, eg:这里id=10, 表示第10个中断源的设置, prio=1
pub fn set_priority(id: u32, prio: u8) {
let actual_prio = prio as u32 & 7;
let prio_reg = phys_to_virt(PLIC_PRIORITY) as *mut u32;
unsafe {
prio_reg.add(id as usize).write_volatile(actual_prio); //0x0c000000 + 4 * 10 <= 1 = 1 & 7
}
}
//设置中断target的全局阀值0..7] <= threshold会被屏蔽
pub fn set_threshold(tsh: u8) {
let actual_tsh = tsh & 7; //使用0b111保留最后三位
let tsh_reg = phys_to_virt(PLIC_THRESHOLD) as *mut u32;
unsafe {
tsh_reg.write_volatile(actual_tsh as u32); // 0x0c20_0000 <= 0 = 0 & 7
}
}
pub fn handle_interrupt() {
if let Some(interrupt) = next() {
match interrupt {
1..=8 => {
//virtio::handle_interrupt(interrupt);
bare_println!("plic virtio external interrupt: {}", interrupt);
/*
if interrupt == 7 {
//virtio net irq
use crate::drivers::IRQ_MANAGER;
IRQ_MANAGER.read()
.try_handle_interrupt(Some(interrupt as usize));
}
*/
}
UART0_INT_NUM => {
//UART中断ID是10
uart::handle_interrupt();
//换用sbi的方式获取字符
//interrupt::try_process_serial();
}
_ => {
bare_println!("Unknown external interrupt: {}", interrupt);
}
}
//这将复位pending的中断允许UART再次中断。
//否则UART将被“卡住”
complete(interrupt);
}
}

View File

@ -0,0 +1,45 @@
pub fn console_putchar(ch: usize) {
sbi_call(SBI_CONSOLE_PUTCHAR, ch, 0, 0);
}
pub fn console_getchar() -> usize {
return sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0);
}
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let ret: usize;
unsafe {
llvm_asm!("ecall"
:"={x10}"(ret)
:"{x10}"(arg0), "{x11}"(arg1), "{x12}"(arg2), "{x17}"(which)
:"memory"
:"volatile");
}
ret
}
pub fn set_timer(stime_value: u64) {
#[cfg(target_pointer_width = "32")]
sbi_call(SBI_SET_TIMER, stime_value as usize, (stime_value >> 32), 0);
#[cfg(target_pointer_width = "64")]
sbi_call(SBI_SET_TIMER, stime_value as usize, 0, 0);
}
pub fn clear_ipi() {
sbi_call(SBI_CLEAR_IPI, 0, 0, 0);
}
pub fn send_ipi(sipi_value: usize) {
sbi_call(SBI_SEND_IPI, sipi_value, 0, 0);
}
const SBI_SET_TIMER: usize = 0;
const SBI_CONSOLE_PUTCHAR: usize = 1;
const SBI_CONSOLE_GETCHAR: usize = 2;
const SBI_CLEAR_IPI: usize = 3;
const SBI_SEND_IPI: usize = 4;
const SBI_REMOTE_FENCE_I: usize = 5;
const SBI_REMOTE_SFENCE_VMA: usize = 6;
const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
const SBI_SHUTDOWN: usize = 8;

View File

@ -0,0 +1,106 @@
use super::consts::UART_BASE;
use crate::{phys_to_virt, putfmt};
use core::convert::TryInto;
use core::fmt::{Error, Write};
pub struct Uart {
base_address: usize,
}
// 结构体Uart的实现块
impl Uart {
pub fn new(base_address: usize) -> Self {
Uart { base_address }
}
#[cfg(not(feature = "board_d1"))]
pub fn simple_init(&mut self) {
let ptr = self.base_address as *mut u8;
unsafe {
// Enable FIFO; (base + 2)
ptr.add(2).write_volatile(0xC7);
// MODEM Ctrl; (base + 4)
ptr.add(4).write_volatile(0x0B);
// Enable interrupts; (base + 1)
ptr.add(1).write_volatile(0x01);
}
}
#[cfg(feature = "board_d1")]
pub fn simple_init(&mut self) {
let ptr = self.base_address as *mut u32;
unsafe {
// Enable FIFO; (base + 2)
ptr.add(2).write_volatile(0x7);
// MODEM Ctrl; (base + 4)
ptr.add(4).write_volatile(0x3);
//D1 ALLWINNER的uart中断使能
// D1 UART_IER offset = 0x4
//
// Enable interrupts; (base + 1)
ptr.add(1).write_volatile(0x1);
}
}
pub fn get(&mut self) -> Option<u8> {
#[cfg(not(feature = "board_d1"))]
let ptr = self.base_address as *mut u8;
#[cfg(feature = "board_d1")]
let ptr = self.base_address as *mut u32;
unsafe {
//查看LSR的DR位为1则有数据
if ptr.add(5).read_volatile() & 0b1 == 0 {
None
} else {
Some((ptr.add(0).read_volatile() & 0xff) as u8)
}
}
}
pub fn put(&mut self, c: u8) {
let ptr = self.base_address as *mut u8;
unsafe {
//此时transmitter empty
ptr.add(0).write_volatile(c);
}
}
}
// 需要实现的write_str()重要函数
impl Write for Uart {
fn write_str(&mut self, out: &str) -> Result<(), Error> {
for c in out.bytes() {
self.put(c);
}
Ok(())
}
}
pub fn handle_interrupt() {
let mut my_uart = Uart::new(phys_to_virt(UART_BASE));
if let Some(c) = my_uart.get() {
let c = c & 0xff;
//CONSOLE
super::serial_put(c);
/*
* serial_write()
match c {
0x7f => { //0x8 [backspace] ; 而实际qemu运行[backspace]键输出0x7f, 表示del
bare_print!("{} {}", 8 as char, 8 as char);
},
10 | 13 => { // 新行或回车
bare_println!();
},
_ => {
bare_print!("{}", c as char);
},
}
*/
}
}

View File

@ -0,0 +1,77 @@
#![allow(dead_code)]
use crate::get_acpi_table;
pub use acpi::{
interrupt::{InterruptModel, InterruptSourceOverride, IoApic, Polarity, TriggerMode},
Acpi,
};
use alloc::vec::Vec;
use lazy_static::*;
use spin::Mutex;
pub struct AcpiTable {
inner: Acpi,
}
lazy_static! {
static ref ACPI_TABLE: Mutex<Option<AcpiTable>> = Mutex::default();
}
impl AcpiTable {
fn initialize_check() {
#[cfg(target_arch = "x86_64")]
{
let mut table = ACPI_TABLE.lock();
if table.is_none() {
*table = get_acpi_table().map(|x| AcpiTable { inner: x });
}
}
}
pub fn invalidate() {
*ACPI_TABLE.lock() = None;
}
pub fn get_ioapic() -> Vec<IoApic> {
Self::initialize_check();
let table = ACPI_TABLE.lock();
match &*table {
None => Vec::default(),
Some(table) => match table.inner.interrupt_model.as_ref().unwrap() {
InterruptModel::Apic(apic) => {
apic.io_apics.iter().map(|x| IoApic { ..*x }).collect()
}
_ => Vec::default(),
},
}
}
pub fn get_interrupt_source_overrides() -> Vec<InterruptSourceOverride> {
Self::initialize_check();
let table = ACPI_TABLE.lock();
match &*table {
None => Vec::default(),
Some(table) => match table.inner.interrupt_model.as_ref().unwrap() {
InterruptModel::Apic(apic) => apic
.interrupt_source_overrides
.iter()
.map(|x| InterruptSourceOverride {
polarity: Self::clone_polarity(&x.polarity),
trigger_mode: Self::clone_trigger_mode(&x.trigger_mode),
..*x
})
.collect(),
_ => Vec::default(),
},
}
}
fn clone_polarity(x: &Polarity) -> Polarity {
match x {
Polarity::SameAsBus => Polarity::SameAsBus,
Polarity::ActiveHigh => Polarity::ActiveHigh,
Polarity::ActiveLow => Polarity::ActiveLow,
}
}
fn clone_trigger_mode(x: &TriggerMode) -> TriggerMode {
match x {
TriggerMode::SameAsBus => TriggerMode::SameAsBus,
TriggerMode::Edge => TriggerMode::Edge,
TriggerMode::Level => TriggerMode::Level,
}
}
}

View File

@ -0,0 +1,423 @@
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
use super::{acpi_table::*, phys_to_virt};
use alloc::boxed::Box;
use alloc::vec::Vec;
use apic::IoApic;
use ps2_mouse::{Mouse, MouseState};
use spin::Mutex;
use trapframe::TrapFrame;
const IO_APIC_NUM_REDIRECTIONS: u8 = 120;
const TABLE_SIZE: usize = 256;
pub type InterruptHandle = Box<dyn Fn() + Send + Sync>;
lazy_static! {
static ref IRQ_TABLE: Mutex<Vec<Option<InterruptHandle>>> = Default::default();
}
lazy_static! {
static ref MOUSE: Mutex<Mouse> = Mutex::new(Mouse::new());
static ref MOUSE_CALLBACK: Mutex<Vec<Box<dyn Fn([u8; 3]) + Send + Sync>>> =
Mutex::new(Vec::new());
}
#[export_name = "hal_mice_set_callback"]
pub fn mice_set_callback(callback: Box<dyn Fn([u8; 3]) + Send + Sync>) {
MOUSE_CALLBACK.lock().push(callback);
}
fn mouse_on_complete(mouse_state: MouseState) {
debug!("mouse state: {:?}", mouse_state);
MOUSE_CALLBACK.lock().iter().for_each(|callback| {
callback([
mouse_state.get_flags().bits(),
mouse_state.get_x() as u8,
mouse_state.get_y() as u8,
]);
});
}
fn mouse() {
use x86_64::instructions::port::PortReadOnly;
let mut port = PortReadOnly::new(0x60);
let packet = unsafe { port.read() };
MOUSE.lock().process_packet(packet);
}
pub fn init() {
MOUSE.lock().init().unwrap();
MOUSE.lock().set_on_complete(mouse_on_complete);
unsafe {
init_ioapic();
}
init_irq_table();
irq_add_handle(Timer + IRQ0, Box::new(timer));
irq_add_handle(Keyboard + IRQ0, Box::new(keyboard));
irq_add_handle(Mouse + IRQ0, Box::new(mouse));
irq_add_handle(COM1 + IRQ0, Box::new(com1));
irq_enable_raw(Keyboard, Keyboard + IRQ0);
irq_enable_raw(Mouse, Mouse + IRQ0);
irq_enable_raw(COM1, COM1 + IRQ0);
}
fn init_irq_table() {
let mut table = IRQ_TABLE.lock();
for _ in 0..TABLE_SIZE {
table.push(None);
}
}
unsafe fn init_ioapic() {
for ioapic in AcpiTable::get_ioapic() {
info!("Ioapic found: {:#x?}", ioapic);
let mut ip = IoApic::new(phys_to_virt(ioapic.address as usize));
ip.disable_all();
}
let mut ip = IoApic::new(phys_to_virt(super::IOAPIC_ADDR));
ip.disable_all();
}
fn get_ioapic(irq: u32) -> Option<acpi::interrupt::IoApic> {
for i in AcpiTable::get_ioapic() {
let num_instr = core::cmp::min(
ioapic_maxinstr(i.address).unwrap(),
IO_APIC_NUM_REDIRECTIONS - 1,
);
if i.global_system_interrupt_base <= irq
&& irq <= i.global_system_interrupt_base + num_instr as u32
{
return Some(i);
}
}
None
}
fn ioapic_controller(i: &acpi::interrupt::IoApic) -> IoApic {
unsafe { IoApic::new(phys_to_virt(i.address as usize)) }
}
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, 0); // TODO 0 should replace in multi-core case
match tf.trap_num as u8 {
Breakpoint => breakpoint(),
DoubleFault => double_fault(tf),
PageFault => page_fault(tf),
IRQ0..=63 => irq_handle(tf.trap_num as u8),
_ => panic!("Unhandled interrupt {:x} {:#x?}", tf.trap_num, tf),
}
}
#[export_name = "hal_irq_handle"]
pub fn irq_handle(irq: u8) {
use super::{LocalApic, XApic, LAPIC_ADDR};
let mut lapic = unsafe { XApic::new(phys_to_virt(LAPIC_ADDR)) };
lapic.eoi();
let table = IRQ_TABLE.lock();
match &table[irq as usize] {
Some(f) => f(),
None => panic!("unhandled external IRQ number: {}", irq),
}
}
#[export_name = "hal_ioapic_set_handle"]
pub fn set_handle(global_irq: u32, handle: InterruptHandle) -> Option<u8> {
info!("set_handle irq={:#x?}", global_irq);
// if global_irq == 1 {
// irq_add_handle(global_irq as u8 + IRQ0, handle);
// return Some(global_irq as u8 + IRQ0);
// }
let ioapic_info = get_ioapic(global_irq)?;
let mut ioapic = ioapic_controller(&ioapic_info);
let offset = (global_irq - ioapic_info.global_system_interrupt_base) as u8;
let irq = ioapic.irq_vector(offset);
let new_handle = if global_irq == 0x1 {
Box::new(move || {
handle();
keyboard();
mouse();
})
} else {
handle
};
irq_add_handle(irq, new_handle).map(|x| {
info!(
"irq_set_handle: mapping from {:#x?} to {:#x?}",
global_irq, x
);
ioapic.set_irq_vector(offset, x);
x
})
}
#[export_name = "hal_ioapic_reset_handle"]
pub fn reset_handle(global_irq: u32) -> bool {
info!("reset_handle");
let ioapic_info = if let Some(x) = get_ioapic(global_irq) {
x
} else {
return false;
};
let mut ioapic = ioapic_controller(&ioapic_info);
let offset = (global_irq - ioapic_info.global_system_interrupt_base) as u8;
let irq = ioapic.irq_vector(offset);
if !irq_remove_handle(irq) {
ioapic.set_irq_vector(offset, 0);
true
} else {
false
}
}
/// Add a handle to IRQ table. Return the specified irq or an allocated irq on success
#[export_name = "hal_irq_add_handle"]
pub fn irq_add_handle(irq: u8, handle: InterruptHandle) -> Option<u8> {
info!("IRQ add handle {:#x?}", irq);
let mut table = IRQ_TABLE.lock();
// allocate a valid irq number
if irq == 0 {
let mut id = 0x20;
while id < table.len() {
if table[id].is_none() {
table[id] = Some(handle);
return Some(id as u8);
}
id += 1;
}
return None;
}
match table[irq as usize] {
Some(_) => None,
None => {
table[irq as usize] = Some(handle);
Some(irq)
}
}
}
#[export_name = "hal_irq_remove_handle"]
pub fn irq_remove_handle(irq: u8) -> bool {
// TODO: ioapic redirection entries associated with this should be reset.
info!("IRQ remove handle {:#x?}", irq);
let irq = irq as usize;
let mut table = IRQ_TABLE.lock();
match table[irq] {
Some(_) => {
table[irq] = None;
false
}
None => true,
}
}
#[export_name = "hal_irq_allocate_block"]
pub fn allocate_block(irq_num: u32) -> Option<(usize, usize)> {
info!("hal_irq_allocate_block: count={:#x?}", irq_num);
let irq_num = u32::next_power_of_two(irq_num) as usize;
let mut irq_start = 0x20;
let mut irq_cur = irq_start;
let mut table = IRQ_TABLE.lock();
while irq_cur < TABLE_SIZE && irq_cur < irq_start + irq_num {
if table[irq_cur].is_none() {
irq_cur += 1;
} else {
irq_start = (irq_cur - irq_cur % irq_num) + irq_num;
irq_cur = irq_start;
}
}
for i in irq_start..irq_start + irq_num {
table[i] = Some(Box::new(|| {}));
}
info!(
"hal_irq_allocate_block: start={:#x?} num={:#x?}",
irq_start, irq_num
);
Some((irq_start, irq_num))
}
#[export_name = "hal_irq_free_block"]
pub fn free_block(irq_start: u32, irq_num: u32) {
let mut table = IRQ_TABLE.lock();
for i in irq_start..irq_start + irq_num {
table[i as usize] = None;
}
}
#[export_name = "hal_irq_overwrite_handler"]
pub fn overwrite_handler(msi_id: u32, handle: Box<dyn Fn() + Send + Sync>) -> bool {
info!("IRQ overwrite handle {:#x?}", msi_id);
let mut table = IRQ_TABLE.lock();
let set = table[msi_id as usize].is_none();
table[msi_id as usize] = Some(handle);
set
}
#[export_name = "hal_irq_enable"]
pub fn irq_enable(irq: u32) {
info!("irq_enable irq={:#x?}", irq);
// if irq == 1 {
// irq_enable_raw(irq as u8, irq as u8 + IRQ0);
// return;
// }
if let Some(x) = get_ioapic(irq) {
let mut ioapic = ioapic_controller(&x);
ioapic.enable((irq - x.global_system_interrupt_base) as u8, 0);
}
}
fn irq_enable_raw(irq: u8, vector: u8) {
info!("irq_enable_raw: irq={:#x?}, vector={:#x?}", irq, vector);
let mut ioapic = unsafe { IoApic::new(phys_to_virt(super::IOAPIC_ADDR)) };
ioapic.set_irq_vector(irq, vector);
ioapic.enable(irq, 0)
}
const IrqMin: usize = 0x20;
const IrqMax: usize = 0x3f;
// Drivers IrqManager
#[inline(always)]
pub fn enable_irq(irq: usize) {
let mut ioapic = unsafe { IoApic::new(phys_to_virt(super::IOAPIC_ADDR as usize)) };
ioapic.set_irq_vector(irq as u8, (IrqMin + irq) as u8);
ioapic.enable(irq as u8, 0);
}
#[export_name = "hal_irq_disable"]
pub fn irq_disable(irq: u32) {
info!("irq_disable");
if let Some(x) = get_ioapic(irq) {
let mut ioapic = ioapic_controller(&x);
ioapic.disable((irq - x.global_system_interrupt_base) as u8);
}
}
#[export_name = "hal_irq_configure"]
pub fn irq_configure(
global_irq: u32,
vector: u8,
dest: u8,
level_trig: bool,
active_high: bool,
) -> bool {
info!(
"irq_configure: irq={:#x?}, vector={:#x?}, dest={:#x?}, level_trig={:#x?}, active_high={:#x?}",
global_irq, vector, dest, level_trig, active_high
);
get_ioapic(global_irq)
.map(|x| {
let mut ioapic = ioapic_controller(&x);
ioapic.config(
(global_irq - x.global_system_interrupt_base) as u8,
vector,
dest,
level_trig,
active_high,
false, /* physical */
true, /* mask */
);
})
.is_some()
}
#[export_name = "hal_irq_maxinstr"]
pub fn ioapic_maxinstr(ioapic_addr: u32) -> Option<u8> {
let mut table = MAX_INSTR_TABLE.lock();
for (addr, v) in table.iter() {
if *addr == ioapic_addr as usize {
return Some(*v);
}
}
let mut ioapic = unsafe { IoApic::new(phys_to_virt(ioapic_addr as usize)) };
let v = ioapic.maxintr();
table.push((ioapic_addr as usize, v));
Some(v)
}
lazy_static! {
static ref MAX_INSTR_TABLE: Mutex<Vec<(usize, u8)>> = Mutex::default();
}
#[export_name = "hal_irq_isvalid"]
pub fn irq_is_valid(irq: u32) -> bool {
trace!("irq_is_valid: irq={:#x?}", irq);
get_ioapic(irq).is_some()
}
fn breakpoint() {
panic!("\nEXCEPTION: Breakpoint");
}
fn double_fault(tf: &TrapFrame) {
panic!("\nEXCEPTION: Double Fault\n{:#x?}", tf);
}
fn page_fault(tf: &mut TrapFrame) {
panic!("\nEXCEPTION: Page Fault\n{:#x?}", tf);
}
fn timer() {
super::timer_tick();
}
fn com1() {
let c = super::COM1.lock().receive();
super::serial_put(c);
}
fn keyboard() {
use pc_keyboard::{DecodedKey, KeyCode};
if let Some(key) = super::keyboard::receive() {
match key {
DecodedKey::Unicode(c) => super::serial_put(c as u8),
DecodedKey::RawKey(code) => {
let s = match code {
KeyCode::ArrowUp => "\u{1b}[A",
KeyCode::ArrowDown => "\u{1b}[B",
KeyCode::ArrowRight => "\u{1b}[C",
KeyCode::ArrowLeft => "\u{1b}[D",
_ => "",
};
for c in s.bytes() {
super::serial_put(c);
}
}
}
}
}
// Reference: https://wiki.osdev.org/Exceptions
const DivideError: u8 = 0;
const Debug: u8 = 1;
const NonMaskableInterrupt: u8 = 2;
const Breakpoint: u8 = 3;
const Overflow: u8 = 4;
const BoundRangeExceeded: u8 = 5;
const InvalidOpcode: u8 = 6;
const DeviceNotAvailable: u8 = 7;
const DoubleFault: u8 = 8;
const CoprocessorSegmentOverrun: u8 = 9;
const InvalidTSS: u8 = 10;
const SegmentNotPresent: u8 = 11;
const StackSegmentFault: u8 = 12;
const GeneralProtectionFault: u8 = 13;
const PageFault: u8 = 14;
const FloatingPointException: u8 = 16;
const AlignmentCheck: u8 = 17;
const MachineCheck: u8 = 18;
const SIMDFloatingPointException: u8 = 19;
const VirtualizationException: u8 = 20;
const SecurityException: u8 = 30;
const IRQ0: u8 = 32;
// IRQ
const Timer: u8 = 0;
const Keyboard: u8 = 1;
const COM2: u8 = 3;
const COM1: u8 = 4;
const Mouse: u8 = 12;
const IDE: u8 = 14;
const Error: u8 = 19;
const Spurious: u8 = 31;

View File

@ -0,0 +1,42 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
);
static ref KBD_CALLBACK: Mutex<Vec<Box<dyn Fn(u16, i32) + Send + Sync>>> =
Mutex::new(Vec::new());
}
#[export_name = "hal_kbd_set_callback"]
pub fn kbd_set_callback(callback: Box<dyn Fn(u16, i32) + Send + Sync>) {
KBD_CALLBACK.lock().push(callback);
}
/// Receive character from keyboard
/// Should be called on every interrupt
pub fn receive() -> Option<DecodedKey> {
let mut keyboard = KEYBOARD.lock();
let mut data_port = Port::<u8>::new(0x60);
let mut status_port = Port::<u8>::new(0x64);
// Output buffer status = 1
if unsafe { status_port.read() } & 1 != 0 {
let scancode = unsafe { data_port.read() };
KBD_CALLBACK.lock().iter().for_each(|callback| {
match scancode {
0x80..=0xFF => callback((scancode as u16) - 0x80, 0),
_ => callback(scancode as u16, 1),
};
});
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
return keyboard.process_keyevent(key_event);
}
}
None
}

View File

@ -0,0 +1,525 @@
use {
super::super::*,
acpi::{parse_rsdp, Acpi, AcpiHandler, PhysicalMapping},
alloc::{collections::VecDeque, vec::Vec},
apic::{LocalApic, XApic},
core::arch::x86_64::{__cpuid, _mm_clflush, _mm_mfence},
core::convert::TryFrom,
core::fmt::{Arguments, Write},
core::ptr::NonNull,
core::time::Duration,
git_version::git_version,
kernel_hal::{
ColorDepth, ColorFormat, FramebufferInfo, HalError, PageTableTrait, Result, FRAME_BUFFER,
},
rcore_console::{Console, ConsoleOnGraphic, DrawTarget, Pixel, Rgb888, Size},
spin::Mutex,
uart_16550::SerialPort,
x86_64::{
instructions::port::Port,
registers::control::{Cr2, Cr3, Cr3Flags, Cr4, Cr4Flags},
structures::paging::{PageTableFlags as PTF, *},
},
};
mod acpi_table;
pub mod interrupt;
mod keyboard;
/// Page Table
#[repr(C)]
pub struct PageTableImpl {
root_paddr: PhysAddr,
}
impl PageTableImpl {
#[export_name = "hal_pt_current"]
pub fn current() -> Self {
PageTableImpl {
root_paddr: Cr3::read().0.start_address().as_u64() as _,
}
}
/// Create a new `PageTable`.
#[allow(clippy::new_without_default)]
#[export_name = "hal_pt_new"]
pub fn new() -> Self {
let root_frame = Frame::alloc().expect("failed to alloc frame");
let root_vaddr = phys_to_virt(root_frame.paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
root.zero();
map_kernel(root_vaddr as _, frame_to_page_table(Cr3::read().0) as _);
trace!("create page table @ {:#x}", root_frame.paddr);
PageTableImpl {
root_paddr: root_frame.paddr,
}
}
fn get(&mut self) -> OffsetPageTable<'_> {
let root_vaddr = phys_to_virt(self.root_paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
let offset = x86_64::VirtAddr::new(phys_to_virt(0) as u64);
unsafe { OffsetPageTable::new(root, offset) }
}
}
impl PageTableTrait for PageTableImpl {
/// Map the page of `vaddr` to the frame of `paddr` with `flags`.
#[export_name = "hal_pt_map"]
fn map(&mut self, vaddr: VirtAddr, paddr: PhysAddr, flags: MMUFlags) -> Result<()> {
let mut pt = self.get();
unsafe {
pt.map_to_with_table_flags(
Page::<Size4KiB>::from_start_address(x86_64::VirtAddr::new(vaddr as u64)).unwrap(),
PhysFrame::from_start_address(x86_64::PhysAddr::new(paddr as u64)).unwrap(),
flags.to_ptf(),
PTF::PRESENT | PTF::WRITABLE | PTF::USER_ACCESSIBLE,
&mut FrameAllocatorImpl,
)
.unwrap()
.flush();
};
debug!(
"map: {:x?} -> {:x?}, flags={:?} in {:#x?}",
vaddr, paddr, flags, self.root_paddr
);
Ok(())
}
/// Unmap the page of `vaddr`.
#[export_name = "hal_pt_unmap"]
fn unmap(&mut self, vaddr: VirtAddr) -> Result<()> {
let mut pt = self.get();
let page =
Page::<Size4KiB>::from_start_address(x86_64::VirtAddr::new(vaddr as u64)).unwrap();
// This is a workaround to an issue in the x86-64 crate
// A page without PRESENT bit is not unmappable AND mapable
// So we add PRESENT bit here
unsafe {
pt.update_flags(page, PTF::PRESENT | PTF::NO_EXECUTE).ok();
}
match pt.unmap(page) {
Ok((_, flush)) => {
flush.flush();
trace!("unmap: {:x?} in {:#x?}", vaddr, self.root_paddr);
}
Err(mapper::UnmapError::PageNotMapped) => {
trace!(
"unmap not mapped, skip: {:x?} in {:#x?}",
vaddr,
self.root_paddr
);
return Ok(());
}
Err(err) => {
debug!(
"unmap failed: {:x?} err={:x?} in {:#x?}",
vaddr, err, self.root_paddr
);
return Err(HalError);
}
}
Ok(())
}
/// Change the `flags` of the page of `vaddr`.
#[export_name = "hal_pt_protect"]
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<()> {
let mut pt = self.get();
let page =
Page::<Size4KiB>::from_start_address(x86_64::VirtAddr::new(vaddr as u64)).unwrap();
if let Ok(flush) = unsafe { pt.update_flags(page, flags.to_ptf()) } {
flush.flush();
}
trace!("protect: {:x?}, flags={:?}", vaddr, flags);
Ok(())
}
/// Query the physical address which the page of `vaddr` maps to.
#[export_name = "hal_pt_query"]
fn query(&mut self, vaddr: VirtAddr) -> Result<PhysAddr> {
let pt = self.get();
let ret = pt
.translate_addr(x86_64::VirtAddr::new(vaddr as u64))
.map(|addr| addr.as_u64() as PhysAddr)
.ok_or(HalError);
trace!("query: {:x?} => {:x?}", vaddr, ret);
ret
}
/// Get the physical address of root page table.
#[export_name = "hal_pt_table_phys"]
fn table_phys(&self) -> PhysAddr {
self.root_paddr
}
// /// Activate this page table
// #[export_name = "hal_pt_activate"]
// fn activate(&self) {
// unimplemented!()
// }
}
/// Set page table.
///
/// # Safety
/// This function will set CR3 to `vmtoken`.
pub unsafe fn set_page_table(vmtoken: usize) {
let frame = PhysFrame::containing_address(x86_64::PhysAddr::new(vmtoken as _));
if Cr3::read().0 == frame {
return;
}
Cr3::write(frame, Cr3Flags::empty());
debug!("set page_table @ {:#x}", vmtoken);
}
fn frame_to_page_table(frame: PhysFrame) -> *mut PageTable {
let vaddr = phys_to_virt(frame.start_address().as_u64() as usize);
vaddr as *mut PageTable
}
trait FlagsExt {
fn to_ptf(self) -> PTF;
}
impl FlagsExt for MMUFlags {
fn to_ptf(self) -> PTF {
let mut flags = PTF::empty();
if self.contains(MMUFlags::READ) {
flags |= PTF::PRESENT;
}
if self.contains(MMUFlags::WRITE) {
flags |= PTF::WRITABLE;
}
if !self.contains(MMUFlags::EXECUTE) {
flags |= PTF::NO_EXECUTE;
}
if self.contains(MMUFlags::USER) {
flags |= PTF::USER_ACCESSIBLE;
}
let cache_policy = (self.bits() & 3) as u32; // 最低三位用于储存缓存策略
match CachePolicy::try_from(cache_policy) {
Ok(CachePolicy::Cached) => {
flags.remove(PTF::WRITE_THROUGH);
}
Ok(CachePolicy::Uncached) | Ok(CachePolicy::UncachedDevice) => {
flags |= PTF::NO_CACHE | PTF::WRITE_THROUGH;
}
Ok(CachePolicy::WriteCombining) => {
flags |= PTF::NO_CACHE | PTF::WRITE_THROUGH;
// 当位于level=1时页面更大在1<<12位上0x100为1
// 但是bitflags里面没有这一位。由页表自行管理标记位去吧
}
Err(_) => unreachable!("invalid cache policy"),
}
flags
}
}
struct FrameAllocatorImpl;
unsafe impl FrameAllocator<Size4KiB> for FrameAllocatorImpl {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
Frame::alloc().map(|f| {
let paddr = x86_64::PhysAddr::new(f.paddr as u64);
PhysFrame::from_start_address(paddr).unwrap()
})
}
}
impl FrameDeallocator<Size4KiB> for FrameAllocatorImpl {
unsafe fn deallocate_frame(&mut self, frame: PhysFrame) {
Frame {
paddr: frame.start_address().as_u64() as usize,
}
.dealloc()
}
}
static CONSOLE: Mutex<Option<ConsoleOnGraphic<Framebuffer>>> = Mutex::new(None);
struct Framebuffer {
width: u32,
height: u32,
buf: &'static mut [u32],
}
impl DrawTarget<Rgb888> for Framebuffer {
type Error = core::convert::Infallible;
fn draw_pixel(&mut self, item: Pixel<Rgb888>) -> core::result::Result<(), Self::Error> {
let idx = (item.0.x as u32 + item.0.y as u32 * self.width) as usize;
self.buf[idx] = unsafe { core::mem::transmute(item.1) };
Ok(())
}
fn size(&self) -> Size {
Size::new(self.width, self.height)
}
}
/// Initialize console on framebuffer.
pub fn init_framebuffer(width: u32, height: u32, addr: usize, size: usize) {
let vaddr = phys_to_virt(addr);
let fb_info = FramebufferInfo {
xres: width,
yres: height,
xres_virtual: width,
yres_virtual: height,
xoffset: 0,
yoffset: 0,
depth: ColorDepth::ColorDepth32,
format: ColorFormat::RGBA8888,
paddr: addr,
vaddr,
screen_size: size,
};
*FRAME_BUFFER.write() = Some(fb_info);
let console = Console::on_frame_buffer(Framebuffer {
width,
height,
buf: unsafe {
core::slice::from_raw_parts_mut(vaddr as *mut u32, (width * height) as usize)
},
});
*CONSOLE.lock() = Some(console);
}
static COM1: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
pub fn putfmt(fmt: Arguments) {
COM1.lock().write_fmt(fmt).unwrap();
if let Some(console) = CONSOLE.lock().as_mut() {
console.write_fmt(fmt).unwrap();
}
}
lazy_static! {
static ref STDIN: Mutex<VecDeque<u8>> = Mutex::new(VecDeque::new());
static ref STDIN_CALLBACK: Mutex<Vec<Box<dyn Fn() -> bool + Send + Sync>>> =
Mutex::new(Vec::new());
}
/// Put a char by serial interrupt handler.
pub fn serial_put(mut x: u8) {
if x == b'\r' {
x = b'\n';
}
STDIN.lock().push_back(x);
STDIN_CALLBACK.lock().retain(|f| !f());
}
#[export_name = "hal_serial_set_callback"]
pub fn serial_set_callback(callback: Box<dyn Fn() -> bool + Send + Sync>) {
STDIN_CALLBACK.lock().push(callback);
}
#[export_name = "hal_serial_read"]
pub fn serial_read(buf: &mut [u8]) -> usize {
let mut stdin = STDIN.lock();
let len = stdin.len().min(buf.len());
for c in &mut buf[..len] {
*c = stdin.pop_front().unwrap();
}
len
}
#[export_name = "hal_serial_write"]
pub fn serial_write(s: &str) {
putfmt(format_args!("{}", s));
}
/// Get TSC frequency.
///
/// WARN: This will be very slow on virtual machine since it uses CPUID instruction.
fn tsc_frequency() -> u16 {
const DEFAULT: u16 = 2600;
if let Some(info) = raw_cpuid::CpuId::new().get_processor_frequency_info() {
let f = info.processor_base_frequency();
return if f == 0 { DEFAULT } else { f };
}
// FIXME: QEMU, AMD, VirtualBox
DEFAULT
}
#[export_name = "hal_timer_now"]
pub fn timer_now() -> Duration {
let tsc = unsafe { core::arch::x86_64::_rdtsc() };
Duration::from_nanos(tsc * 1000 / unsafe { TSC_FREQUENCY } as u64)
}
fn timer_init() {
let mut lapic = unsafe { XApic::new(phys_to_virt(LAPIC_ADDR)) };
lapic.cpu_init();
}
#[export_name = "hal_apic_local_id"]
pub fn apic_local_id() -> u8 {
let lapic = unsafe { XApic::new(phys_to_virt(LAPIC_ADDR)) };
lapic.id() as u8
}
const LAPIC_ADDR: usize = 0xfee0_0000;
const IOAPIC_ADDR: usize = 0xfec0_0000;
#[export_name = "hal_vdso_constants"]
fn vdso_constants() -> VdsoConstants {
let tsc_frequency = unsafe { TSC_FREQUENCY };
let mut constants = VdsoConstants {
max_num_cpus: 1,
features: Features {
cpu: 0,
hw_breakpoint_count: 0,
hw_watchpoint_count: 0,
},
dcache_line_size: 0,
icache_line_size: 0,
ticks_per_second: tsc_frequency as u64 * 1_000_000,
ticks_to_mono_numerator: 1000,
ticks_to_mono_denominator: tsc_frequency as u32,
physmem: 0,
version_string_len: 0,
version_string: Default::default(),
};
constants.set_version_string(git_version!(
prefix = "git-",
args = ["--always", "--abbrev=40", "--dirty=-dirty"]
));
constants
}
/// Initialize the HAL.
pub fn init(config: Config) {
timer_init();
interrupt::init();
COM1.lock().init();
unsafe {
// enable global page
Cr4::update(|f| f.insert(Cr4Flags::PAGE_GLOBAL));
// store config
CONFIG = config;
// get tsc frequency
TSC_FREQUENCY = tsc_frequency();
// start multi-processors
fn ap_main() {
info!("processor {} started", apic_local_id());
unsafe {
trapframe::init();
}
timer_init();
let ap_fn = unsafe { CONFIG.ap_fn };
ap_fn()
}
fn stack_fn(pid: usize) -> usize {
// split and reuse the current stack
unsafe {
let mut stack: usize;
asm!("mov {}, rsp", out(reg) stack);
stack -= 0x4000 * pid;
stack
}
}
x86_smpboot::start_application_processors(ap_main, stack_fn, phys_to_virt);
}
}
/// Configuration of HAL.
pub struct Config {
pub acpi_rsdp: u64,
pub smbios: u64,
pub ap_fn: fn() -> !,
}
#[export_name = "fetch_fault_vaddr"]
pub fn fetch_fault_vaddr() -> VirtAddr {
Cr2::read().as_u64() as _
}
/// Get physical address of `acpi_rsdp` and `smbios` on x86_64.
#[export_name = "hal_pc_firmware_tables"]
pub fn pc_firmware_tables() -> (u64, u64) {
unsafe { (CONFIG.acpi_rsdp, CONFIG.smbios) }
}
static mut CONFIG: Config = Config {
acpi_rsdp: 0,
smbios: 0,
ap_fn: || unreachable!(),
};
static mut TSC_FREQUENCY: u16 = 2600;
/// Build ACPI Table
struct AcpiHelper {}
impl AcpiHandler for AcpiHelper {
unsafe fn map_physical_region<T>(
&mut self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<T> {
#[allow(non_snake_case)]
let OFFSET = 0;
let page_start = physical_address / PAGE_SIZE;
let page_end = (physical_address + size + PAGE_SIZE - 1) / PAGE_SIZE;
PhysicalMapping::<T> {
physical_start: physical_address,
virtual_start: NonNull::new_unchecked(phys_to_virt(physical_address + OFFSET) as *mut T),
mapped_length: size,
region_length: PAGE_SIZE * (page_end - page_start),
}
}
fn unmap_physical_region<T>(&mut self, _region: PhysicalMapping<T>) {}
}
#[export_name = "hal_acpi_table"]
pub fn get_acpi_table() -> Option<Acpi> {
#[cfg(target_arch = "x86_64")]
{
let mut handler = AcpiHelper {};
match unsafe { parse_rsdp(&mut handler, pc_firmware_tables().0 as usize) } {
Ok(table) => Some(table),
Err(info) => {
warn!("get_acpi_table error: {:#x?}", info);
None
}
}
}
#[cfg(not(target_arch = "x86_64"))]
None
}
/// IO Port in/out instruction
#[export_name = "hal_outpd"]
pub fn outpd(port: u16, value: u32) {
unsafe {
Port::new(port).write(value);
}
}
#[export_name = "hal_inpd"]
pub fn inpd(port: u16) -> u32 {
unsafe { Port::new(port).read() }
}
/// Flush the physical frame.
#[export_name = "hal_frame_flush"]
pub fn frame_flush(target: PhysAddr) {
unsafe {
for paddr in (target..target + PAGE_SIZE).step_by(cacheline_size()) {
_mm_clflush(phys_to_virt(paddr) as *const u8);
}
_mm_mfence();
}
}
/// Get cache line size in bytes.
fn cacheline_size() -> usize {
let leaf = unsafe { __cpuid(1).ebx };
(((leaf >> 8) & 0xff) << 3) as usize
}
#[export_name = "hal_current_pgtable"]
pub fn current_page_table() -> usize {
PageTableImpl::current().root_paddr
}

View File

@ -0,0 +1,61 @@
/*
use super::bus::virtio_mmio::virtio_probe;
use super::serial::uart16550;
*/
use super::irq::IntcDriver;
use super::CMDLINE;
use crate::phys_to_virt;
use alloc::{collections::BTreeMap, string::String, sync::Arc};
use core::slice;
pub use device_tree::{DeviceTree, Node};
use spin::RwLock;
const DEVICE_TREE_MAGIC: u32 = 0xd00dfeed;
lazy_static! {
/// Compatible lookup
pub static ref DEVICE_TREE_REGISTRY: RwLock<BTreeMap<&'static str, fn(&Node)>> =
RwLock::new(BTreeMap::new());
/// Interrupt controller lookup
pub static ref DEVICE_TREE_INTC: RwLock<BTreeMap<u32, Arc<dyn IntcDriver>>> =
RwLock::new(BTreeMap::new());
}
fn walk_dt_node(dt: &Node, intc_only: bool) {
if let Ok(compatible) = dt.prop_str("compatible") {
if dt.has_prop("interrupt-controller") == intc_only {
let registry = DEVICE_TREE_REGISTRY.read();
if let Some(f) = registry.get(compatible) {
f(dt);
}
}
}
if let Ok(bootargs) = dt.prop_str("bootargs") {
if bootargs.len() > 0 {
info!("Kernel cmdline: {}", bootargs);
*CMDLINE.write() = String::from(bootargs);
}
}
for child in dt.children.iter() {
walk_dt_node(child, intc_only);
}
}
struct DtbHeader {
magic: u32,
size: u32,
}
pub fn init(dtb: usize) {
let header = unsafe { &*(dtb as *const DtbHeader) };
let magic = u32::from_be(header.magic);
if magic == DEVICE_TREE_MAGIC {
let size = u32::from_be(header.size);
let dtb_data = unsafe { slice::from_raw_parts(dtb as *const u8, size as usize) };
if let Ok(dt) = DeviceTree::load(dtb_data) {
// find interrupt controller first
walk_dt_node(&dt.root, true);
walk_dt_node(&dt.root, false);
}
}
}

View File

@ -0,0 +1,75 @@
//! BCM2836 interrupt
use super::super::DRIVERS;
use super::{super::IRQ_MANAGER, IntcDriver, IrqManager};
use crate::drivers::{
device_tree::DEVICE_TREE_INTC, device_tree::DEVICE_TREE_REGISTRY, DeviceType, Driver,
};
use crate::memory::phys_to_virt;
use crate::{sync::SpinNoIrqLock as Mutex, util::read, util::write};
use alloc::string::String;
use alloc::sync::Arc;
use bcm2837::interrupt::Controller;
use bcm2837::interrupt::Interrupt;
pub struct Bcm2837Intc {
manager: Mutex<IrqManager>,
}
impl Driver for Bcm2837Intc {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
let mut res = false;
let manager = self.manager.lock();
for intr in Controller::new().pending_interrupts() {
res |= manager.try_handle_interrupt(Some(intr as usize));
}
res
}
fn device_type(&self) -> DeviceType {
DeviceType::Intc
}
fn get_id(&self) -> String {
format!("bcm2837_intc")
}
}
impl IntcDriver for Bcm2837Intc {
/// Register interrupt controller local irq
fn register_local_irq(&self, irq: usize, driver: Arc<dyn Driver>) {
// enable irq
use bcm2837::interrupt::Interrupt::*;
let intr = match irq {
_ if irq == Timer1 as usize => Timer1,
_ if irq == Aux as usize => Aux,
_ => todo!(),
};
Controller::new().enable(intr);
let mut manager = self.manager.lock();
manager.register_irq(irq, driver);
}
}
// singleton
lazy_static! {
pub static ref BCM2837_INTC: Arc<Bcm2837Intc> = init();
}
fn init() -> Arc<Bcm2837Intc> {
info!("Init bcm2837 interrupt controller");
let intc = Arc::new(Bcm2837Intc {
manager: Mutex::new(IrqManager::new(false)),
});
DRIVERS.write().push(intc.clone());
// register under root irq manager
// 0x10002: from lower el, irq
IRQ_MANAGER.write().register_irq(0x10002, intc.clone());
// 0x10001: from current el, irq
IRQ_MANAGER.write().register_irq(0x10001, intc.clone());
intc
}
pub fn driver_init() {
lazy_static::initialize(&BCM2837_INTC);
}

View File

@ -0,0 +1,95 @@
use crate::arch::interrupt::enable_irq;
use alloc::collections::btree_map::Entry;
use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use alloc::vec::Vec;
use kernel_hal::drivers::Driver;
#[cfg(feature = "board_raspi3")]
pub mod bcm2837;
pub mod plic;
// Irq manager
pub struct IrqManager {
// is root manager?
root: bool,
// drivers that only respond to specific irq
mapping: BTreeMap<usize, Vec<Arc<dyn Driver>>>,
// drivers that respond to all irqs
all: Vec<Arc<dyn Driver>>,
}
impl IrqManager {
pub fn new(root: bool) -> IrqManager {
IrqManager {
root,
mapping: BTreeMap::new(),
all: Vec::new(),
}
}
pub fn register_irq(&mut self, irq: usize, driver: Arc<dyn Driver>) {
// for root manager, enable irq in arch
// for other interrupt controllers, enable irq before calling this function
if self.root {
enable_irq(irq);
}
match self.mapping.entry(irq) {
Entry::Occupied(mut e) => {
e.get_mut().push(driver);
}
Entry::Vacant(e) => {
let mut v = Vec::new();
v.push(driver);
e.insert(v);
}
}
}
pub fn register_all(&mut self, driver: Arc<dyn Driver>) {
self.all.push(driver);
}
pub fn register_opt(&mut self, irq_opt: Option<usize>, driver: Arc<dyn Driver>) {
if let Some(irq) = irq_opt {
self.register_irq(irq, driver);
} else {
self.register_all(driver);
}
}
pub fn deregister_irq(&mut self, irq: usize, driver: Arc<dyn Driver>) {
if let Some(e) = self.mapping.get_mut(&irq) {
e.retain(|d| !Arc::ptr_eq(&d, &driver));
}
}
pub fn deregister_all(&mut self, driver: Arc<dyn Driver>) {
self.all.retain(|d| !Arc::ptr_eq(&d, &driver));
}
pub fn try_handle_interrupt(&self, irq_opt: Option<usize>) -> bool {
if let Some(irq) = irq_opt {
if let Some(e) = self.mapping.get(&irq) {
for dri in e.iter() {
if dri.try_handle_interrupt(Some(irq)) {
return true;
}
}
}
}
for dri in self.all.iter() {
if dri.try_handle_interrupt(irq_opt) {
return true;
}
}
false
}
}
// interrupt controller
pub trait IntcDriver: Driver {
/// Register interrupt controller local irq
fn register_local_irq(&self, irq: usize, driver: Arc<dyn Driver>);
}

View File

@ -0,0 +1,109 @@
//! RISC-V plic
use super::{super::IRQ_MANAGER, IntcDriver, IrqManager};
use crate::drivers::{device_tree::DEVICE_TREE_INTC, device_tree::DEVICE_TREE_REGISTRY};
use crate::phys_to_virt;
pub use kernel_hal::drivers::{DeviceType, Driver, DRIVERS};
//use crate::{sync::SpinNoIrqLock as Mutex, util::read, util::write};
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use core::ptr::{read_volatile, write_volatile};
use device_tree::Node;
use spin::Mutex;
pub struct Plic {
base: usize,
manager: Mutex<IrqManager>,
}
impl Driver for Plic {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
// Supported more than 32 irqs
/* Int id is pending
let id = 10;
let step = ((id / 32) * 4) as usize; //4节
let pending: u32 = read(self.base + step + 0x1000);
let is_pending = (pending & (1 << id%32)) != 0;
debug!("Plic handle irq, Is {} pending: {}", id, is_pending);
*/
let claim: u32 = read(self.base + 0x201004);
if claim != 0 {
//debug!("Plic handle irq: {}", claim);
let manager = self.manager.lock();
let res = manager.try_handle_interrupt(Some(claim as usize));
// complete
write(self.base + 0x201004, claim);
res
} else {
false
}
}
fn device_type(&self) -> DeviceType {
DeviceType::Intc
}
fn get_id(&self) -> String {
format!("plic_{}", self.base)
}
}
impl IntcDriver for Plic {
/// Register interrupt controller local irq
fn register_local_irq(&self, irq: usize, driver: Arc<dyn Driver>) {
let step = (irq / 32) * 4;
// enable irq for context 1
write(
self.base + step + 0x2080,
read::<u32>(self.base + step + 0x2080) | (1 << irq % 32),
);
// set priority to 7
write(self.base + irq * 4, 7);
let mut manager = self.manager.lock();
manager.register_irq(irq, driver);
}
}
pub const SupervisorExternal: usize = usize::MAX / 2 + 1 + 8;
pub fn init_dt(dt: &Node) {
let addr = dt.prop_u64("reg").unwrap() as usize;
let phandle = dt.prop_u32("phandle").unwrap();
info!("Found riscv plic at {:#x}, {:?}", addr, dt);
let base = phys_to_virt(addr);
let plic = Arc::new(Plic {
base,
manager: Mutex::new(IrqManager::new(false)),
});
// set prio threshold to 0 for context 1
write(base + 0x201000, 0);
DRIVERS.write().push(plic.clone());
// register under root irq manager
IRQ_MANAGER
.write()
.register_irq(SupervisorExternal, plic.clone());
// register interrupt controller. phandle: 3
DEVICE_TREE_INTC.write().insert(phandle, plic);
}
pub fn driver_init() {
DEVICE_TREE_REGISTRY.write().insert("riscv,plic0", init_dt);
}
#[inline(always)]
pub fn write<T>(addr: usize, content: T) {
let cell = (addr) as *mut T;
unsafe {
write_volatile(cell, content);
}
}
#[inline(always)]
pub fn read<T>(addr: usize) -> T {
let cell = (addr) as *const T;
unsafe { read_volatile(cell) }
}

View File

@ -0,0 +1,134 @@
//use crate::sync::Condvar;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use rcore_fs::dev::{self, BlockDevice, DevError};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
use spin::RwLock;
pub use kernel_hal::drivers::{BlockDriver, BLK_DRIVERS};
pub use self::virtio::*;
//pub use block::BlockDriver;
//pub use net::NetDriver;
//pub use rtc::RtcDriver;
pub use serial::SerialDriver;
/// Block device
//pub mod block;
/// Bus controller
//pub mod bus;
/// Character console
//pub mod console;
/// Device tree
pub mod device_tree;
/// Display controller
//pub mod gpu;
/// Mouse device
//pub mod input;
/// Interrupt controller
pub mod irq;
/// Network controller
pub mod net;
/// For isomorphic_drivers
pub mod provider;
/// Real time clock
//pub mod rtc;
/// Serial port
pub mod serial;
/// MMC controller
//pub mod mmc;
/// virtio device
pub mod virtio;
/* define in kernel-hal
#[derive(Debug, Eq, PartialEq)]
pub enum DeviceType {
Net,
Gpu,
Input,
Block,
Rtc,
Serial,
Intc,
}
pub trait Driver: Send + Sync {
// if interrupt belongs to this driver, handle it and return true
// return false otherwise
// irq number is provided when available
// driver should skip handling when irq number is mismatched
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool;
// return the correspondent device type, see DeviceType
fn device_type(&self) -> DeviceType;
// get unique identifier for this device
// should be different for each instance
fn get_id(&self) -> String;
// trait casting
fn as_net(&self) -> Option<&dyn NetDriver> {
None
}
fn as_block(&self) -> Option<&dyn BlockDriver> {
None
}
/*
fn as_rtc(&self) -> Option<&dyn RtcDriver> {
None
}
*/
}
*/
lazy_static! {
// NOTE: RwLock only write when initializing drivers
/* define in kernel-hal
pub static ref DRIVERS: RwLock<Vec<Arc<dyn Driver>>> = RwLock::new(Vec::new());
pub static ref NET_DRIVERS: RwLock<Vec<Arc<dyn NetDriver>>> = RwLock::new(Vec::new());
pub static ref BLK_DRIVERS: RwLock<Vec<Arc<dyn BlockDriver>>> = RwLock::new(Vec::new());
*/
pub static ref INPUT_DRIVERS: RwLock<Vec<Arc<dyn InputDriver>>> = RwLock::new(Vec::new());
pub static ref GPU_DRIVERS: RwLock<Vec<Arc<dyn GpuDriver>>> = RwLock::new(Vec::new());
//pub static ref RTC_DRIVERS: RwLock<Vec<Arc<dyn RtcDriver>>> = RwLock::new(Vec::new());
pub static ref SERIAL_DRIVERS: RwLock<Vec<Arc<dyn SerialDriver>>> = RwLock::new(Vec::new());
pub static ref IRQ_MANAGER: RwLock<irq::IrqManager> = RwLock::new(irq::IrqManager::new(true));
}
pub struct BlockDriverWrapper(pub Arc<dyn BlockDriver>);
impl BlockDevice for BlockDriverWrapper {
const BLOCK_SIZE_LOG2: u8 = 9; // 512
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> dev::Result<()> {
match self.0.read_block(block_id, buf) {
true => Ok(()),
false => Err(DevError),
}
}
fn write_at(&self, block_id: usize, buf: &[u8]) -> dev::Result<()> {
match self.0.write_block(block_id, buf) {
true => Ok(()),
false => Err(DevError),
}
}
fn sync(&self) -> dev::Result<()> {
Ok(())
}
}
/*
lazy_static! {
//pub static ref SOCKET_ACTIVITY: Condvar = Condvar::new();
}
*/
lazy_static! {
// Write only once at boot
pub static ref CMDLINE: RwLock<String> = RwLock::new(String::new());
}

View File

@ -12,35 +12,32 @@ use smoltcp::time::Instant;
use smoltcp::wire::*;
use smoltcp::Result;
use super::ProviderImpl;
use super::PAGE_SIZE;
use crate::net::get_sockets;
use crate::scheme::{NetScheme, Scheme};
use crate::{DeviceError, DeviceResult};
use crate::PAGE_SIZE;
use isomorphic_drivers::net::ethernet::intel::e1000::E1000;
use isomorphic_drivers::net::ethernet::structs::EthernetAddress as DriverEthernetAddress;
use lock::Mutex;
use crate::drivers::{provider::Provider, BlockDriver};
//use crate::sync::SpinNoIrqLock as Mutex;
use spin::Mutex;
use super::super::IRQ_MANAGER;
use kernel_hal::drivers::{DeviceType, Driver, NetDriver, DRIVERS, NET_DRIVERS, SOCKETS};
#[derive(Clone)]
pub struct E1000Driver(Arc<Mutex<E1000<ProviderImpl>>>);
pub struct E1000Driver(Arc<Mutex<E1000<Provider>>>);
#[derive(Clone)]
pub struct E1000Interface {
iface: Arc<Mutex<Interface<'static, E1000Driver>>>,
iface: Mutex<Interface<'static, E1000Driver>>,
driver: E1000Driver,
name: String,
irq: usize,
irq: Option<usize>,
}
impl Scheme for E1000Interface {
fn name(&self) -> &str {
"e1000"
}
fn handle_irq(&self, irq: usize) {
if irq != self.irq {
impl Driver for E1000Interface {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
if irq.is_some() && self.irq.is_some() && irq != self.irq {
// not ours, skip it
return;
return false;
}
let data = self.driver.0.lock().handle_interrupt();
@ -48,23 +45,40 @@ impl Scheme for E1000Interface {
if data {
//let timestamp = Instant::from_millis(crate::trap::uptime_msec() as i64);
// Fix me
let timestamp = Instant::from_millis(0);
let sockets = get_sockets();
let mut sockets = sockets.lock();
let timestamp = Instant::from_millis(100);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(p) => {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
info!("e1000 try_handle_interrupt poll: {:?}", p);
error!("e1000 try_handle_interrupt SOCKET_ACTIVITY unimplemented !");
}
Err(err) => {
warn!("poll got err {}", err);
debug!("poll got err {}", err);
}
}
}
return data;
}
fn device_type(&self) -> DeviceType {
DeviceType::Net
}
fn get_id(&self) -> String {
String::from("e1000")
}
fn as_net(&self) -> Option<&dyn NetDriver> {
Some(self)
}
fn as_block(&self) -> Option<&dyn BlockDriver> {
None
}
}
impl NetScheme for E1000Interface {
impl NetDriver for E1000Interface {
fn get_mac(&self) -> EthernetAddress {
self.iface.lock().ethernet_addr()
}
@ -74,45 +88,52 @@ impl NetScheme for E1000Interface {
}
// get ip addresses
fn get_ip_address(&self) -> Vec<IpCidr> {
fn get_ip_addresses(&self) -> Vec<IpCidr> {
Vec::from(self.iface.lock().ip_addrs())
}
fn poll(&self) -> DeviceResult {
fn ipv4_address(&self) -> Option<Ipv4Address> {
self.iface.lock().ipv4_address()
}
fn poll(&self) {
//let timestamp = Instant::from_millis(crate::trap::uptime_msec() as i64);
let timestamp = Instant::from_millis(0);
let sockets = get_sockets();
let mut sockets = sockets.lock();
let timestamp = Instant::from_millis(100);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(p) => {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
info!("e1000 NetScheme poll: {:?}", p);
Ok(())
error!("e1000 poll SOCKET_ACTIVITY unimplemented !");
}
Err(err) => {
warn!("poll got err {}", err);
Err(DeviceError::IoError)
debug!("poll got err {}", err);
}
}
}
fn recv(&self, buf: &mut [u8]) -> DeviceResult<usize> {
if let Some(vec_recv) = self.driver.0.lock().receive() {
buf.copy_from_slice(&vec_recv);
Ok(vec_recv.len())
fn send(&self, data: &[u8]) -> Option<usize> {
use smoltcp::phy::TxToken;
let token = E1000TxToken(self.driver.clone());
if token
.consume(Instant::from_millis(0), data.len(), |buffer| {
buffer.copy_from_slice(&data);
Ok(())
})
.is_ok()
{
Some(data.len())
} else {
Err(DeviceError::NotReady)
None
}
}
fn send(&self, data: &[u8]) -> DeviceResult<usize> {
if self.driver.0.lock().can_send() {
let mut driver = self.driver.0.lock();
driver.send(data);
Ok(data.len())
} else {
Err(DeviceError::NotReady)
}
fn get_arp(&self, ip: IpAddress) -> Option<EthernetAddress> {
/*
let iface = self.iface.lock();
let cache = iface.neighbor_cache();
cache.lookup(&ip, Instant::from_millis(0))
*/
unimplemented!()
}
}
@ -171,13 +192,7 @@ impl phy::TxToken for E1000TxToken {
}
// JudgeDuck-OS/kern/e1000.c
pub fn init(
name: String,
irq: usize,
header: usize,
size: usize,
index: usize,
) -> DeviceResult<E1000Interface> {
pub fn init(name: String, irq: Option<usize>, header: usize, size: usize, index: usize) {
info!("Probing e1000 {}", name);
// randomly generated
@ -188,31 +203,25 @@ pub fn init(
let net_driver = E1000Driver(Arc::new(Mutex::new(e1000)));
let ethernet_addr = EthernetAddress::from_bytes(&mac);
let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, 2, (15 + index) as u8), 24)];
let default_v4_gw = Ipv4Address::new(10, 0, 2, 2); //Qemu user network gateway: 10.0.2.2
static mut ROUTES_STORAGE: [Option<(IpCidr, Route)>; 1] = [None; 1];
let mut routes = unsafe { Routes::new(&mut ROUTES_STORAGE[..]) };
routes.add_default_ipv4_route(default_v4_gw).unwrap();
//let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, index as u8, 2), 24)];
let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, 2, 15), 24)];
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let iface = InterfaceBuilder::new(net_driver.clone())
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
info!(
"e1000 interface {} up with addr 10.0.2.{}/24",
name,
15 + index
);
info!("e1000 interface {} up with addr 10.0.{}.2/24", name, index);
let e1000_iface = E1000Interface {
iface: Arc::new(Mutex::new(iface)),
driver: net_driver,
iface: Mutex::new(iface),
driver: net_driver.clone(),
name,
irq,
};
Ok(e1000_iface)
let driver = Arc::new(e1000_iface);
DRIVERS.write().push(driver.clone());
IRQ_MANAGER.write().register_opt(irq, driver.clone());
NET_DRIVERS.write().push(driver);
}

View File

@ -0,0 +1,245 @@
//! Intel 10Gb Network Adapter 82599 i.e. ixgbe network driver
//! Datasheet: https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use alloc::collections::BTreeMap;
use isomorphic_drivers::net::ethernet::intel::ixgbe;
use log::*;
use smoltcp::iface::*;
use smoltcp::phy::{self, Checksum, DeviceCapabilities};
use smoltcp::time::Instant;
use smoltcp::wire::EthernetAddress;
use smoltcp::wire::*;
use smoltcp::Result;
//use crate::sync::FlagsGuard;
use spin::Mutex;
use super::super::{provider::Provider, IRQ_MANAGER};
use kernel_hal::drivers::{DeviceType, Driver, NetDriver, DRIVERS, NET_DRIVERS, SOCKETS};
#[derive(Clone)]
struct IXGBEDriver {
inner: Arc<Mutex<ixgbe::IXGBE<Provider>>>,
header: usize,
size: usize,
mtu: usize,
}
pub struct IXGBEInterface {
iface: Mutex<Interface<'static, IXGBEDriver>>,
driver: IXGBEDriver,
ifname: String,
irq: Option<usize>,
id: String,
}
impl Driver for IXGBEInterface {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
if irq.is_some() && self.irq.is_some() && irq != self.irq {
// not ours, skip it
return false;
}
let handled = {
//let _ = FlagsGuard::no_irq_region();
self.driver.inner.lock().try_handle_interrupt()
};
if handled {
//let timestamp = Instant::from_millis(crate::trap::uptime_msec() as i64);
let timestamp = Instant::from_millis(100);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
error!("ixgbe try_handle_interrupt SOCKET_ACTIVITY unimplemented !");
}
Err(err) => {
debug!("poll got err {}", err);
}
}
}
return handled;
}
fn device_type(&self) -> DeviceType {
DeviceType::Net
}
fn get_id(&self) -> String {
self.ifname.clone()
}
fn as_net(&self) -> Option<&dyn NetDriver> {
Some(self)
}
/*
fn as_block(&self) -> Option<&dyn BlockDriver> {
None
}
*/
}
impl NetDriver for IXGBEInterface {
fn get_mac(&self) -> EthernetAddress {
self.iface.lock().ethernet_addr()
}
fn get_ifname(&self) -> String {
self.ifname.clone()
}
// get ip addresses
fn get_ip_addresses(&self) -> Vec<IpCidr> {
Vec::from(self.iface.lock().ip_addrs())
}
fn ipv4_address(&self) -> Option<Ipv4Address> {
self.iface.lock().ipv4_address()
}
fn poll(&self) {
//let timestamp = Instant::from_millis(crate::trap::uptime_msec() as i64);
let timestamp = Instant::from_millis(100);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
error!("ixgbe poll SOCKET_ACTIVITY unimplemented !");
}
Err(err) => {
debug!("poll got err {}", err);
}
}
}
fn send(&self, data: &[u8]) -> Option<usize> {
self.driver.inner.lock().send(&data);
Some(data.len())
}
fn get_arp(&self, ip: IpAddress) -> Option<EthernetAddress> {
/*
let iface = self.iface.lock();
let cache = iface.neighbor_cache();
cache.lookup_pure(&ip, Instant::from_millis(0))
*/
unimplemented!()
}
}
pub struct IXGBERxToken(Vec<u8>);
pub struct IXGBETxToken(IXGBEDriver);
impl<'a> phy::Device<'a> for IXGBEDriver {
type RxToken = IXGBERxToken;
type TxToken = IXGBETxToken;
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
//let _ = FlagsGuard::no_irq_region();
if self.inner.lock().can_send() {
if let Some(data) = self.inner.lock().recv() {
Some((IXGBERxToken(data), IXGBETxToken(self.clone())))
} else {
None
}
} else {
None
}
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
//let _ = FlagsGuard::no_irq_region();
if self.inner.lock().can_send() {
Some(IXGBETxToken(self.clone()))
} else {
None
}
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
// do not use max MTU by default
//caps.max_transmission_unit = ixgbe::IXGBEDriver::get_mtu(); // max MTU
caps.max_transmission_unit = self.mtu;
caps.max_burst_size = Some(256);
// IP Rx checksum is offloaded with RXCSUM
caps.checksum.ipv4 = Checksum::Tx;
caps
}
}
impl phy::RxToken for IXGBERxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
f(&mut self.0)
}
}
impl phy::TxToken for IXGBETxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
//let _ = FlagsGuard::no_irq_region();
let mut buffer = [0u8; ixgbe::IXGBE::<Provider>::get_mtu()];
let result = f(&mut buffer[..len]);
if result.is_ok() {
self.0.inner.lock().send(&buffer[..len]);
}
result
}
}
pub fn ixgbe_init(
name: String,
irq: Option<usize>,
header: usize,
size: usize,
index: usize,
) -> Arc<IXGBEInterface> {
//let _ = FlagsGuard::no_irq_region();
let mut ixgbe = ixgbe::IXGBE::new(header, size);
ixgbe.enable_irq();
let ethernet_addr = EthernetAddress::from_bytes(&ixgbe.get_mac().as_bytes());
let net_driver = IXGBEDriver {
inner: Arc::new(Mutex::new(ixgbe)),
header,
size,
mtu: 1500,
};
//let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, index as u8, 2), 24)];
let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, 2, 15), 24)];
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let iface = InterfaceBuilder::new(net_driver.clone())
.ethernet_addr(ethernet_addr)
.ip_addrs(ip_addrs)
.neighbor_cache(neighbor_cache)
.finalize();
info!("ixgbe interface {} up with addr 10.0.{}.2/24", name, index);
let ixgbe_iface = IXGBEInterface {
iface: Mutex::new(iface),
driver: net_driver.clone(),
ifname: name.clone(),
id: name,
irq,
};
let driver = Arc::new(ixgbe_iface);
IRQ_MANAGER.write().register_opt(irq, driver.clone());
DRIVERS.write().push(driver.clone());
NET_DRIVERS.write().push(driver.clone());
driver
}

View File

@ -0,0 +1,119 @@
// smoltcp
use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Route, Routes};
use smoltcp::phy::{Loopback, Medium};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use alloc::sync::Arc;
use kernel_hal::drivers::{DeviceType, Driver, NetDriver, NET_DRIVERS, SOCKETS};
use alloc::string::String;
use spin::Mutex;
#[derive(Clone)]
pub struct LoopbackInterface {
pub iface: Arc<Mutex<Interface<'static, Loopback>>>,
pub name: String,
}
impl Driver for LoopbackInterface {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
return false;
}
fn device_type(&self) -> DeviceType {
DeviceType::Net
}
fn get_id(&self) -> String {
String::from("loopback")
}
fn as_net(&self) -> Option<&dyn NetDriver> {
Some(self)
}
}
impl NetDriver for LoopbackInterface {
fn get_mac(&self) -> EthernetAddress {
self.iface.lock().ethernet_addr()
}
// get ip addresses
fn get_ip_addresses(&self) -> Vec<IpCidr> {
unimplemented!()
}
fn ipv4_address(&self) -> Option<Ipv4Address> {
self.iface.lock().ipv4_address()
}
fn poll(&self) {
let timestamp = Instant::from_millis(0);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => {}
Err(err) => {
debug!("poll got err {}", err);
}
}
}
fn send(&self, data: &[u8]) -> Option<usize> {
unimplemented!()
}
fn get_arp(&self, ip: IpAddress) -> Option<EthernetAddress> {
/*
let iface = self.iface.lock();
let cache = iface.neighbor_cache();
cache.lookup(&ip, Instant::from_millis(0))
*/
unimplemented!()
}
}
pub fn init(name: String) {
warn!("loopback");
// 初始化 一个 协议栈
// 从外界 接受 一些 配置 参数 如果 没有 选择 默认 的
// 网络 设备
// 默认 loopback
let loopback = Loopback::new(Medium::Ethernet);
// 为 设备 分配 网络 身份
// 物理地址
let mac: [u8; 6] = [0x52, 0x54, 0x98, 0x76, 0x54, 0x32];
let ethernet_addr = EthernetAddress::from_bytes(&mac);
// ip 地址
let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 24)];
// let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, 2, 15), 24)];
// 路由
let default_gateway = Ipv4Address::new(127, 0, 0, 1);
// let default_gateway = Ipv4Address::new(10, 0, 2, 2);
static mut routes_storage: [Option<(IpCidr, Route)>; 1] = [None; 1];
let mut routes = unsafe { Routes::new(&mut routes_storage[..]) };
routes.add_default_ipv4_route(default_gateway).unwrap();
// arp缓存
let neighbor_cache = NeighborCache::new(BTreeMap::new());
// 设置 主要 设置 iface
let iface = InterfaceBuilder::new(loopback)
.ethernet_addr(ethernet_addr)
.ip_addrs(ip_addrs)
.routes(routes)
.neighbor_cache(neighbor_cache)
.finalize();
let loopback_iface = LoopbackInterface {
iface: Arc::new(Mutex::new(iface)),
name,
};
let driver = Arc::new(loopback_iface);
NET_DRIVERS.write().push(driver);
}

View File

@ -0,0 +1,8 @@
pub mod e1000;
pub mod ixgbe;
pub mod rtl8x;
pub mod virtio_net;
pub mod realtek;
pub mod loopback;

View File

@ -0,0 +1,41 @@
pub mod mii;
//pub mod rtl8211f;
use crate::{phys_to_virt, virt_to_phys};
use isomorphic_drivers::provider::Provider;
#[macro_use]
mod log {
macro_rules! trace {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
macro_rules! debug {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
macro_rules! info {
($($arg:expr),*) => { $( let _ = $arg; )*};
}
macro_rules! warn {
($($arg:expr),*) => { $( let _ = $arg; )*};
}
macro_rules! error {
($($arg:expr),*) => { $( let _ = $arg; )* };
}
}
pub mod rtl8211f;
/*
/// External functions that drivers must use
pub trait Provider {
/// Page size (usually 4K)
const PAGE_SIZE: usize;
/// Allocate consequent physical memory for DMA.
/// Return (`virtual address`, `physical address`).
/// The address is page aligned.
fn alloc_dma(size: usize) -> (usize, usize);
/// Deallocate DMA
fn dealloc_dma(vaddr: usize, size: usize);
}
*/

View File

@ -3,30 +3,38 @@
extern crate alloc;
use super::mii::*;
use super::utils::*;
use crate::arch::cpu_C906::{fence_w, flush_cache, invalidate_dcache};
use core::marker::PhantomData;
use core::mem::size_of;
use super::Provider;
use super::{phys_to_virt, virt_to_phys};
use super::{phys_to_virt, virt_to_phys, Provider};
use alloc::boxed::Box;
use alloc::slice;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::{bare_print, putfmt};
//use super::{error, warn, info, debug};
/*
use conquer_once::spin::OnceCell;
use spin::RwLock;
*/
// RTL8211F
pub const GMAC_BASE: u32 = 0x04500000;
pub const CCU_BASE: u32 = 0x02001000;
pub const SYS_CFG_BASE: u32 = 0x03000000;
pub const PINCTRL_GPIO_BASE: u32 = 0x02000000;
const GMAC_BASE: u32 = 0x04500000;
const CCU_BASE: u32 = 0x02001000;
const SYS_CFG_BASE: u32 = 0x03000000;
const EMAC_BGR_REG: u32 = 0x097C; // CCU
const EMAC_25M_CLK_REG: u32 = 0x0970;
const EMAC_EPHY_CLK_REG0: u32 = 0x30; // SYS_CFG
const PINCTRL_GPIO_BASE: u32 = 0x02000000;
// mac addr 3a:c5:31:d5:de:88
const MAC_ADDR: &str = "3a:c5:31:d5:de:88";
@ -124,8 +132,8 @@ const SF_DMA_MODE: usize = 1;
* - 2: Tx Flow
* - 3: Rx & Tx Flow
*/
const FLOW_CTRL: u32 = 0;
const PAUSE: u32 = 0x400;
const Flow_Ctrl: u32 = 0;
const Pause: u32 = 0x400;
/* Enable or disable autonegotiation. */
const AUTONEG_DISABLE: usize = 0;
@ -181,6 +189,13 @@ pub enum tx_dma_irq_status {
handle_tx_rx = 3,
}
/*
static RTL8139_STATE: OnceCell<Arc<RwLock<Rtl8139State>>> = OnceCell::uninit();
#[cfg(feature = "async")]
static WAKER: AtomicWaker = AtomicWaker::new();
*/
pub struct RTL8211F<P: Provider> {
base: u32, // 0x4500000
base_ccu: u32, // CCU_BASE
@ -229,17 +244,22 @@ where
(v_addr == 0x5fa)
// mac addr is broadcast
{
let tokens: Vec<&str> = MAC_ADDR.split(':').collect();
let tokens: Vec<&str> = MAC_ADDR.split(":").collect();
for (i, s) in tokens.iter().enumerate() {
mac[i] = u8::from_str_radix(s, 16).unwrap();
}
} else {
mac = *mac_addr;
mac = mac_addr.clone();
}
info!(
"mac addr: {:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
bare_print!(
"mac addr: {:x}:{:x}:{:x}:{:x}:{:x}:{:x}\n",
mac[0],
mac[1],
mac[2],
mac[3],
mac[4],
mac[5]
);
// DMA使用的dma_desc内存有一致性要求一般非cache的
@ -639,7 +659,7 @@ where
// 而没网线Link时, 不进行下列设置: PHY state change UP -> NOLINK
if link != 0 {
if pause != 0 {
self.flow_ctrl(duplex, FLOW_CTRL, PAUSE);
self.flow_ctrl(duplex, Flow_Ctrl, Pause);
}
self.set_link_mode(duplex, speed);
@ -767,9 +787,8 @@ where
}
info!(
"RX DMA State: {:#x}, recv packets: {}",
read_volatile((self.base + GETH_RX_DMA_STA) as *mut u32),
rx_packets
"RX DMA State: {:#x}",
read_volatile((self.base + GETH_RX_DMA_STA) as *mut u32)
);
if rxcount > 0 {
@ -980,12 +999,7 @@ where
entry = self.tx_clean;
let mut desc = &mut self.send_ring[entry];
invalidate_dcache(
virt_to_phys(desc as *const dma_desc as usize) as u64,
size_of::<dma_desc>() as u64,
);
if desc_get_own(desc) != 0 {
warn!("tx_complete get desc own failed !");
break;
}
@ -1004,18 +1018,11 @@ where
// dma_unmap_single
//self.send_buffers[entry], clear 2k
unsafe {
slice::from_raw_parts_mut(self.send_buffers[entry] as *mut u8, 2048).fill(0);
}
flush_cache(virt_to_phys(self.send_buffers[entry]) as u64, 2048);
//self.send_buffers[entry] = 0;
// 注意不要把desc2的Buffer Addr清零了
desc_init(desc);
self.tx_clean = (entry + 1) % DMA_DESC_TX;
}
debug!("send packets: {}, send errors: {}", tx_packets, tx_errors);
}
// Enable and Restart Autonegotiation
@ -1028,7 +1035,7 @@ where
let phyaddr = 0;
let mut autoneg_complete: u32 = 0;
loop {
while true {
// Read link and autonegotiation status
let status = self.mdio_read(phyaddr, MII_BMSR);
autoneg_complete = status & BMSR_ANEGCOMPLETE;
@ -1142,9 +1149,10 @@ where
}
/* 正常的 TX/RX NORMAL interrupts */
// (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) != 0
if (intr_status & (TX_INT | RX_INT)) != 0 {
status = tx_dma_irq_status::handle_tx_rx as i32;
if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) != 0 {
if (intr_status & (TX_INT | RX_INT)) != 0 {
status = tx_dma_irq_status::handle_tx_rx as i32;
}
}
/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
write_volatile((self.base + GETH_INT_STA) as *mut u32, intr_status & 0x3FFF);
@ -1199,7 +1207,7 @@ where
(*desc).desc1 |= 0b11 << 27;
desc = desc.add(1);
}
count += 1;
count = count + 1;
if count > end {
break;
@ -1220,7 +1228,6 @@ where
pub fn dma_init(&mut self) {
write_volatile((self.base + GETH_BASIC_CTL1) as *mut u32, (8 << 24)); // burst
// 打开网卡中断
self.int_enable();
}
@ -1382,9 +1389,8 @@ where
}
pub fn pinctrl_gpio_set(&mut self, offset: u32, value: u32) {
// 0x0 <= offset <= 0x0350
assert!(
offset <= 0x0350,
0x0 <= offset && offset <= 0x0350,
"Invalid gpio register offset: {:#x}",
offset
);
@ -1431,7 +1437,7 @@ where
match speed {
1000 => ctrl &= !0x0C,
_ => {
100 | 10 | _ => {
ctrl |= 0x08;
if (speed == 100) {
ctrl |= 0x04;
@ -1549,9 +1555,6 @@ pub fn desc_buf_set(desc: &mut dma_desc, paddr: u32, size: u32) {
pub fn desc_init(desc: &mut dma_desc) {
desc.desc1 = 0;
desc.desc1 |= (1 << 24);
// 这里用的Buffer Addr不发生改变
//desc.desc2 = 0;
}
fn read_volatile<T>(src: *const T) -> T {
@ -1564,4 +1567,63 @@ fn write_volatile<T>(dst: *mut T, value: T) {
}
}
pub fn print_hex_dump(buf: &[u8], len: usize) {}
pub fn print_hex_dump(buf: &[u8], len: usize) {
return;
//let mut linebuf: [char; 16] = [0 as char; 16];
let mut linebuf = String::with_capacity(32);
let buf_len = buf.len();
for i in 0..len {
if (i % 16) == 0 {
bare_print!("\t{:?}\nHEX DUMP: ", linebuf);
//linebuf.fill(0 as char);
linebuf.clear();
}
if i >= buf_len {
bare_print!(" {:02x}", 0);
} else {
bare_print!(" {:02x}", buf[i]);
//linebuf[i%16] = buf[i] as char;
linebuf.push(buf[i] as char);
}
}
bare_print!("\t{:?}\n", linebuf);
}
// For testing
pub fn init() {
//EAPOL packet: 0007326b9940 ca9027e0a80f 888e020000050104000501
let frame: Box<[u8]> = Box::new([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56, 0x88, 0x8e, 0x02,
0x00, 0x00, 0x05, 0x01, 0x04, 0x00, 0x05, 0x01,
]);
let mac: [u8; 6] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
//let mut rtl8211f = RTL8211F::<Provider>::new(&mac);
let mut rtl8211f = RTL8211F::<crate::drivers::provider::Provider>::new(&mac);
unsafe {
rtl8211f.open();
rtl8211f.set_rx_mode();
rtl8211f.adjust_link();
//开始接收和发送数据
}
debug!("cycle start :::");
let c = crate::arch::get_cycle();
while crate::arch::get_cycle() < c + 100_000_000 {
let (buffer, work_done) = rtl8211f.geth_recv(BUDGET);
if !buffer.is_empty() {
print_hex_dump(&buffer, 32);
}
}
debug!("Send two network frames :::");
rtl8211f.geth_send(&frame);
rtl8211f.geth_send(&frame);
loop {}
}

View File

@ -0,0 +1,219 @@
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;
use smoltcp::iface::*;
use smoltcp::phy::{self, Device, DeviceCapabilities, Medium};
use smoltcp::time::Instant;
use smoltcp::wire::*;
use smoltcp::Result;
use super::super::IRQ_MANAGER;
use super::realtek::rtl8211f::RTL8211F;
use crate::drivers::provider::Provider;
use crate::PAGE_SIZE;
use kernel_hal::drivers::{DeviceType, Driver, NetDriver, DRIVERS, NET_DRIVERS, SOCKETS};
#[derive(Clone)]
pub struct RTL8xDriver(Arc<Mutex<RTL8211F<Provider>>>);
#[derive(Clone)]
pub struct RTL8xInterface {
pub iface: Arc<Mutex<Interface<'static, RTL8xDriver>>>,
pub driver: RTL8xDriver,
pub name: String,
pub irq: Option<usize>,
}
impl Driver for RTL8xInterface {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
if irq.is_some() && self.irq.is_some() && irq != self.irq {
// not ours, skip it
return false;
}
let status = self.driver.0.lock().interrupt_status();
let handle_tx_rx = 3;
if status == handle_tx_rx {
let timestamp = Instant::from_millis(0);
let mut sockets = SOCKETS.lock(); //引发死锁?
self.driver.0.lock().int_disable();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
// error!("rtl8x try_handle_interrupt SOCKET_ACTIVITY unimplemented !");
}
Err(err) => {
debug!("poll got err {}", err);
}
}
self.driver.0.lock().int_enable();
return true;
}
return false;
}
fn device_type(&self) -> DeviceType {
DeviceType::Net
}
fn get_id(&self) -> String {
String::from("e1000")
}
fn as_net(&self) -> Option<&dyn NetDriver> {
Some(self)
}
}
impl NetDriver for RTL8xInterface {
fn get_mac(&self) -> EthernetAddress {
self.iface.lock().ethernet_addr()
}
fn get_ifname(&self) -> String {
self.name.clone()
}
// get ip addresses
fn get_ip_addresses(&self) -> Vec<IpCidr> {
Vec::from(self.iface.lock().ip_addrs())
}
fn ipv4_address(&self) -> Option<Ipv4Address> {
self.iface.lock().ipv4_address()
}
fn poll(&self) {
let timestamp = Instant::from_millis(0);
let mut sockets = SOCKETS.lock();
match self.iface.lock().poll(&mut sockets, timestamp) {
Ok(_) => {
//SOCKET_ACTIVITY.notify_all();
// error!("poll change : {}!", b);
}
Err(err) => {
debug!("poll got err {}", err);
}
}
}
fn send(&self, data: &[u8]) -> Option<usize> {
self.driver.0.lock().geth_send(&data);
Some(data.len())
}
fn get_arp(&self, ip: IpAddress) -> Option<EthernetAddress> {
/*
let iface = self.iface.lock();
let cache = iface.neighbor_cache();
cache.lookup(&ip, Instant::from_millis(0))
*/
unimplemented!()
}
}
pub struct RTL8xRxToken(Vec<u8>);
pub struct RTL8xTxToken(RTL8xDriver);
impl<'a> Device<'a> for RTL8xDriver {
type RxToken = RTL8xRxToken;
type TxToken = RTL8xTxToken;
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(64);
caps.medium = Medium::Ethernet;
caps
}
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
if self.0.lock().can_recv() {
//这里每次只接收一个网络包
let (vec_recv, rxcount) = self.0.lock().geth_recv(1);
Some((RTL8xRxToken(vec_recv), RTL8xTxToken(self.clone())))
} else {
None
}
}
fn transmit(&mut self) -> Option<Self::TxToken> {
if self.0.lock().can_send() {
Some(RTL8xTxToken(self.clone()))
} else {
None
}
}
}
impl phy::RxToken for RTL8xRxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
// warn!("rx consume {:?}",self.0);
f(&mut self.0)
}
}
impl phy::TxToken for RTL8xTxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
use alloc::vec;
let mut buffer = vec![0u8; len];
let result = f(&mut buffer[..len]);
if result.is_ok() {
(self.0).0.lock().geth_send(&buffer[..len]);
}
result
}
}
pub fn init(name: String, irq: Option<usize>) {
let mut rtl8211f = RTL8211F::<Provider>::new(&[0u8; 6]);
let mac = rtl8211f.get_umac();
//启动前请为D1插上网线
warn!("Please plug in the Ethernet cable");
rtl8211f.open();
rtl8211f.set_rx_mode();
rtl8211f.adjust_link();
let net_driver = RTL8xDriver(Arc::new(Mutex::new(rtl8211f)));
let ethernet_addr = EthernetAddress::from_bytes(&mac);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 100, 1, 5), 24)];
let default_v4_gw = Ipv4Address::new(192, 100, 1, 1);
warn!("gate way {:?}", default_v4_gw);
#[allow(warnings)]
static mut routes_storage: [Option<(IpCidr, Route)>; 1] = [None; 1];
let mut routes = unsafe { Routes::new(&mut routes_storage[..]) };
routes.add_default_ipv4_route(default_v4_gw).unwrap();
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let iface = InterfaceBuilder::new(net_driver.clone())
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
info!("rtl8211f interface {} up with addr 192.100.1.5/24", name);
let rtl8211f_iface = RTL8xInterface {
iface: Arc::new(Mutex::new(iface)),
driver: net_driver.clone(),
name,
irq,
};
let driver = Arc::new(rtl8211f_iface);
DRIVERS.write().push(driver.clone());
IRQ_MANAGER.write().register_opt(irq, driver.clone());
NET_DRIVERS.write().push(driver);
}

View File

@ -0,0 +1,191 @@
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use device_tree::Node;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{self, DeviceCapabilities};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
use smoltcp::Result;
use virtio_drivers::{VirtIOHeader, VirtIONet};
use super::super::{device_tree::DEVICE_TREE_INTC, IRQ_MANAGER};
use kernel_hal::drivers::{DeviceType, Driver, NetDriver, DRIVERS, NET_DRIVERS};
//use crate::{drivers::BlockDriver, sync::SpinNoIrqLock as Mutex};
use spin::Mutex;
#[derive(Clone)]
pub struct VirtIONetDriver(Arc<Mutex<VirtIONet<'static>>>);
impl NetDriver for VirtIONetDriver {
fn get_mac(&self) -> EthernetAddress {
EthernetAddress(self.0.lock().mac())
}
fn get_ifname(&self) -> String {
format!("virtio{:?}", self.0.lock().mac())
}
fn ipv4_address(&self) -> Option<Ipv4Address> {
unimplemented!()
}
fn poll(&self) {
unimplemented!()
}
}
impl Driver for VirtIONetDriver {
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool {
info!("VirtIONetDriver got interrupt {:?}", irq);
//iface.poll()时中断内发生死锁,暂关闭该中断处理
self.0.lock().ack_interrupt()
}
fn device_type(&self) -> DeviceType {
DeviceType::Net
}
fn get_id(&self) -> String {
format!("virtio_net")
}
fn as_net(&self) -> Option<&dyn NetDriver> {
Some(self)
}
/*
fn as_block(&self) -> Option<&dyn BlockDriver> {
None
}
*/
}
impl phy::Device<'_> for VirtIONetDriver {
type RxToken = VirtIONetDriver;
type TxToken = VirtIONetDriver;
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
/*
let net = self.0.lock();
let r = net.can_recv();
*/
//初始时由于没有添加recv_queue和写queue_notify
//故can_recv()会一直返回false
//这里的判断等待包的过程转移到consume()的driver.recv()中去做吧
//当然最好的方式应该在此调用driver.recv()
if true {
Some((self.clone(), self.clone()))
} else {
None
}
}
fn transmit(&mut self) -> Option<Self::TxToken> {
let net = self.0.lock();
if net.can_send() {
info!("phy::Device transmit");
Some(self.clone())
} else {
None
}
}
fn capabilities(&self) -> DeviceCapabilities {
//info!("phy::Device capabilities()");
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps
}
}
impl phy::RxToken for VirtIONetDriver {
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
info!("RxToken recv consume()");
let mut buffer = [0u8; 2000];
let mut len = buffer.len();
{
//若无括号会与TxToken consume中的lock()发生死锁
let mut driver = self.0.lock();
//需要添加recv_queue和写queue_notify才能触发virtioNet网卡中断一次?
//这里有等待总能收到包TODO: fix me
len = driver.recv(&mut buffer).expect("failed to recv packet");
}
f(&mut buffer[..len])
}
}
impl phy::TxToken for VirtIONetDriver {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
info!("TxToken send consume()");
let mut buffer = [0u8; 2000];
let result = f(&mut buffer[..len]);
//发生死锁
let mut driver = self.0.lock();
driver.send(&buffer[..len]).expect("failed to send packet");
result
}
}
pub fn init(node: &Node, header: &'static mut VirtIOHeader) {
debug!("virtio net init");
let net = VirtIONet::new(header).expect("failed to create net driver");
//let mac = net.mac();
let device = VirtIONetDriver(Arc::new(Mutex::new(net)));
/* Todo like e1000
// let device = Loopback::new(Medium::Ethernet);
let hw_addr = EthernetAddress::from_bytes(&mac);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, 2, 15), 24)];
let iface = InterfaceBuilder::new(device.clone())
.ethernet_addr(hw_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
*/
//let driver = Arc::new(iface);
let driver = Arc::new(device);
let mut found = false;
let irq_opt = node.prop_u32("interrupts").ok().map(|irq| irq as usize);
if let Ok(intc) = node.prop_u32("interrupt-parent") {
if let Some(irq) = irq_opt {
if let Some(manager) = DEVICE_TREE_INTC.write().get_mut(&intc) {
manager.register_local_irq(irq, driver.clone());
info!("Registed virtio net irq {} to INTC", irq);
found = true;
}
}
}
if !found {
info!("Registed virtio net driver to ROOT");
IRQ_MANAGER.write().register_opt(irq_opt, driver.clone());
}
/*
{
let mut buffer = [0u8; 2000];
let mut dri = driver.0.lock();
//触发virtioNet网卡中断一次。 不过现在中断hanlde会死锁
let len = dri.recv(&mut buffer).expect("failed to recv packet");
}
*/
DRIVERS.write().push(driver.clone());
NET_DRIVERS.write().push(driver);
}

View File

@ -0,0 +1,57 @@
use crate::{frame_dealloc, hal_frame_alloc_contiguous, phys_to_virt, virt_to_phys, PAGE_SIZE};
use isomorphic_drivers::provider;
pub struct Provider;
impl provider::Provider for Provider {
const PAGE_SIZE: usize = PAGE_SIZE;
fn alloc_dma(size: usize) -> (usize, usize) {
let paddr = virtio_dma_alloc(size / PAGE_SIZE);
let vaddr = phys_to_virt(paddr);
(vaddr, paddr)
}
fn dealloc_dma(vaddr: usize, size: usize) {
let paddr = virt_to_phys(vaddr);
for i in 0..size / PAGE_SIZE {
unsafe {
frame_dealloc(&(paddr + i * PAGE_SIZE));
}
//dealloc_frame(paddr + i * PAGE_SIZE);
}
}
}
#[no_mangle]
extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
//let paddr = alloc_frame_contiguous(pages, 0).unwrap();
let paddr = unsafe { hal_frame_alloc_contiguous(pages, 0).unwrap() };
trace!("alloc DMA: paddr={:#x}, pages={}", paddr, pages);
paddr
}
#[no_mangle]
extern "C" fn virtio_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
for i in 0..pages {
unsafe {
frame_dealloc(&(paddr + i * PAGE_SIZE));
}
//dealloc_frame(paddr + i * PAGE_SIZE);
}
trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages);
0
}
#[no_mangle]
extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
phys_to_virt(paddr)
}
#[no_mangle]
extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
virt_to_phys(vaddr)
}
type VirtAddr = usize;
type PhysAddr = usize;

Some files were not shown because too many files have changed in this diff Show More