forked from rcore-os/zCore
Compare commits
1 Commits
master
...
simple-vmo
| Author | SHA1 | Date |
|---|---|---|
|
|
d004ae2ac2 |
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y $@
|
||||
pip3 install -r tests/requirements.txt
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
name: Deploy docs
|
||||
|
||||
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 }}
|
||||
|
||||
- name: Build docs
|
||||
run: |
|
||||
cargo doc --no-deps --all-features
|
||||
.github/scripts/add-doc-index.sh
|
||||
|
||||
- name: Deploy to Github Pages
|
||||
if: ${{ github.ref == 'refs/heads/master' }}
|
||||
uses: JamesIves/github-pages-deploy-action@releases/v3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: gh-pages
|
||||
FOLDER: target/doc
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2021-03-01
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Check code format
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-features
|
||||
|
||||
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-03-01
|
||||
components: rust-src
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
- name: Build zCore
|
||||
run: |
|
||||
cd zCore
|
||||
make build
|
||||
- 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-03-01
|
||||
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-03-01
|
||||
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
|
||||
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
|
||||
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-03-01
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -1,18 +1,8 @@
|
|||
**/.*
|
||||
!.github/
|
||||
!.cargo/
|
||||
!.vscode/settings.json
|
||||
|
||||
/target
|
||||
**/target
|
||||
**/*.rs.bk
|
||||
*.img
|
||||
*.bin
|
||||
*.log
|
||||
Cargo.lock
|
||||
|
||||
/ignored
|
||||
/rootfs
|
||||
zCore/src/platform/riscv/kernel-vars.ld
|
||||
|
||||
.DS_Store
|
||||
__pycache__
|
||||
/prebuilt/linux/alpine*
|
||||
.idea
|
||||
scripts/linux/test-result.txt
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
18
Cargo.toml
18
Cargo.toml
|
|
@ -1,17 +1,17 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"drivers",
|
||||
"kernel-hal",
|
||||
"zircon-object",
|
||||
"zircon-syscall",
|
||||
"zircon-loader",
|
||||
"linux-object",
|
||||
"linux-syscall",
|
||||
"loader",
|
||||
"zCore",
|
||||
"xtask",
|
||||
"linux-loader",
|
||||
"kernel-hal-unix",
|
||||
"kernel-hal",
|
||||
]
|
||||
default-members = ["xtask"]
|
||||
exclude = ["zircon-user", "rboot"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
exclude = [
|
||||
"zircon-user",
|
||||
"zCore",
|
||||
"rboot",
|
||||
]
|
||||
|
|
|
|||
87
Makefile
87
Makefile
|
|
@ -1,53 +1,62 @@
|
|||
# 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)
|
||||
|
||||
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)
|
||||
rootfs: prebuilt/linux/$(ROOTFS_TAR)
|
||||
rm -rf rootfs && mkdir -p rootfs
|
||||
tar xf $< -C rootfs
|
||||
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
|
||||
|
||||
# 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
|
||||
@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
|
||||
@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) +50M
|
||||
|
||||
# build and open project document
|
||||
doc:
|
||||
cargo doc --open
|
||||
|
||||
# clean targets
|
||||
clean:
|
||||
cargo clean
|
||||
rm -rf rootfs
|
||||
rm -rf ignored/target
|
||||
find zCore -maxdepth 1 -name "*.img" -delete
|
||||
|
||||
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.
|
||||
rv64-image: rcore-fs-fuse
|
||||
@echo building riscv64.img
|
||||
@rm -rf rootfs
|
||||
@mkdir rootfs
|
||||
@mkdir rootfs/bin
|
||||
ifeq ($(wildcard prebuilt/linux/riscv64/busybox),)
|
||||
@mkdir -p prebuilt/linux/riscv64
|
||||
@wget https://github.com/rcore-os/busybox-prebuilts/raw/master/busybox-1.30.1-riscv64/busybox -O prebuilt/linux/riscv64/busybox
|
||||
endif
|
||||
@cp prebuilt/linux/riscv64/busybox rootfs/bin/
|
||||
@@rcore-fs-fuse riscv64.img rootfs zip
|
||||
@qemu-img resize riscv64.img +50M
|
||||
|
|
|
|||
235
README.md
235
README.md
|
|
@ -1,42 +1,24 @@
|
|||
# zCore
|
||||
|
||||
[](https://github.com/rcore-os/zCore/actions)
|
||||
[](https://rcore-os.github.io/zCore/)
|
||||
[](https://rcore-os.github.io/zCore/zircon_object/)
|
||||
[](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
|
||||
|
||||
- 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
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
Environments:
|
||||
|
||||
- [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
|
||||
* [Rust toolchain](http://rustup.rs)
|
||||
* [QEMU](https://www.qemu.org)
|
||||
* [Git LFS](https://git-lfs.github.com)
|
||||
|
||||
Clone repo and pull prebuilt fuchsia images:
|
||||
|
||||
|
|
@ -53,117 +35,55 @@ For users in China, there's a mirror you can try:
|
|||
git clone https://github.com.cnpmjs.org/rcore-os/zCore --recursive
|
||||
```
|
||||
|
||||
### Run zcore in libos mode
|
||||
Prepare Alpine Linux rootfs:
|
||||
|
||||
#### Run zcore in linux-libos mode
|
||||
```sh
|
||||
make rootfs
|
||||
```
|
||||
|
||||
- step 1: Prepare Alpine Linux rootfs:
|
||||
Run native Linux program (Busybox):
|
||||
|
||||
```sh
|
||||
make rootfs
|
||||
```
|
||||
```sh
|
||||
cargo run --release -p linux-loader -- /bin/busybox [args]
|
||||
```
|
||||
|
||||
- step 2: Compile & Run native Linux program (Busybox) in libos mode:
|
||||
Run native Zircon program (shell):
|
||||
|
||||
```sh
|
||||
cargo run --release --features "linux libos" -- /bin/busybox [args]
|
||||
```
|
||||
```sh
|
||||
cargo run --release -p zircon-loader -- prebuilt/zircon/x64
|
||||
```
|
||||
|
||||
You can also add the feature `graphic` to show the graphical output (with [sdl2](https://www.libsdl.org) installed).
|
||||
Run Linux shell on bare-metal (zCore):
|
||||
|
||||
To debug, set the `LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
|
||||
```sh
|
||||
make image
|
||||
cd zCore && make run mode=release linux=1 [graphic=on] [accel=1]
|
||||
```
|
||||
|
||||
#### Run native Zircon program (shell) in zircon-libos mode:
|
||||
Run Zircon on bare-metal (zCore):
|
||||
|
||||
- step 1: Compile and Run Zircon shell
|
||||
```sh
|
||||
cd zCore && make run mode=release [graphic=on] [accel=1]
|
||||
```
|
||||
|
||||
```sh
|
||||
cargo run --release --features "zircon libos" -- prebuilt/zircon/x64/bringup.zbi
|
||||
```
|
||||
Build and run your own Zircon user programs:
|
||||
|
||||
The `graphic` and `LOG` options are the same as Linux.
|
||||
```sh
|
||||
# See template in zircon-user
|
||||
cd zircon-user && make zbi mode=release
|
||||
|
||||
### Run zcore in bare-metal mode
|
||||
# Run your programs in zCore
|
||||
cd zCore && make run mode=release user=1
|
||||
```
|
||||
|
||||
#### Run Linux shell in linux-bare-metal mode:
|
||||
|
||||
- step 1: Prepare Alpine Linux rootfs:
|
||||
|
||||
```sh
|
||||
make rootfs
|
||||
```
|
||||
|
||||
- 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]
|
||||
```
|
||||
|
||||
#### Run Zircon shell in zircon-bare-metal mode:
|
||||
|
||||
- step 1: Build and run zcore in zircon-bare-metal mode:
|
||||
|
||||
```sh
|
||||
cd zCore && make run MODE=release [LOG=warn] [GRAPHIC=on] [ACCEL=1]
|
||||
```
|
||||
|
||||
- step 2: Build and run your own Zircon user programs:
|
||||
|
||||
```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 [LOG=warn] [GRAPHIC=on] [ACCEL=1]
|
||||
```
|
||||
To debug, set `RUST_LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
|
||||
|
||||
## 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:
|
||||
|
|
@ -174,91 +94,15 @@ cd scripts && python3 core-tests.py
|
|||
# Check `zircon/test-result.txt` for results.
|
||||
```
|
||||
|
||||
#### x86-64 Linux related
|
||||
|
||||
Run Linux musl libc-tests for CI:
|
||||
|
||||
```sh
|
||||
## Prepare rootfs with libc-test apps
|
||||
make baremetal-test-img
|
||||
## Build zCore kernel
|
||||
cd zCore && make build MODE=release LINUX=1 ARCH=x86_64
|
||||
## Testing
|
||||
cd scripts && python3 baremetal-libc-test.py
|
||||
##
|
||||
make rootfs && make libc-test
|
||||
cd scripts && python3 libc-tests.py
|
||||
# Check `linux/test-result.txt` for results.
|
||||
```
|
||||
|
||||
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
|
||||
##
|
||||
```
|
||||
|
||||
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>
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
Then you can play the game.
|
||||
Operation
|
||||
|
||||
- Keyboard
|
||||
- `W`/`A`/`S`/`D`: Move
|
||||
- `R`: Restart
|
||||
- `ESC`: End
|
||||
- Mouse
|
||||
- `Left`: Speed up
|
||||
- `Right`: Slow down
|
||||
- `Middle`: Pause/Resume
|
||||
|
||||
## Doc
|
||||
|
||||
```
|
||||
make doc
|
||||
```
|
||||
|
||||
### RISC-V 64 porting info
|
||||
|
||||
- [porting riscv64 doc](./docs/porting-rv64.md)
|
||||
|
||||
## Components
|
||||
|
||||
### Overview
|
||||
|
|
@ -277,6 +121,5 @@ make doc
|
|||
| Thread Management | `executor` | `async-std::task` |
|
||||
| Exception Handling | Interrupt | Signal |
|
||||
|
||||
### Small Goal & Little Plans
|
||||
|
||||
- <https://github.com/rcore-os/zCore/wiki/Plans>
|
||||
### Some plans
|
||||
- https://github.com/rcore-os/zCore/wiki/Plans
|
||||
|
|
|
|||
|
|
@ -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=?>
|
||||
```
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
# zCore for riscv64
|
||||
|
||||
## 编译 zCore 系统镜像
|
||||
|
||||
先在源码根目录下编译 riscv64 的文件系统。
|
||||
|
||||
然后进入子目录 zCore 编译内核,会生成系统镜像`zcore.bin`
|
||||
|
||||
```sh
|
||||
make riscv-image
|
||||
cd zCore
|
||||
make build LINUX=1 ARCH=riscv64 PLATFORM=d1 MODE=release
|
||||
```
|
||||
|
||||
## riscv64 开发板的烧写
|
||||
|
||||
以全志 D1 c906 开发板为例。
|
||||
|
||||
下载并编译烧写工具 `xfel`:
|
||||
|
||||
```sh
|
||||
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
|
||||
```
|
||||
|
||||
### 手动烧写运行:
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 引导运行
|
||||
|
||||
zCore 成功引导后, OpenSBI 会将 dtb 加载到高地址 `0x5ff00000`,运行如下所示:
|
||||
|
||||
```
|
||||
OpenSBI smartx-d1-tina-v1.0.1-release
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : T-HEAD Xuantie Platform
|
||||
Platform HART Features : RV64ACDFIMSUVX
|
||||
Platform Max HARTs : 1
|
||||
Current Hart : 0
|
||||
Firmware Base : 0x40000400
|
||||
Firmware Size : 75 KB
|
||||
Runtime SBI Version : 0.2
|
||||
|
||||
MIDELEG : 0x0000000000000222
|
||||
MEDELEG : 0x000000000000b1ff
|
||||
PMP0 : 0x0000000040000000-0x000000004001ffff (A)
|
||||
PMP1 : 0x0000000040000000-0x000000007fffffff (A,R,W,X)
|
||||
PMP2 : 0x0000000080000000-0x00000000bfffffff (A,R,W,X)
|
||||
PMP3 : 0x0000000000020000-0x0000000000027fff (A,R,W,X)
|
||||
PMP4 : 0x0000000000000000-0x000000003fffffff (A,R,W)
|
||||
____
|
||||
____/ ___|___ _ __ ___
|
||||
|_ / | / _ \| '__/ _ \
|
||||
/ /| |__| (_) | | | __/
|
||||
/___|\____\___/|_| \___|
|
||||
|
||||
Welcome to zCore rust_main( hartid: 0x0, device_tree_paddr: 0x44ddc )
|
||||
Uart output testing
|
||||
+++ Setting up UART interrupts +++
|
||||
+++ Setting up PLIC +++
|
||||
+++ setup interrupt +++
|
||||
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
|
||||
[139.8335662s WARN 0 0:0] TCGETS | TIOCGWINSZ | TIOCSPGRP, pretend to be tty.
|
||||
[140.5358217s WARN 0 0:0] TIOCGPGRP, pretend to be have a tty process group.
|
||||
[140.6017734s WARN 0 0:0] getpgid: unimplemented
|
||||
[140.8971624s WARN 0 0:0] setpgid: unimplemented
|
||||
/ #
|
||||
/ # ls
|
||||
bin dev tmp
|
||||
/ # hello
|
||||
Hello world from user mode program!
|
||||
By xiaoluoyuan@163.com
|
||||
/ #
|
||||
|
||||
```
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# Porting zCore to RiscV64 (Qemu)
|
||||
|
||||
### 2021-03-04
|
||||
<br>之前的内存部分,使用的比较简单的页表映射大页的方式;
|
||||
<br>在加载Qemu文件系统镜像到内存中解开时使用,就需要对虚拟内存、virtio-blk-device以及SimpleFileSystem完全初始化好;
|
||||
<br>其中有些可以比较方便地调用crate库;
|
||||
<br>对中间抽象层kernel-hal的内存操作相关函数的实现:
|
||||
* PageTable 新建页表或获取页表;
|
||||
* trait PageTableTrait,从虚拟地址到物理地址的映射、查询等;
|
||||
* hal_frame_alloc物理帧实现;
|
||||
* pmem_write/read物理地址访问;
|
||||
|
||||
<br>其中,把rCore中的可复用的页表构建PageTableImpl,实际映射的MemoryArea,移到zCore中;
|
||||
<br>在底下bare层的函数变量暴露给启动部分使用,修复一些错误;
|
||||
<br>从目前的运行结果看,应该是页表部分应该是切换好了;
|
||||
```
|
||||
[1.2317591s DEBUG 0 0:0] switch table 8000000000080220 -> 8000000000080a49
|
||||
```
|
||||
|
||||
### zCore系统结构<br>
|
||||

|
||||
|
||||
### riscv64初始移植路径<br>
|
||||
按照:<br>
|
||||
|Qemu riscv64|-|kernel-hal-bare|-|kernel-hal|-|zircon-object/ linux-object|-|linux-syscall|-|linux-loader|-|busybox|
|
||||
|-|-|-|-|-|-|-|-|-|-|-|-|-|
|
||||
|
||||
### 实现思路
|
||||
|
||||
* 分析Makefile的结构,弄清楚包括编译和运行的命令执行流;
|
||||
* 结合Qemu virt和opensbi以及编译出所需文件格式的kernel,也包括用户文件系统生成由x86_64到riscv64,这些命令过程写入Makefile;
|
||||
* 分析Cargo.toml的结构,理解各个features和依赖库crates的关系,如有些是可选的;
|
||||
<br><br>
|
||||
* 创建关于riscv64的Rust target-spec-json编译目标规格描述.json
|
||||
* 创建riscv64对应的链接脚本.ld以及启动入口汇编.asm
|
||||
* 重要的修改部分在kernel-hal-bare,arch下添加riscv架构,包括硬件初始化和对opensbi接口调用的封装;
|
||||
<br><br>
|
||||
|
||||
* 先定一个小目标,让OS跑起来打印初始一段字符
|
||||
* 这时会有大量的编译错误提示,需要解决,Rust错误提示很详细,会建议如何来修正:
|
||||
- 报错可能来自多个方面,包括:riscv64在Cargo.toml中的依赖库,非必需的crates先关掉,添加架构相关所需要的;
|
||||
- kernel-hal-bare中缺失的待实现的接口,可见kernel-hal中的定义;参考了arch/x86_64的函数;
|
||||
- 与target_arch由x86_64移植到riscv64的cfg,其相关的函数或变量需在代码中补上;
|
||||
- 变量及函数的作用范围等需要注意
|
||||
* Rust的条件编译cfg,`#[cfg(target_arch = "x86_64")]`也需要为riscv64实现一份;
|
||||
|
||||
* 要让OS能打印,要把串口输出初始化;
|
||||
- 有两种方式:一种是调用opensbi的打印接口,一种是MMIO的方式初始化串口输出;
|
||||
- 然后实现fmt::Write和宏println;
|
||||
<br><br>
|
||||
|
||||
|
||||
* 关于中断,可通过调用crate riscv,方便的进行指令和寄存器操作;
|
||||
* 陷入trap填入riscv64上下文切换的汇编;
|
||||
* 初始化S态各种中断,包括时钟中断和plic外部中断,在这里的Qemu和K210开发板会有不同;
|
||||
- K210对指令`rdtime`报错非法指令,且无法通过tval取得指令值,故K210无法通过riscv::register::time::read()读当前时间;Qemu无此问题;
|
||||
- 通过联合opensbi调试,riscv当硬件决定触发时钟中断时,会将sip寄存器的STIP位设置为1;当一条指令执行完毕后,如果发现STIP为1,此时如果sie 的STIE位也为1,会进入S态时钟中断的处理程序;
|
||||
- 当在M态不进行时钟中断委派到S态时,Qemu的M态可接收到S态时钟中断;
|
||||
- K210的M态无法收到S态中断,时钟中断和软件中断可以委派到S态来收, 而PLIC外部中断即使委派了也不行, 真是大坑! 同时也要感谢前面童鞋踩过坑的提示;
|
||||
|
||||
* PLIC外部中断是uart串口输出和virtio-blk-device加载文件系统的关键部分;
|
||||
- Qemu UART0_IRQ=10, K210的串口ID为33;
|
||||
- 通过MMIO地址对平台级中断控制器PLIC的寄存器进行设置:设置中断源的优先级,分0~7级,7是最高级;设置中断target的全局阀值[0..7], <= threshold会被屏蔽;
|
||||
- 使能target中某个给定ID的中断,中断ID可查找qemu/include/hw/riscv/virt.h;注意一个核的不同权限模式是不同Target,算出的Target不同则操作的地址也不同,跨步0x80;
|
||||
|
||||
|Target:| 0 | 1 | 2 | | 3 | 4 | 5 |
|
||||
|-|-|-|-|-|-|-|-|
|
||||
|Hart0:| M | S | U | Hart1:| M | S | U |
|
||||
|
||||
这里基于opensbi后一般运行于Hart0 S态,故为Target1
|
||||
|
||||
* PLIC中断初始化完成后,初始化串口中断,Qemu virt串口基地址是0x1000_0000,而K210是0x38000000;
|
||||
- 串口中断处理函数,在每个字符从键盘输入时,输出打印出来;
|
||||
|
||||
* 接着处理虚拟内存和文件系统
|
||||
|
||||
* 由Qemu启动opensbi,装载kernel并引导_start函数,初始化日志log打印,物理内存初始化,进入硬件初始化;
|
||||
* 后以slice的方式载入ramfs文件系统到内存指定地址,打开该SimpleFileSystem的文件系统并通过linux_loader调用用户程序busybox执行;
|
||||
|
||||
* 解析由rcore-fs-fuse生成的Simple FileSystem,通过SimpleFileSystem::open()来打开内存中的文件系统,读取文件和目录;
|
||||
* 最后通过linux_loader::run busybox sh
|
||||
|
||||
* 之前这部分工作由uefi bootloader的rboot把initramfs放到内存的指定地址;
|
||||
* 内存要初始化好,这里使用Qemu的virtio块设备,故也需要初始化好;
|
||||
|
||||
|
||||
移植未完...<br>
|
||||
系统运行效果演示:<br>
|
||||
|
||||
|
||||
在移植过程中,得到老师和童鞋们的很多帮助!感谢!
|
||||
|
|
@ -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"
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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>;
|
||||
}
|
||||
|
|
@ -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) }
|
||||
}
|
||||
|
|
@ -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 in:AllDeviceList
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
//! Only UEFI Display currently.
|
||||
|
||||
mod uefi;
|
||||
|
||||
pub use uefi::UefiDisplay;
|
||||
Binary file not shown.
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//! Only Mouse currently.
|
||||
|
||||
mod mouse;
|
||||
|
||||
pub mod input_event_codes;
|
||||
|
||||
pub use mouse::{Mouse, MouseFlags, MouseState};
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
pub mod sdl;
|
||||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
//! Intel PRO/1000 Network Adapter i.e. e1000 network driver
|
||||
//! Datasheet: https://www.intel.ca/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use smoltcp::iface::*;
|
||||
use smoltcp::phy::{self, DeviceCapabilities};
|
||||
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 isomorphic_drivers::net::ethernet::intel::e1000::E1000;
|
||||
use isomorphic_drivers::net::ethernet::structs::EthernetAddress as DriverEthernetAddress;
|
||||
use lock::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct E1000Driver(Arc<Mutex<E1000<ProviderImpl>>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct E1000Interface {
|
||||
iface: Arc<Mutex<Interface<'static, E1000Driver>>>,
|
||||
driver: E1000Driver,
|
||||
name: String,
|
||||
irq: usize,
|
||||
}
|
||||
|
||||
impl Scheme for E1000Interface {
|
||||
fn name(&self) -> &str {
|
||||
"e1000"
|
||||
}
|
||||
|
||||
fn handle_irq(&self, irq: usize) {
|
||||
if irq != self.irq {
|
||||
// not ours, skip it
|
||||
return;
|
||||
}
|
||||
|
||||
let data = self.driver.0.lock().handle_interrupt();
|
||||
|
||||
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();
|
||||
match self.iface.lock().poll(&mut sockets, timestamp) {
|
||||
Ok(p) => {
|
||||
//SOCKET_ACTIVITY.notify_all();
|
||||
info!("e1000 try_handle_interrupt poll: {:?}", p);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("poll got err {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetScheme for E1000Interface {
|
||||
fn get_mac(&self) -> EthernetAddress {
|
||||
self.iface.lock().ethernet_addr()
|
||||
}
|
||||
|
||||
fn get_ifname(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
// get ip addresses
|
||||
fn get_ip_address(&self) -> Vec<IpCidr> {
|
||||
Vec::from(self.iface.lock().ip_addrs())
|
||||
}
|
||||
|
||||
fn poll(&self) -> DeviceResult {
|
||||
//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();
|
||||
match self.iface.lock().poll(&mut sockets, timestamp) {
|
||||
Ok(p) => {
|
||||
//SOCKET_ACTIVITY.notify_all();
|
||||
info!("e1000 NetScheme poll: {:?}", p);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("poll got err {}", err);
|
||||
Err(DeviceError::IoError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
} else {
|
||||
Err(DeviceError::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct E1000RxToken(Vec<u8>);
|
||||
pub struct E1000TxToken(E1000Driver);
|
||||
|
||||
impl phy::Device<'_> for E1000Driver {
|
||||
type RxToken = E1000RxToken;
|
||||
type TxToken = E1000TxToken;
|
||||
|
||||
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
self.0
|
||||
.lock()
|
||||
.receive()
|
||||
.map(|vec| (E1000RxToken(vec), E1000TxToken(self.clone())))
|
||||
}
|
||||
|
||||
fn transmit(&mut self) -> Option<Self::TxToken> {
|
||||
if self.0.lock().can_send() {
|
||||
Some(E1000TxToken(self.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.max_transmission_unit = 1536;
|
||||
caps.max_burst_size = Some(64);
|
||||
caps
|
||||
}
|
||||
}
|
||||
|
||||
impl phy::RxToken for E1000RxToken {
|
||||
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 E1000TxToken {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let mut buffer = [0u8; PAGE_SIZE];
|
||||
let result = f(&mut buffer[..len]);
|
||||
|
||||
let mut driver = (self.0).0.lock();
|
||||
driver.send(&buffer);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// JudgeDuck-OS/kern/e1000.c
|
||||
pub fn init(
|
||||
name: String,
|
||||
irq: usize,
|
||||
header: usize,
|
||||
size: usize,
|
||||
index: usize,
|
||||
) -> DeviceResult<E1000Interface> {
|
||||
info!("Probing e1000 {}", name);
|
||||
|
||||
// randomly generated
|
||||
let mac: [u8; 6] = [0x54, 0x51, 0x9F, 0x71, 0xC0, index as u8];
|
||||
|
||||
let e1000 = E1000::new(header, size, DriverEthernetAddress::from_bytes(&mac));
|
||||
|
||||
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 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
|
||||
);
|
||||
let e1000_iface = E1000Interface {
|
||||
iface: Arc::new(Mutex::new(iface)),
|
||||
driver: net_driver,
|
||||
name,
|
||||
irq,
|
||||
};
|
||||
|
||||
Ok(e1000_iface)
|
||||
}
|
||||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
// From Linux
|
||||
|
||||
/* Generic MII registers. */
|
||||
pub const MII_BMCR: u32 = 0x00;
|
||||
pub const MII_BMSR: u32 = 0x01;
|
||||
pub const MII_PHYSID1: u32 = 0x02;
|
||||
pub const MII_PHYSID2: u32 = 0x03;
|
||||
pub const MII_ADVERTISE: u32 = 0x04;
|
||||
pub const MII_LPA: u32 = 0x05;
|
||||
pub const MII_EXPANSION: u32 = 0x06;
|
||||
pub const MII_CTRL1000: u32 = 0x09;
|
||||
pub const MII_STAT1000: u32 = 0x0a;
|
||||
pub const MII_MMD_CTRL: u32 = 0x0d;
|
||||
pub const MII_MMD_DATA: u32 = 0x0e;
|
||||
pub const MII_ESTATUS: u32 = 0x0f;
|
||||
pub const MII_DCOUNTER: u32 = 0x12;
|
||||
pub const MII_FCSCOUNTER: u32 = 0x13;
|
||||
pub const MII_NWAYTEST: u32 = 0x14;
|
||||
pub const MII_RERRCOUNTER: u32 = 0x15;
|
||||
pub const MII_SREVISION: u32 = 0x16;
|
||||
pub const MII_RESV1: u32 = 0x17;
|
||||
pub const MII_LBRERROR: u32 = 0x18;
|
||||
pub const MII_PHYADDR: u32 = 0x19;
|
||||
pub const MII_RESV2: u32 = 0x1a;
|
||||
pub const MII_TPISTATUS: u32 = 0x1b;
|
||||
pub const MII_NCONFIG: u32 = 0x1c;
|
||||
|
||||
/* Basic mode control register. */
|
||||
pub const BMCR_RESV: u32 = 0x003f;
|
||||
pub const BMCR_SPEED1000: u32 = 0x0040;
|
||||
pub const BMCR_CTST: u32 = 0x0080;
|
||||
pub const BMCR_FULLDPLX: u32 = 0x0100;
|
||||
pub const BMCR_ANRESTART: u32 = 0x0200;
|
||||
pub const BMCR_ISOLATE: u32 = 0x0400;
|
||||
pub const BMCR_PDOWN: u32 = 0x0800;
|
||||
pub const BMCR_ANENABLE: u32 = 0x1000;
|
||||
pub const BMCR_SPEED100: u32 = 0x2000;
|
||||
pub const BMCR_LOOPBACK: u32 = 0x4000;
|
||||
pub const BMCR_RESET: u32 = 0x8000;
|
||||
pub const BMCR_SPEED10: u32 = 0x0000;
|
||||
|
||||
/* Basic mode status register. */
|
||||
pub const BMSR_ERCAP: u32 = 0x0001;
|
||||
pub const BMSR_JCD: u32 = 0x0002;
|
||||
pub const BMSR_LSTATUS: u32 = 0x0004;
|
||||
pub const BMSR_ANEGCAPABLE: u32 = 0x0008;
|
||||
pub const BMSR_RFAULT: u32 = 0x0010;
|
||||
pub const BMSR_ANEGCOMPLETE: u32 = 0x0020;
|
||||
pub const BMSR_RESV: u32 = 0x00c0;
|
||||
pub const BMSR_ESTATEN: u32 = 0x0100;
|
||||
pub const BMSR_100HALF2: u32 = 0x0200;
|
||||
pub const BMSR_100FULL2: u32 = 0x0400;
|
||||
pub const BMSR_10HALF: u32 = 0x0800;
|
||||
pub const BMSR_10FULL: u32 = 0x1000;
|
||||
pub const BMSR_100HALF: u32 = 0x2000;
|
||||
pub const BMSR_100FULL: u32 = 0x4000;
|
||||
pub const BMSR_100BASE4: u32 = 0x8000;
|
||||
|
||||
/* Advertisement control register. */
|
||||
pub const ADVERTISE_SLCT: u32 = 0x001f;
|
||||
pub const ADVERTISE_CSMA: u32 = 0x0001;
|
||||
pub const ADVERTISE_10HALF: u32 = 0x0020;
|
||||
pub const ADVERTISE_1000XFULL: u32 = 0x0020;
|
||||
pub const ADVERTISE_10FULL: u32 = 0x0040;
|
||||
pub const ADVERTISE_1000XHALF: u32 = 0x0040;
|
||||
pub const ADVERTISE_100HALF: u32 = 0x0080;
|
||||
pub const ADVERTISE_1000XPAUSE: u32 = 0x0080;
|
||||
pub const ADVERTISE_100FULL: u32 = 0x0100;
|
||||
pub const ADVERTISE_1000XPSE_ASYM: u32 = 0x0100;
|
||||
pub const ADVERTISE_100BASE4: u32 = 0x0200;
|
||||
pub const ADVERTISE_PAUSE_CAP: u32 = 0x0400;
|
||||
pub const ADVERTISE_PAUSE_ASYM: u32 = 0x0800;
|
||||
pub const ADVERTISE_RESV: u32 = 0x1000;
|
||||
pub const ADVERTISE_RFAULT: u32 = 0x2000;
|
||||
pub const ADVERTISE_LPACK: u32 = 0x4000;
|
||||
pub const ADVERTISE_NPAGE: u32 = 0x8000;
|
||||
|
||||
pub const ADVERTISE_FULL: u32 = ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA;
|
||||
pub const ADVERTISE_ALL: u32 =
|
||||
ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL;
|
||||
|
||||
/* Link partner ability register. */
|
||||
pub const LPA_SLCT: u32 = 0x001f;
|
||||
pub const LPA_10HALF: u32 = 0x0020;
|
||||
pub const LPA_1000XFULL: u32 = 0x0020;
|
||||
pub const LPA_10FULL: u32 = 0x0040;
|
||||
pub const LPA_1000XHALF: u32 = 0x0040;
|
||||
pub const LPA_100HALF: u32 = 0x0080;
|
||||
pub const LPA_1000XPAUSE: u32 = 0x0080;
|
||||
pub const LPA_100FULL: u32 = 0x0100;
|
||||
pub const LPA_1000XPAUSE_ASYM: u32 = 0x0100;
|
||||
pub const LPA_100BASE4: u32 = 0x0200;
|
||||
pub const LPA_PAUSE_CAP: u32 = 0x0400;
|
||||
pub const LPA_PAUSE_ASYM: u32 = 0x0800;
|
||||
pub const LPA_RESV: u32 = 0x1000;
|
||||
pub const LPA_RFAULT: u32 = 0x2000;
|
||||
pub const LPA_LPACK: u32 = 0x4000;
|
||||
pub const LPA_NPAGE: u32 = 0x8000;
|
||||
|
||||
pub const LPA_DUPLEX: u32 = (LPA_10FULL | LPA_100FULL);
|
||||
pub const LPA_100: u32 = (LPA_100FULL | LPA_100HALF | LPA_100BASE4);
|
||||
|
||||
/* 1000BASE-T Control register */
|
||||
pub const ADVERTISE_1000FULL: u32 = 0x0200;
|
||||
pub const ADVERTISE_1000HALF: u32 = 0x0100;
|
||||
pub const CTL1000_AS_MASTER: u32 = 0x0800;
|
||||
pub const CTL1000_ENABLE_MASTER: u32 = 0x1000;
|
||||
|
||||
/* 1000BASE-T Status register */
|
||||
pub const LPA_1000MSFAIL: u32 = 0x8000;
|
||||
pub const LPA_1000LOCALRXOK: u32 = 0x2000;
|
||||
pub const LPA_1000REMRXOK: u32 = 0x1000;
|
||||
pub const LPA_1000FULL: u32 = 0x0800;
|
||||
pub const LPA_1000HALF: u32 = 0x0400;
|
||||
|
||||
/* Flow control flags */
|
||||
pub const FLOW_CTRL_TX: u32 = 0x01;
|
||||
pub const FLOW_CTRL_RX: u32 = 0x02;
|
||||
|
|
@ -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;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,91 +0,0 @@
|
|||
// 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;
|
||||
|
||||
pub fn flush_cache(addr: u64, size: u64) {
|
||||
flush_dcache_range(addr, addr + size);
|
||||
}
|
||||
|
||||
pub fn invalidate_dcache(addr: u64, size: u64) {
|
||||
invalidate_dcache_range(addr, addr + size);
|
||||
}
|
||||
|
||||
// 注意start输入物理地址
|
||||
pub fn flush_dcache_range(start: u64, end: u64) {
|
||||
// CACHE_LINE 64对齐
|
||||
let end = (end + (CACHE_LINE_SIZE - 1)) & !(CACHE_LINE_SIZE - 1);
|
||||
|
||||
// 地址对齐到L1 Cache的节
|
||||
let mut i: u64 = start & !(L1_CACHE_BYTES - 1);
|
||||
while i < end {
|
||||
unsafe {
|
||||
// 老风格的llvm asm
|
||||
// DCACHE 指定物理地址清脏表项
|
||||
// llvm_asm!("dcache.cpa $0"::"r"(i));
|
||||
|
||||
// 新asm
|
||||
asm!(".long 0x0295000b", in("a0") i); // dcache.cpa a0, 因编译器无法识别该指令
|
||||
}
|
||||
|
||||
i += L1_CACHE_BYTES;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
//llvm_asm!("sync.is");
|
||||
|
||||
asm!(".long 0x01b0000b"); // sync.is
|
||||
}
|
||||
}
|
||||
|
||||
// start 物理地址
|
||||
pub fn invalidate_dcache_range(start: u64, end: u64) {
|
||||
let end = (end + (CACHE_LINE_SIZE - 1)) & !(CACHE_LINE_SIZE - 1);
|
||||
let mut i: u64 = start & !(L1_CACHE_BYTES - 1);
|
||||
while i < end {
|
||||
unsafe {
|
||||
//llvm_asm!("dcache.ipa $0"::"r"(i)); // DCACHE 指定物理地址无效表项
|
||||
asm!(".long 0x02a5000b", in("a0") i); // dcache.ipa a0
|
||||
}
|
||||
|
||||
i += L1_CACHE_BYTES;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
//llvm_asm!("sync.is");
|
||||
asm!(".long 0x01b0000b"); // sync.is
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fence_w() {
|
||||
unsafe {
|
||||
//llvm_asm!("fence ow, ow" ::: "memory");
|
||||
asm!("fence ow, ow");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -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![]));
|
||||
// }
|
||||
|
|
@ -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};
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
[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
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
spin = "0.7"
|
||||
git-version = "0.3"
|
||||
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" }
|
||||
trapframe = "0.7.0"
|
||||
kernel-hal = { path = "../kernel-hal" }
|
||||
naive-timer = "0.1.0"
|
||||
lazy_static = { version = "1.4", features = ["spin_no_std" ] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
x86_64 = "0.13.2"
|
||||
uart_16550 = "=0.2.12"
|
||||
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"
|
||||
|
||||
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
|
||||
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"], rev = "c62af46" }
|
||||
|
|
@ -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::*;
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
use super::*;
|
||||
use kernel_hal::{PageTableTrait, PhysAddr, VirtAddr, Result};
|
||||
use riscv::addr::Page;
|
||||
use riscv::paging::{PageTableFlags as PTF, *};
|
||||
use riscv::register::satp;
|
||||
|
||||
/// 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 _);
|
||||
trace!("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<()> {
|
||||
let mut pt = self.get();
|
||||
let page = Page::of_addr(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!("map: {:x?} -> {:x?}, flags={:?}", vaddr, paddr, flags);
|
||||
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::of_addr(riscv::addr::VirtAddr::new(vaddr));
|
||||
pt.unmap(page).unwrap().1.flush();
|
||||
trace!("unmap: {:x?}", vaddr);
|
||||
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::of_addr(riscv::addr::VirtAddr::new(vaddr));
|
||||
pt.update_flags(page, flags.to_ptf()).unwrap().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<riscv::addr::PhysAddr> {
|
||||
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()),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the physical address of root page table.
|
||||
#[export_name = "hal_pt_table_phys"]
|
||||
fn table_phys(&self) -> PhysAddr {
|
||||
self.root_paddr
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
satp::set(mode, 0, vmtoken >> 12);
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
#![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 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();
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
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(COM1 + IRQ0, Box::new(com1));
|
||||
irq_enable_raw(Keyboard, Keyboard + 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();
|
||||
})
|
||||
} 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)
|
||||
}
|
||||
|
||||
#[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 IDE: u8 = 14;
|
||||
const Error: u8 = 19;
|
||||
const Spurious: u8 = 31;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
use lazy_static::lazy_static;
|
||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
/// Receive character from keyboard
|
||||
/// Should be called on every interrupt
|
||||
pub fn receive() -> Option<DecodedKey> {
|
||||
lazy_static! {
|
||||
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
|
||||
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
|
||||
);
|
||||
}
|
||||
|
||||
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() };
|
||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||
return keyboard.process_keyevent(key_event);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -0,0 +1,497 @@
|
|||
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::{Result, HalError, PageTableTrait},
|
||||
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;
|
||||
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();
|
||||
};
|
||||
trace!(
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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());
|
||||
}
|
||||
|
||||
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, paddr: PhysAddr) {
|
||||
let fb = Framebuffer {
|
||||
width,
|
||||
height,
|
||||
buf: unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
phys_to_virt(paddr) as *mut u32,
|
||||
(width * height) as usize,
|
||||
)
|
||||
},
|
||||
};
|
||||
let console = Console::on_frame_buffer(fb);
|
||||
*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.
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
//! Zircon HAL implementation for bare metal environment.
|
||||
//!
|
||||
//! This crate implements the following interfaces:
|
||||
//! - `hal_pt_new`
|
||||
//! - `hal_pt_map`
|
||||
//! - `hal_pt_unmap`
|
||||
//! - `hal_pt_protect`
|
||||
//! - `hal_pt_query`
|
||||
//! - `hal_pmem_read`
|
||||
//! - `hal_pmem_write`
|
||||
//!
|
||||
//! And you have to implement these interfaces in addition:
|
||||
//! - `hal_pt_map_kernel`
|
||||
//! - `hal_pmem_base`
|
||||
|
||||
#![no_std]
|
||||
#![feature(asm)]
|
||||
#![feature(linkage)]
|
||||
#![deny(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::time::Duration;
|
||||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use kernel_hal::defs::*;
|
||||
use kernel_hal::vdso::*;
|
||||
use kernel_hal::UserContext;
|
||||
use naive_timer::Timer;
|
||||
use spin::Mutex;
|
||||
|
||||
pub mod arch;
|
||||
|
||||
pub use self::arch::*;
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C" {
|
||||
fn hal_pt_map_kernel(pt: *mut u8, current: *const u8);
|
||||
fn hal_frame_alloc() -> Option<PhysAddr>;
|
||||
fn hal_frame_dealloc(paddr: &PhysAddr);
|
||||
#[link_name = "hal_pmem_base"]
|
||||
static PMEM_BASE: usize;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Thread {
|
||||
thread: usize,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
#[export_name = "hal_thread_spawn"]
|
||||
pub fn spawn(
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
||||
vmtoken: usize,
|
||||
) -> Self {
|
||||
struct PageTableSwitchWrapper {
|
||||
inner: Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>,
|
||||
vmtoken: usize,
|
||||
}
|
||||
impl Future for PageTableSwitchWrapper {
|
||||
type Output = ();
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
unsafe {
|
||||
arch::set_page_table(self.vmtoken);
|
||||
}
|
||||
self.inner.lock().as_mut().poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
executor::spawn(PageTableSwitchWrapper {
|
||||
inner: Mutex::new(future),
|
||||
vmtoken,
|
||||
});
|
||||
Thread { thread: 0 }
|
||||
}
|
||||
|
||||
#[export_name = "hal_thread_set_tid"]
|
||||
pub fn set_tid(_tid: u64, _pid: u64) {}
|
||||
|
||||
#[export_name = "hal_thread_get_tid"]
|
||||
pub fn get_tid() -> (u64, u64) {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "hal_context_run"]
|
||||
pub fn context_run(context: &mut UserContext) {
|
||||
context.run();
|
||||
}
|
||||
|
||||
/// Map kernel for the new page table.
|
||||
///
|
||||
/// `pt` is a page-aligned pointer to the root page table.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn map_kernel(pt: *mut u8, current: *const u8) {
|
||||
unsafe {
|
||||
hal_pt_map_kernel(pt, current);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Frame {
|
||||
paddr: PhysAddr,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn alloc() -> Option<Self> {
|
||||
unsafe { hal_frame_alloc().map(|paddr| Frame { paddr }) }
|
||||
}
|
||||
|
||||
pub fn dealloc(&mut self) {
|
||||
unsafe {
|
||||
hal_frame_dealloc(&self.paddr);
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "hal_zero_frame_paddr"]
|
||||
pub fn zero_frame_addr() -> PhysAddr {
|
||||
#[repr(align(0x1000))]
|
||||
struct Page([u8; PAGE_SIZE]);
|
||||
static ZERO_PAGE: Page = Page([0u8; PAGE_SIZE]);
|
||||
unsafe { ZERO_PAGE.0.as_ptr() as usize - PMEM_BASE }
|
||||
}
|
||||
}
|
||||
|
||||
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
|
||||
unsafe { PMEM_BASE + paddr }
|
||||
}
|
||||
|
||||
/// Read physical memory from `paddr` to `buf`.
|
||||
#[export_name = "hal_pmem_read"]
|
||||
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
|
||||
trace!("pmem_read: addr={:#x}, len={:#x}", paddr, buf.len());
|
||||
unsafe {
|
||||
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Write physical memory to `paddr` from `buf`.
|
||||
#[export_name = "hal_pmem_write"]
|
||||
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
|
||||
trace!("pmem_write: addr={:#x}, len={:#x}", paddr, buf.len());
|
||||
unsafe {
|
||||
buf.as_ptr()
|
||||
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero physical memory at `[paddr, paddr + len)`
|
||||
#[export_name = "hal_pmem_zero"]
|
||||
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
|
||||
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
|
||||
unsafe {
|
||||
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy content of `src` frame to `target` frame
|
||||
#[export_name = "hal_frame_copy"]
|
||||
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
|
||||
trace!("frame_copy: {:#x} <- {:#x}", target, src);
|
||||
unsafe {
|
||||
let buf = phys_to_virt(src) as *const u8;
|
||||
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NAIVE_TIMER: Mutex<Timer> = Mutex::new(Timer::default());
|
||||
}
|
||||
|
||||
#[export_name = "hal_timer_set"]
|
||||
pub fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>) {
|
||||
NAIVE_TIMER.lock().add(deadline, callback);
|
||||
}
|
||||
|
||||
#[export_name = "hal_timer_tick"]
|
||||
pub fn timer_tick() {
|
||||
let now = arch::timer_now();
|
||||
NAIVE_TIMER.lock().expire(now);
|
||||
}
|
||||
|
||||
/// Initialize the HAL.
|
||||
pub fn init(config: Config) {
|
||||
unsafe {
|
||||
trapframe::init();
|
||||
}
|
||||
arch::init(config);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn hal_pt_map_kernel(_pt: *mut u8, _current: *const u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn hal_frame_alloc() -> Option<PhysAddr> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn hal_frame_dealloc(_paddr: &PhysAddr) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[export_name = "hal_pmem_base"]
|
||||
static PMEM_BASE: usize = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "kernel-hal-unix"
|
||||
version = "0.1.0"
|
||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
description = "Kernel HAL implementation on Linux and macOS."
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
libc = "0.2"
|
||||
tempfile = "3"
|
||||
bitflags = "1.2"
|
||||
lazy_static = "1.4"
|
||||
kernel-hal = { path = "../kernel-hal" }
|
||||
async-std = "1.9"
|
||||
git-version = "0.3"
|
||||
trapframe = "0.7.0"
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
#![feature(asm)]
|
||||
#![feature(linkage)]
|
||||
#![deny(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use {
|
||||
alloc::collections::VecDeque,
|
||||
async_std::task_local,
|
||||
core::{cell::Cell, future::Future, pin::Pin},
|
||||
git_version::git_version,
|
||||
kernel_hal::PageTableTrait,
|
||||
lazy_static::lazy_static,
|
||||
std::fmt::{Debug, Formatter},
|
||||
std::fs::{File, OpenOptions},
|
||||
std::io::Error,
|
||||
std::os::unix::io::AsRawFd,
|
||||
std::sync::Mutex,
|
||||
std::time::{Duration, SystemTime},
|
||||
tempfile::tempdir,
|
||||
};
|
||||
|
||||
pub use kernel_hal::defs::*;
|
||||
use kernel_hal::vdso::*;
|
||||
pub use kernel_hal::*;
|
||||
use std::io::Read;
|
||||
pub use trapframe::syscall_fn_entry as syscall_entry;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
include!("macos.rs");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Thread {
|
||||
thread: usize,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
#[export_name = "hal_thread_spawn"]
|
||||
pub fn spawn(
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
||||
_vmtoken: usize,
|
||||
) -> Self {
|
||||
async_std::task::spawn(future);
|
||||
Thread { thread: 0 }
|
||||
}
|
||||
|
||||
#[export_name = "hal_thread_set_tid"]
|
||||
pub fn set_tid(tid: u64, pid: u64) {
|
||||
TID.with(|x| x.set(tid));
|
||||
PID.with(|x| x.set(pid));
|
||||
}
|
||||
|
||||
#[export_name = "hal_thread_get_tid"]
|
||||
pub fn get_tid() -> (u64, u64) {
|
||||
(TID.with(|x| x.get()), PID.with(|x| x.get()))
|
||||
}
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static TID: Cell<u64> = Cell::new(0);
|
||||
static PID: Cell<u64> = Cell::new(0);
|
||||
}
|
||||
|
||||
#[export_name = "hal_context_run"]
|
||||
unsafe fn context_run(context: &mut UserContext) {
|
||||
context.run_fncall();
|
||||
}
|
||||
|
||||
/// Page Table
|
||||
#[repr(C)]
|
||||
pub struct PageTable {
|
||||
table_phys: PhysAddr,
|
||||
}
|
||||
|
||||
impl PageTable {
|
||||
/// Create a new `PageTable`.
|
||||
#[allow(clippy::new_without_default)]
|
||||
#[export_name = "hal_pt_new"]
|
||||
pub fn new() -> Self {
|
||||
PageTable { table_phys: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl PageTableTrait for PageTable {
|
||||
/// 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<()> {
|
||||
debug_assert!(page_aligned(vaddr));
|
||||
debug_assert!(page_aligned(paddr));
|
||||
let prot = flags.to_mmap_prot();
|
||||
mmap(FRAME_FILE.as_raw_fd(), paddr, PAGE_SIZE, vaddr, prot);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmap the page of `vaddr`.
|
||||
#[export_name = "hal_pt_unmap"]
|
||||
fn unmap(&mut self, vaddr: VirtAddr) -> Result<()> {
|
||||
self.unmap_cont(vaddr, 1)
|
||||
}
|
||||
|
||||
/// Change the `flags` of the page of `vaddr`.
|
||||
#[export_name = "hal_pt_protect"]
|
||||
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<()> {
|
||||
debug_assert!(page_aligned(vaddr));
|
||||
let prot = flags.to_mmap_prot();
|
||||
let ret = unsafe { libc::mprotect(vaddr as _, PAGE_SIZE, prot) };
|
||||
assert_eq!(ret, 0, "failed to mprotect: {:?}", Error::last_os_error());
|
||||
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> {
|
||||
debug_assert!(page_aligned(vaddr));
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Get the physical address of root page table.
|
||||
#[export_name = "hal_pt_table_phys"]
|
||||
fn table_phys(&self) -> PhysAddr {
|
||||
self.table_phys
|
||||
}
|
||||
|
||||
#[export_name = "hal_pt_unmap_cont"]
|
||||
fn unmap_cont(&mut self, vaddr: VirtAddr, pages: usize) -> Result<()> {
|
||||
if pages == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
debug_assert!(page_aligned(vaddr));
|
||||
let ret = unsafe { libc::munmap(vaddr as _, PAGE_SIZE * pages) };
|
||||
assert_eq!(ret, 0, "failed to munmap: {:?}", Error::last_os_error());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PhysFrame {
|
||||
paddr: PhysAddr,
|
||||
}
|
||||
|
||||
impl Debug for PhysFrame {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::result::Result<(), std::fmt::Error> {
|
||||
write!(f, "PhysFrame({:#x})", self.paddr)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref AVAILABLE_FRAMES: Mutex<VecDeque<usize>> =
|
||||
Mutex::new((PAGE_SIZE..PMEM_SIZE).step_by(PAGE_SIZE).collect());
|
||||
}
|
||||
|
||||
impl PhysFrame {
|
||||
#[export_name = "hal_frame_alloc"]
|
||||
pub fn alloc() -> Option<Self> {
|
||||
let ret = AVAILABLE_FRAMES
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.map(|paddr| PhysFrame { paddr });
|
||||
trace!("frame alloc: {:?}", ret);
|
||||
ret
|
||||
}
|
||||
#[export_name = "hal_zero_frame_paddr"]
|
||||
pub fn zero_frame_addr() -> PhysAddr {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PhysFrame {
|
||||
#[export_name = "hal_frame_dealloc"]
|
||||
fn drop(&mut self) {
|
||||
trace!("frame dealloc: {:?}", self);
|
||||
AVAILABLE_FRAMES.lock().unwrap().push_back(self.paddr);
|
||||
}
|
||||
}
|
||||
|
||||
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
|
||||
/// Map physical memory from here.
|
||||
const PMEM_BASE: VirtAddr = 0x8_0000_0000;
|
||||
|
||||
PMEM_BASE + paddr
|
||||
}
|
||||
|
||||
/// Ensure physical memory are mmapped and accessible.
|
||||
fn ensure_mmap_pmem() {
|
||||
FRAME_FILE.as_raw_fd();
|
||||
}
|
||||
|
||||
/// Read physical memory from `paddr` to `buf`.
|
||||
#[export_name = "hal_pmem_read"]
|
||||
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
|
||||
trace!("pmem read: paddr={:#x}, len={:#x}", paddr, buf.len());
|
||||
assert!(paddr + buf.len() <= PMEM_SIZE);
|
||||
ensure_mmap_pmem();
|
||||
unsafe {
|
||||
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Write physical memory to `paddr` from `buf`.
|
||||
#[export_name = "hal_pmem_write"]
|
||||
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
|
||||
trace!("pmem write: paddr={:#x}, len={:#x}", paddr, buf.len());
|
||||
assert!(paddr + buf.len() <= PMEM_SIZE);
|
||||
ensure_mmap_pmem();
|
||||
unsafe {
|
||||
buf.as_ptr()
|
||||
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero physical memory at `[paddr, paddr + len)`
|
||||
#[export_name = "hal_pmem_zero"]
|
||||
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
|
||||
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
|
||||
assert!(paddr + len <= PMEM_SIZE);
|
||||
ensure_mmap_pmem();
|
||||
unsafe {
|
||||
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy content of `src` frame to `target` frame
|
||||
#[export_name = "hal_frame_copy"]
|
||||
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
|
||||
trace!("frame_copy: {:#x} <- {:#x}", target, src);
|
||||
assert!(src + PAGE_SIZE <= PMEM_SIZE && target + PAGE_SIZE <= PMEM_SIZE);
|
||||
ensure_mmap_pmem();
|
||||
unsafe {
|
||||
let buf = phys_to_virt(src) as *const u8;
|
||||
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush the physical frame.
|
||||
#[export_name = "hal_frame_flush"]
|
||||
pub fn frame_flush(_target: PhysAddr) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const PAGE_SIZE: usize = 0x1000;
|
||||
|
||||
fn page_aligned(x: VirtAddr) -> bool {
|
||||
x % PAGE_SIZE == 0
|
||||
}
|
||||
|
||||
const PMEM_SIZE: usize = 0x4000_0000; // 1GiB
|
||||
|
||||
lazy_static! {
|
||||
static ref FRAME_FILE: File = create_pmem_file();
|
||||
}
|
||||
|
||||
fn create_pmem_file() -> File {
|
||||
let dir = tempdir().expect("failed to create pmem dir");
|
||||
let path = dir.path().join("pmem");
|
||||
|
||||
// workaround on macOS to avoid permission denied.
|
||||
// see https://jiege.ch/software/2020/02/07/macos-mmap-exec/ for analysis on this problem.
|
||||
#[cfg(target_os = "macos")]
|
||||
std::mem::forget(dir);
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)
|
||||
.expect("failed to create pmem file");
|
||||
file.set_len(PMEM_SIZE as u64)
|
||||
.expect("failed to resize file");
|
||||
trace!("create pmem file: path={:?}, size={:#x}", path, PMEM_SIZE);
|
||||
let prot = libc::PROT_READ | libc::PROT_WRITE;
|
||||
mmap(file.as_raw_fd(), 0, PMEM_SIZE, phys_to_virt(0), prot);
|
||||
file
|
||||
}
|
||||
|
||||
/// Mmap frame file `fd` to `vaddr`.
|
||||
fn mmap(fd: libc::c_int, offset: usize, len: usize, vaddr: VirtAddr, prot: libc::c_int) {
|
||||
// workaround on macOS to write text section.
|
||||
#[cfg(target_os = "macos")]
|
||||
let prot = if prot & libc::PROT_EXEC != 0 {
|
||||
prot | libc::PROT_WRITE
|
||||
} else {
|
||||
prot
|
||||
};
|
||||
|
||||
let ret = unsafe {
|
||||
let flags = libc::MAP_SHARED | libc::MAP_FIXED;
|
||||
libc::mmap(vaddr as _, len, prot, flags, fd, offset as _)
|
||||
} as usize;
|
||||
trace!(
|
||||
"mmap file: fd={}, offset={:#x}, len={:#x}, vaddr={:#x}, prot={:#b}",
|
||||
fd,
|
||||
offset,
|
||||
len,
|
||||
vaddr,
|
||||
prot,
|
||||
);
|
||||
assert_eq!(ret, vaddr, "failed to mmap: {:?}", Error::last_os_error());
|
||||
}
|
||||
|
||||
trait FlagsExt {
|
||||
fn to_mmap_prot(&self) -> libc::c_int;
|
||||
}
|
||||
|
||||
impl FlagsExt for MMUFlags {
|
||||
fn to_mmap_prot(&self) -> libc::c_int {
|
||||
let mut flags = 0;
|
||||
if self.contains(MMUFlags::READ) {
|
||||
flags |= libc::PROT_READ;
|
||||
}
|
||||
if self.contains(MMUFlags::WRITE) {
|
||||
flags |= libc::PROT_WRITE;
|
||||
}
|
||||
if self.contains(MMUFlags::EXECUTE) {
|
||||
flags |= libc::PROT_EXEC;
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
fn serial_put(x: u8) {
|
||||
STDIN.lock().unwrap().push_back(x);
|
||||
STDIN_CALLBACK.lock().unwrap().retain(|f| !f());
|
||||
}
|
||||
|
||||
#[export_name = "hal_serial_set_callback"]
|
||||
pub fn serial_set_callback(callback: Box<dyn Fn() -> bool + Send + Sync>) {
|
||||
STDIN_CALLBACK.lock().unwrap().push(callback);
|
||||
}
|
||||
|
||||
#[export_name = "hal_serial_read"]
|
||||
pub fn serial_read(buf: &mut [u8]) -> usize {
|
||||
let mut stdin = STDIN.lock().unwrap();
|
||||
let len = stdin.len().min(buf.len());
|
||||
for c in &mut buf[..len] {
|
||||
*c = stdin.pop_front().unwrap();
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
/// Output a char to console.
|
||||
#[export_name = "hal_serial_write"]
|
||||
pub fn serial_write(s: &str) {
|
||||
eprint!("{}", s);
|
||||
}
|
||||
|
||||
/// Get current time.
|
||||
#[export_name = "hal_timer_now"]
|
||||
pub fn timer_now() -> Duration {
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Set a new timer.
|
||||
///
|
||||
/// After `deadline`, the `callback` will be called.
|
||||
#[export_name = "hal_timer_set"]
|
||||
pub fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>) {
|
||||
std::thread::spawn(move || {
|
||||
let now = timer_now();
|
||||
if deadline > now {
|
||||
std::thread::sleep(deadline - now);
|
||||
}
|
||||
callback(timer_now());
|
||||
});
|
||||
}
|
||||
|
||||
#[export_name = "hal_vdso_constants"]
|
||||
pub fn vdso_constants() -> VdsoConstants {
|
||||
let tsc_frequency = 3000u16;
|
||||
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: PMEM_SIZE as u64,
|
||||
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.
|
||||
///
|
||||
/// This function must be called at the beginning.
|
||||
pub fn init() {
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
register_sigsegv_handler();
|
||||
}
|
||||
// spawn a thread to read stdin
|
||||
// TODO: raw mode
|
||||
std::thread::spawn(|| {
|
||||
for i in std::io::stdin().bytes() {
|
||||
serial_put(i.unwrap());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A valid virtual address base to mmap.
|
||||
const VBASE: VirtAddr = 0x2_00000000;
|
||||
|
||||
#[test]
|
||||
fn map_unmap() {
|
||||
let mut pt = PageTable::new();
|
||||
let flags = MMUFlags::READ | MMUFlags::WRITE;
|
||||
// map 2 pages to 1 frame
|
||||
pt.map(VBASE, 0x1000, flags).unwrap();
|
||||
pt.map(VBASE + 0x1000, 0x1000, flags).unwrap();
|
||||
|
||||
unsafe {
|
||||
const MAGIC: usize = 0xdead_beaf;
|
||||
(VBASE as *mut usize).write(MAGIC);
|
||||
assert_eq!(((VBASE + 0x1000) as *mut usize).read(), MAGIC);
|
||||
}
|
||||
|
||||
pt.unmap(VBASE + 0x1000).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/// Register signal handler for SIGSEGV (Segmentation Fault).
|
||||
///
|
||||
///
|
||||
unsafe fn register_sigsegv_handler() {
|
||||
let sa = libc::sigaction {
|
||||
sa_sigaction: handler as usize,
|
||||
sa_flags: libc::SA_SIGINFO,
|
||||
sa_mask: 0,
|
||||
};
|
||||
libc::sigaction(libc::SIGSEGV, &sa, core::ptr::null_mut());
|
||||
|
||||
#[repr(C)]
|
||||
struct Ucontext {
|
||||
uc_onstack: i32,
|
||||
uc_sigmask: u32,
|
||||
uc_stack: [u32; 5],
|
||||
uc_link: usize,
|
||||
uc_mcsize: usize,
|
||||
uc_mcontext: *const Mcontext,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct Mcontext {
|
||||
trapno: u16,
|
||||
cpu: u16,
|
||||
err: u32,
|
||||
faultvaddr: u64,
|
||||
rax: u64,
|
||||
rbx: u64,
|
||||
rcx: u64,
|
||||
rdx: u64,
|
||||
rdi: u64,
|
||||
rsi: u64,
|
||||
rbp: u64,
|
||||
rsp: u64,
|
||||
r8: u64,
|
||||
r9: u64,
|
||||
r10: u64,
|
||||
r11: u64,
|
||||
r12: u64,
|
||||
r13: u64,
|
||||
r14: u64,
|
||||
r15: u64,
|
||||
rip: u64,
|
||||
rflags: u64,
|
||||
cs: u64,
|
||||
fs: u64,
|
||||
gs: u64,
|
||||
}
|
||||
|
||||
/// Signal handler for when code tries to use %fs.
|
||||
///
|
||||
/// Ref: https://github.com/NuxiNL/cloudabi-utils/blob/38d845bc5cc6fcf441fe0d3c2433f9298cbeb760/src/libemulator/tls.c#L30-L53
|
||||
unsafe extern "C" fn handler(
|
||||
_sig: libc::c_int,
|
||||
_si: *const libc::siginfo_t,
|
||||
uc: *const Ucontext,
|
||||
) {
|
||||
let mut rip = (*(*uc).uc_mcontext).rip as *mut u8;
|
||||
// skip data16 prefix
|
||||
while rip.read() == 0x66 {
|
||||
rip = rip.add(1);
|
||||
}
|
||||
match rip.read() {
|
||||
// Instruction starts with 0x64, meaning it tries to access %fs. By
|
||||
// changing the first byte to 0x65, it uses %gs instead.
|
||||
0x64 => rip.write(0x65),
|
||||
// Instruction has already been patched up, but it may well be the
|
||||
// case that this was done by another CPU core. There is nothing
|
||||
// else we can do than return and try again. This may cause us to
|
||||
// get stuck indefinitely.
|
||||
0x65 => {}
|
||||
// Segmentation violation on an instruction that does not try to
|
||||
// access %fs. Reset the handler to its default action, so that the
|
||||
// segmentation violation is rethrown.
|
||||
_ => {
|
||||
// switch back to kernel gs
|
||||
asm!(
|
||||
"
|
||||
mov rdi, gs:48
|
||||
syscall
|
||||
",
|
||||
in("eax") 0x3000003,
|
||||
out("rdi") _,
|
||||
out("rcx") _,
|
||||
out("r11") _,
|
||||
);
|
||||
panic!("catch SIGSEGV: {:#x?}", *(*uc).uc_mcontext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +1,14 @@
|
|||
[package]
|
||||
name = "kernel-hal"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Runji Wang <wangrunji0408@163.com>",
|
||||
"Yuekai Jia <equation618@gmail.com>",
|
||||
]
|
||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
description = "Kernel HAL interface definations."
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["libos"]
|
||||
smp = []
|
||||
libos = [
|
||||
"nix",
|
||||
"tempfile",
|
||||
"async-std",
|
||||
"bitmap-allocator",
|
||||
"zcore-drivers/mock",
|
||||
]
|
||||
graphic = ["zcore-drivers/graphic"]
|
||||
|
||||
loopback = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
spin = "0.9"
|
||||
cfg-if = "1.0"
|
||||
bitflags = "1.3"
|
||||
trapframe = "0.9.0"
|
||||
git-version = "0.3"
|
||||
bitflags = "1.2"
|
||||
trapframe = "0.7.0"
|
||||
numeric-enum-macro = "0.2"
|
||||
lazy_static = { version = "1.4", features = ["spin_no_std"] }
|
||||
zcore-drivers = { path = "../drivers", features = ["virtio"] }
|
||||
lock = { git = "https://github.com/DeathWish5/kernel-sync", rev = "01b2e70" }
|
||||
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]
|
||||
nix = { version = "0.23", optional = true }
|
||||
tempfile = { version = "3", optional = true }
|
||||
async-std = { version = "1.10", optional = true }
|
||||
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator.git", rev = "88e871a5", optional = true }
|
||||
|
||||
# Bare-metal mode
|
||||
[target.'cfg(target_os = "none")'.dependencies]
|
||||
executor = { git = "https://github.com/DeathWish5/PreemptiveScheduler", rev = "3b04ba4" }
|
||||
naive-timer = "0.2.0"
|
||||
|
||||
# All mode on x86_64
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
x86 = "0.46"
|
||||
x86_64 = "0.14"
|
||||
|
||||
# Bare-metal mode on x86_64
|
||||
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
|
||||
uefi = "0.11"
|
||||
raw-cpuid = "9.0"
|
||||
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "1069df3" }
|
||||
x2apic = "0.4"
|
||||
|
||||
# Bare-metal mode on riscv64
|
||||
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
|
||||
riscv = "0.8"
|
||||
acpi = "1.1"
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
//! Kernel configuration.
|
||||
|
||||
/// Kernel configuration passed by kernel when calls [`crate::primary_init_early()`].
|
||||
#[derive(Debug)]
|
||||
pub struct KernelConfig {
|
||||
pub phys_to_virt_offset: usize,
|
||||
pub dtb_paddr: usize,
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
//! CPU information.
|
||||
use crate::utils::init_once::InitOnce;
|
||||
|
||||
pub(super) static CPU_FREQ_MHZ: InitOnce<u16> = InitOnce::new_with_default(10);
|
||||
|
||||
hal_fn_impl! {
|
||||
impl mod crate::hal_fn::cpu {
|
||||
fn cpu_id() -> u8 {
|
||||
let mut cpu_id;
|
||||
unsafe { core::arch::asm!("mv {0}, tp", out(reg) cpu_id) };
|
||||
cpu_id
|
||||
}
|
||||
|
||||
fn cpu_frequency() -> u16 {
|
||||
*CPU_FREQ_MHZ
|
||||
}
|
||||
|
||||
fn reset() -> ! {
|
||||
info!("shutdown...");
|
||||
super::sbi::shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
|
||||
use zcore_drivers::builder::{DevicetreeDriverBuilder, IoMapper};
|
||||
use zcore_drivers::irq::riscv::ScauseIntCode;
|
||||
use zcore_drivers::uart::BufferedUart;
|
||||
use zcore_drivers::{Device, DeviceResult};
|
||||
|
||||
use crate::common::vm::GenericPageTable;
|
||||
use crate::{drivers, mem::phys_to_virt, CachePolicy, MMUFlags, PhysAddr, VirtAddr};
|
||||
|
||||
struct IoMapperImpl;
|
||||
|
||||
impl IoMapper for IoMapperImpl {
|
||||
fn query_or_map(&self, paddr: PhysAddr, size: usize) -> Option<VirtAddr> {
|
||||
let vaddr = phys_to_virt(paddr);
|
||||
let mut pt = super::vm::kernel_page_table().lock();
|
||||
if let Ok((paddr_mapped, _, _)) = pt.query(vaddr) {
|
||||
if paddr_mapped == paddr {
|
||||
Some(vaddr)
|
||||
} else {
|
||||
warn!(
|
||||
"IoMapper::query_or_map: not linear mapping: vaddr={:#x}, paddr={:#x}",
|
||||
vaddr, paddr_mapped
|
||||
);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let size = crate::addr::align_up(size);
|
||||
let flags = MMUFlags::READ
|
||||
| MMUFlags::WRITE
|
||||
| MMUFlags::HUGE_PAGE
|
||||
| MMUFlags::from_bits_truncate(CachePolicy::UncachedDevice as usize);
|
||||
if let Err(err) = pt.map_cont(vaddr, size, paddr, flags) {
|
||||
warn!(
|
||||
"IoMapper::query_or_map: failed to map {:#x?} => {:#x}, flags={:?}: {:?}",
|
||||
vaddr..vaddr + size,
|
||||
paddr,
|
||||
flags,
|
||||
err
|
||||
);
|
||||
None
|
||||
} else {
|
||||
Some(vaddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize device drivers.
|
||||
pub(super) fn init() -> DeviceResult {
|
||||
// prase DTB and probe devices
|
||||
let dev_list =
|
||||
DevicetreeDriverBuilder::new(phys_to_virt(crate::KCONFIG.dtb_paddr), IoMapperImpl)?
|
||||
.build()?;
|
||||
// add drivers
|
||||
for dev in dev_list.into_iter() {
|
||||
if let Device::Uart(uart) = dev {
|
||||
drivers::add_device(Device::Uart(BufferedUart::new(uart)));
|
||||
} else {
|
||||
drivers::add_device(dev);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "loopback"))]
|
||||
{
|
||||
use alloc::sync::Arc;
|
||||
use zcore_drivers::bus::pci;
|
||||
let pci_devs = pci::init(Some(Arc::new(IoMapperImpl)))?;
|
||||
for d in pci_devs.into_iter() {
|
||||
drivers::add_device(d);
|
||||
}
|
||||
}
|
||||
|
||||
intc_init()?;
|
||||
|
||||
#[cfg(feature = "graphic")]
|
||||
if let Some(display) = drivers::all_display().first() {
|
||||
crate::console::init_graphic_console(display.clone());
|
||||
if display.need_flush() {
|
||||
// TODO: support nested interrupt to render in time
|
||||
crate::thread::spawn(crate::common::future::DisplayFlushFuture::new(display, 30));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "loopback")]
|
||||
{
|
||||
use crate::net;
|
||||
net::init();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn intc_init() -> DeviceResult {
|
||||
let irq = drivers::all_irq()
|
||||
.find(format!("riscv-intc-cpu{}", crate::cpu::cpu_id()).as_str())
|
||||
.expect("IRQ device 'riscv-intc' not initialized!");
|
||||
// register soft interrupts handler
|
||||
irq.register_handler(
|
||||
ScauseIntCode::SupervisorSoft as _,
|
||||
Box::new(super::trap::super_soft),
|
||||
)?;
|
||||
// register timer interrupts handler
|
||||
irq.register_handler(
|
||||
ScauseIntCode::SupervisorTimer as _,
|
||||
Box::new(super::trap::super_timer),
|
||||
)?;
|
||||
irq.unmask(ScauseIntCode::SupervisorSoft as _)?;
|
||||
irq.unmask(ScauseIntCode::SupervisorTimer as _)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
//! Interrupts management.
|
||||
|
||||
use riscv::{asm, register::sstatus};
|
||||
|
||||
hal_fn_impl! {
|
||||
impl mod crate::hal_fn::interrupt {
|
||||
fn wait_for_interrupt() {
|
||||
let enable = sstatus::read().sie();
|
||||
if !enable {
|
||||
unsafe { sstatus::set_sie() };
|
||||
}
|
||||
unsafe { asm::wfi(); }
|
||||
if !enable {
|
||||
unsafe { sstatus::clear_sie() };
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_irq(cause: usize) {
|
||||
trace!("Handle irq cause: {}", cause);
|
||||
crate::drivers::all_irq().first_unwrap().handle_irq(cause)
|
||||
}
|
||||
|
||||
fn intr_on() {
|
||||
unsafe { sstatus::set_sie() };
|
||||
}
|
||||
|
||||
fn intr_off() {
|
||||
unsafe { sstatus::clear_sie() };
|
||||
}
|
||||
|
||||
fn intr_get() -> bool {
|
||||
sstatus::read().sie()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::addr::{align_down, align_up};
|
||||
use crate::utils::init_once::InitOnce;
|
||||
use crate::{PhysAddr, KCONFIG, PAGE_SIZE};
|
||||
|
||||
pub fn free_pmem_regions() -> Vec<Range<PhysAddr>> {
|
||||
extern "C" {
|
||||
fn end();
|
||||
}
|
||||
|
||||
static FREE_PMEM_REGIONS: InitOnce<Vec<Range<PhysAddr>>> = InitOnce::new();
|
||||
FREE_PMEM_REGIONS.init_once(|| {
|
||||
let initrd = super::INITRD_REGION.as_ref();
|
||||
let min_start = align_up(end as usize + PAGE_SIZE) - KCONFIG.phys_to_virt_offset;
|
||||
|
||||
let mut regions = Vec::new();
|
||||
for r in super::MEMORY_REGIONS.iter() {
|
||||
let (start, end) = (align_up(r.start.max(min_start)), align_down(r.end));
|
||||
if start >= end {
|
||||
continue;
|
||||
}
|
||||
if let Some(initrd) = initrd {
|
||||
// no overlap at all
|
||||
if initrd.end <= start || end <= initrd.start {
|
||||
regions.push(start..end);
|
||||
continue;
|
||||
}
|
||||
// no overlap on the left
|
||||
if start < initrd.start {
|
||||
regions.push(start..align_down(initrd.start));
|
||||
}
|
||||
// no overlap on the right
|
||||
if initrd.end < end {
|
||||
regions.push(align_up(initrd.end)..end);
|
||||
}
|
||||
} else {
|
||||
regions.push(start..end);
|
||||
}
|
||||
}
|
||||
regions
|
||||
});
|
||||
FREE_PMEM_REGIONS.clone()
|
||||
}
|
||||
|
||||
pub fn frame_flush(_target: crate::PhysAddr) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
mod drivers;
|
||||
mod trap;
|
||||
|
||||
pub mod config;
|
||||
pub mod cpu;
|
||||
pub mod interrupt;
|
||||
pub mod mem;
|
||||
pub mod sbi;
|
||||
pub mod timer;
|
||||
pub mod vm;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::ops::Range;
|
||||
use zcore_drivers::utils::devicetree::Devicetree;
|
||||
|
||||
use crate::{mem::phys_to_virt, utils::init_once::InitOnce, PhysAddr};
|
||||
|
||||
static CMDLINE: InitOnce<String> = InitOnce::new_with_default(String::new());
|
||||
static INITRD_REGION: InitOnce<Option<Range<PhysAddr>>> = InitOnce::new_with_default(None);
|
||||
static MEMORY_REGIONS: InitOnce<Vec<Range<PhysAddr>>> = InitOnce::new_with_default(Vec::new());
|
||||
|
||||
pub const fn timer_interrupt_vector() -> usize {
|
||||
trap::SUPERVISOR_TIMER_INT_VEC
|
||||
}
|
||||
|
||||
pub fn cmdline() -> String {
|
||||
CMDLINE.clone()
|
||||
}
|
||||
|
||||
pub fn init_ram_disk() -> Option<&'static mut [u8]> {
|
||||
INITRD_REGION.as_ref().map(|range| unsafe {
|
||||
core::slice::from_raw_parts_mut(phys_to_virt(range.start) as *mut u8, range.len())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn primary_init_early() {
|
||||
let dt = Devicetree::from(phys_to_virt(crate::KCONFIG.dtb_paddr)).unwrap();
|
||||
if let Some(cmdline) = dt.bootargs() {
|
||||
info!("Load kernel cmdline from DTB: {:?}", cmdline);
|
||||
CMDLINE.init_once_by(cmdline.into());
|
||||
}
|
||||
if let Some(time_freq) = dt.timebase_frequency() {
|
||||
info!("Load CPU clock frequency from DTB: {} Hz", time_freq);
|
||||
super::cpu::CPU_FREQ_MHZ.init_once_by((time_freq / 1_000_000) as u16);
|
||||
}
|
||||
if let Some(initrd_region) = dt.initrd_region() {
|
||||
info!("Load initrd regions from DTB: {:#x?}", initrd_region);
|
||||
INITRD_REGION.init_once_by(Some(initrd_region));
|
||||
}
|
||||
if let Ok(regions) = dt.memory_regions() {
|
||||
info!("Load memory regions from DTB: {:#x?}", regions);
|
||||
MEMORY_REGIONS.init_once_by(regions);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary_init() {
|
||||
vm::init();
|
||||
drivers::init().unwrap();
|
||||
// We should set first time interrupt before run into first user program
|
||||
// timer::init();
|
||||
}
|
||||
|
||||
pub fn timer_init() {
|
||||
timer::init();
|
||||
}
|
||||
|
||||
pub fn secondary_init() {
|
||||
vm::init();
|
||||
drivers::intc_init().unwrap();
|
||||
let plic = crate::drivers::all_irq()
|
||||
.find("riscv-plic")
|
||||
.expect("IRQ device 'riscv-plic' not initialized!");
|
||||
plic.init_hart();
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
// Legacy Extensions (EIDs 0x00 - 0x0F)
|
||||
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;
|
||||
|
||||
// Hart State Management Extension
|
||||
const HSM_EID: usize = 0x48534D;
|
||||
const SBI_HART_START_FID: usize = 0; // SBI Verson=0.2
|
||||
const SBI_HART_STOP_FID: usize = 1; // SBI Verson=0.2
|
||||
const SBI_HART_GET_STATUS_FID: usize = 2; // SBI Verson=0.2
|
||||
const SBI_HART_SUSPEND_FID: usize = 3; // SBI Verson=0.3
|
||||
|
||||
// SBI Error Code
|
||||
pub const SBI_SUCCESS: usize = 0;
|
||||
pub const SBI_ERR_FAILED: usize = usize::MAX; // -1
|
||||
pub const SBI_ERR_NOT_SUPPORTED: usize = usize::MAX - 1; // -2
|
||||
pub const SBI_ERR_INVALID_PARAM: usize = usize::MAX - 2; // -3
|
||||
pub const SBI_ERR_DENIED: usize = usize::MAX - 3; // -4
|
||||
pub const SBI_ERR_INVALID_ADDRESS: usize = usize::MAX - 4; // -5
|
||||
pub const SBI_ERR_ALREADY_AVAILABLE: usize = usize::MAX - 5; // -6
|
||||
pub const SBI_ERR_ALREADY_STARTED: usize = usize::MAX - 6; // -7
|
||||
pub const SBI_ERR_ALREADY_STOPPED: usize = usize::MAX - 7; // -8
|
||||
|
||||
#[inline(always)]
|
||||
fn sbi_call(eid: usize, fid: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
|
||||
let ret;
|
||||
unsafe {
|
||||
core::arch::asm!("ecall",
|
||||
in("a0") arg0,
|
||||
in("a1") arg1,
|
||||
in("a2") arg2,
|
||||
in("a6") fid,
|
||||
in("a7") eid,
|
||||
lateout("a0") ret,
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn console_putchar(ch: usize) -> usize {
|
||||
sbi_call(SBI_CONSOLE_PUTCHAR, 0, ch, 0, 0)
|
||||
}
|
||||
|
||||
pub fn console_getchar() -> usize {
|
||||
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn set_timer(stime_value: u64) -> usize {
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
return sbi_call(
|
||||
SBI_SET_TIMER,
|
||||
0,
|
||||
stime_value as usize,
|
||||
(stime_value >> 32),
|
||||
0,
|
||||
);
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
sbi_call(SBI_SET_TIMER, 0, stime_value as usize, 0, 0)
|
||||
}
|
||||
|
||||
pub fn clear_ipi() -> usize {
|
||||
sbi_call(SBI_CLEAR_IPI, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn send_ipi(sipi_value: usize) -> usize {
|
||||
sbi_call(SBI_SEND_IPI, 0, sipi_value, 0, 0)
|
||||
}
|
||||
|
||||
pub fn shutdown() -> ! {
|
||||
sbi_call(SBI_SHUTDOWN, 0, 0, 0, 0);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/// executing the target hart in supervisor-mode at address
|
||||
/// specified by start_addr parameter
|
||||
///
|
||||
/// The opaque parameter is a XLEN-bit value which will be
|
||||
/// set in the a1 register when the hart starts executing
|
||||
/// at start_addr.
|
||||
pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> usize {
|
||||
sbi_call(HSM_EID, SBI_HART_START_FID, hartid, start_addr, opaque)
|
||||
}
|
||||
|
||||
/// stop executing the calling hart in supervisor-mode and return
|
||||
/// it’s ownership to the SBI implementation.
|
||||
pub fn hart_stop() -> usize {
|
||||
sbi_call(HSM_EID, SBI_HART_STOP_FID, 0, 0, 0)
|
||||
}
|
||||
|
||||
hal_fn_impl! {
|
||||
impl mod crate::hal_fn::console {
|
||||
fn console_write_early(s: &str) {
|
||||
for c in s.bytes() {
|
||||
console_putchar(c as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
use core::time::Duration;
|
||||
|
||||
fn get_cycle() -> u64 {
|
||||
riscv::register::time::read() as u64
|
||||
}
|
||||
|
||||
pub(super) fn timer_set_next() {
|
||||
let cycles =
|
||||
super::cpu::cpu_frequency() as u64 * 1_000_000 / super::super::timer::TICKS_PER_SEC;
|
||||
super::sbi::set_timer(get_cycle() + cycles);
|
||||
}
|
||||
|
||||
pub(super) fn init() {
|
||||
timer_set_next();
|
||||
}
|
||||
|
||||
pub(crate) fn timer_now() -> Duration {
|
||||
Duration::from_nanos(get_cycle() * 1000 / super::cpu::cpu_frequency() as u64)
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
use riscv::register::scause;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::context::TrapReason;
|
||||
pub(super) const SUPERVISOR_TIMER_INT_VEC: usize = 5; // scause::Interrupt::SupervisorTimer
|
||||
|
||||
fn breakpoint(sepc: &mut usize) {
|
||||
info!("Exception::Breakpoint: A breakpoint set @0x{:x} ", sepc);
|
||||
|
||||
//sepc为触发中断指令ebreak的地址
|
||||
//防止无限循环中断,让sret返回时跳转到sepc的下一条指令地址
|
||||
*sepc += 2
|
||||
}
|
||||
|
||||
pub(super) fn super_timer() {
|
||||
super::timer::timer_set_next();
|
||||
crate::timer::timer_tick();
|
||||
//发生外界中断时,epc的指令还没有执行,故无需修改epc到下一条
|
||||
}
|
||||
|
||||
pub(super) fn super_soft() {
|
||||
super::sbi::clear_ipi();
|
||||
info!("Interrupt::SupervisorSoft!");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
|
||||
let scause = scause::read();
|
||||
trace!("kernel trap happened: {:?}", TrapReason::from(scause));
|
||||
trace!(
|
||||
"sepc = 0x{:x} pgtoken = 0x{:x}",
|
||||
tf.sepc,
|
||||
crate::vm::current_vmtoken()
|
||||
);
|
||||
match TrapReason::from(scause) {
|
||||
TrapReason::SoftwareBreakpoint => breakpoint(&mut tf.sepc),
|
||||
TrapReason::PageFault(vaddr, flags) => crate::KHANDLER.handle_page_fault(vaddr, flags),
|
||||
TrapReason::Interrupt(vector) => {
|
||||
crate::interrupt::handle_irq(vector);
|
||||
if vector == SUPERVISOR_TIMER_INT_VEC {
|
||||
executor::handle_timeout();
|
||||
}
|
||||
}
|
||||
other => panic!("Undefined trap: {:x?} {:#x?}", other, tf),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
//! Virtual memory operations.
|
||||
|
||||
use core::fmt::{Debug, Formatter, Result};
|
||||
use core::slice;
|
||||
|
||||
use lock::Mutex;
|
||||
use riscv::{asm, register::satp};
|
||||
|
||||
use crate::utils::page_table::{GenericPTE, PageTableImpl, PageTableLevel3};
|
||||
use crate::{mem::phys_to_virt, MMUFlags, PhysAddr, VirtAddr, KCONFIG};
|
||||
|
||||
lazy_static! {
|
||||
static ref KERNEL_PT: Mutex<PageTable> = Mutex::new(init_kernel_page_table().unwrap());
|
||||
}
|
||||
|
||||
/// remap kernel ELF segments with 4K page
|
||||
fn init_kernel_page_table() -> PagingResult<PageTable> {
|
||||
extern "C" {
|
||||
fn stext();
|
||||
fn etext();
|
||||
fn srodata();
|
||||
fn erodata();
|
||||
fn sdata();
|
||||
fn edata();
|
||||
fn sbss();
|
||||
fn ebss();
|
||||
|
||||
fn bootstack();
|
||||
fn bootstacktop();
|
||||
}
|
||||
|
||||
let mut pt = PageTable::new();
|
||||
let mut map_range = |start: VirtAddr, end: VirtAddr, flags: MMUFlags| -> PagingResult {
|
||||
pt.map_cont(
|
||||
crate::addr::align_down(start),
|
||||
crate::addr::align_up(end - start),
|
||||
start - KCONFIG.phys_to_virt_offset,
|
||||
flags | MMUFlags::HUGE_PAGE,
|
||||
)
|
||||
};
|
||||
|
||||
map_range(
|
||||
stext as usize,
|
||||
etext as usize,
|
||||
MMUFlags::READ | MMUFlags::EXECUTE,
|
||||
)?;
|
||||
map_range(srodata as usize, erodata as usize, MMUFlags::READ)?;
|
||||
map_range(
|
||||
sdata as usize,
|
||||
edata as usize,
|
||||
MMUFlags::READ | MMUFlags::WRITE,
|
||||
)?;
|
||||
map_range(
|
||||
sbss as usize,
|
||||
ebss as usize,
|
||||
MMUFlags::READ | MMUFlags::WRITE,
|
||||
)?;
|
||||
// stack
|
||||
map_range(
|
||||
bootstack as usize,
|
||||
bootstacktop as usize,
|
||||
MMUFlags::READ | MMUFlags::WRITE,
|
||||
)?;
|
||||
// initrd
|
||||
if let Some(initrd) = super::INITRD_REGION.as_ref() {
|
||||
map_range(
|
||||
phys_to_virt(initrd.start),
|
||||
phys_to_virt(initrd.end),
|
||||
MMUFlags::READ | MMUFlags::WRITE,
|
||||
)?;
|
||||
}
|
||||
// physical frames
|
||||
for r in crate::mem::free_pmem_regions() {
|
||||
map_range(
|
||||
phys_to_virt(r.start),
|
||||
phys_to_virt(r.end),
|
||||
MMUFlags::READ | MMUFlags::WRITE,
|
||||
)?;
|
||||
}
|
||||
|
||||
info!("initialized kernel page table @ {:#x}", pt.table_phys());
|
||||
Ok(pt)
|
||||
}
|
||||
|
||||
pub(super) fn kernel_page_table() -> &'static Mutex<PageTable> {
|
||||
&KERNEL_PT
|
||||
}
|
||||
|
||||
pub(super) fn init() {
|
||||
unsafe { KERNEL_PT.lock().activate() };
|
||||
}
|
||||
|
||||
hal_fn_impl! {
|
||||
impl mod crate::hal_fn::vm {
|
||||
fn activate_paging(vmtoken: PhysAddr) {
|
||||
let old_token = current_vmtoken();
|
||||
if old_token != vmtoken {
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
let mode = satp::Mode::Sv39;
|
||||
debug!("switch table {:x?} -> {:x?}", old_token, vmtoken);
|
||||
unsafe {
|
||||
satp::set(mode, 0, vmtoken >> 12);
|
||||
asm::sfence_vma_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn current_vmtoken() -> PhysAddr {
|
||||
satp::read().ppn() << 12
|
||||
}
|
||||
|
||||
fn flush_tlb(vaddr: Option<VirtAddr>) {
|
||||
unsafe {
|
||||
if let Some(vaddr) = vaddr {
|
||||
asm::sfence_vma(0, vaddr)
|
||||
} else {
|
||||
asm::sfence_vma_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pt_clone_kernel_space(dst_pt_root: PhysAddr, src_pt_root: PhysAddr) {
|
||||
let entry_range = 0x100..0x200; // 0xFFFF_FFC0_0000_0000 .. 0xFFFF_FFFF_FFFF_FFFF
|
||||
let dst_table = unsafe { slice::from_raw_parts_mut(phys_to_virt(dst_pt_root) as *mut Rv64PTE, 512) };
|
||||
let src_table = unsafe { slice::from_raw_parts(phys_to_virt(src_pt_root) as *const Rv64PTE, 512) };
|
||||
for i in entry_range {
|
||||
dst_table[i] = src_table[i];
|
||||
if !dst_table[i].is_unused() {
|
||||
dst_table[i].0 |= PTF::GLOBAL.bits() as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Possible flags for a page table entry.
|
||||
struct PTF: usize {
|
||||
const VALID = 1 << 0;
|
||||
const READABLE = 1 << 1;
|
||||
const WRITABLE = 1 << 2;
|
||||
const EXECUTABLE = 1 << 3;
|
||||
const USER = 1 << 4;
|
||||
const GLOBAL = 1 << 5;
|
||||
const ACCESSED = 1 << 6;
|
||||
const DIRTY = 1 << 7;
|
||||
const RESERVED1 = 1 << 8;
|
||||
const RESERVED2 = 1 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MMUFlags> for PTF {
|
||||
fn from(f: MMUFlags) -> Self {
|
||||
if f.is_empty() {
|
||||
return PTF::empty();
|
||||
}
|
||||
let mut flags = PTF::VALID;
|
||||
if f.contains(MMUFlags::READ) {
|
||||
flags |= PTF::READABLE;
|
||||
}
|
||||
if f.contains(MMUFlags::WRITE) {
|
||||
flags |= PTF::READABLE | PTF::WRITABLE;
|
||||
}
|
||||
if f.contains(MMUFlags::EXECUTE) {
|
||||
flags |= PTF::EXECUTABLE;
|
||||
}
|
||||
if f.contains(MMUFlags::USER) {
|
||||
flags |= PTF::USER;
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PTF> for MMUFlags {
|
||||
fn from(f: PTF) -> Self {
|
||||
let mut ret = Self::empty();
|
||||
if f.contains(PTF::READABLE) {
|
||||
ret |= Self::READ;
|
||||
}
|
||||
if f.contains(PTF::WRITABLE) {
|
||||
ret |= Self::WRITE;
|
||||
}
|
||||
if f.contains(PTF::EXECUTABLE) {
|
||||
ret |= Self::EXECUTE;
|
||||
}
|
||||
if f.contains(PTF::USER) {
|
||||
ret |= Self::USER;
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
const PHYS_ADDR_MASK: u64 = 0x003f_ffff_ffff_fc00; // 10..54
|
||||
|
||||
/// Sv39 and Sv48 page table entry.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Rv64PTE(u64);
|
||||
|
||||
impl GenericPTE for Rv64PTE {
|
||||
fn addr(&self) -> PhysAddr {
|
||||
((self.0 & PHYS_ADDR_MASK) << 2) as _
|
||||
}
|
||||
fn flags(&self) -> MMUFlags {
|
||||
PTF::from_bits_truncate(self.0 as usize).into()
|
||||
}
|
||||
fn is_unused(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
fn is_present(&self) -> bool {
|
||||
PTF::from_bits_truncate(self.0 as usize).contains(PTF::VALID)
|
||||
}
|
||||
fn is_leaf(&self) -> bool {
|
||||
PTF::from_bits_truncate(self.0 as usize).intersects(PTF::READABLE | PTF::EXECUTABLE)
|
||||
}
|
||||
|
||||
fn set_addr(&mut self, paddr: PhysAddr) {
|
||||
self.0 = (self.0 & !PHYS_ADDR_MASK) | ((paddr as u64 >> 2) & PHYS_ADDR_MASK);
|
||||
}
|
||||
fn set_flags(&mut self, flags: MMUFlags, _is_huge: bool) {
|
||||
let flags = PTF::from(flags) | PTF::ACCESSED | PTF::DIRTY;
|
||||
debug_assert!(flags.intersects(PTF::READABLE | PTF::EXECUTABLE));
|
||||
self.0 = (self.0 & PHYS_ADDR_MASK) | flags.bits() as u64;
|
||||
}
|
||||
fn set_table(&mut self, paddr: PhysAddr) {
|
||||
self.0 = ((paddr as u64 >> 2) & PHYS_ADDR_MASK) | PTF::VALID.bits() as u64;
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
self.0 = 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Rv64PTE {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
let mut f = f.debug_struct("Rv64PTE");
|
||||
f.field("raw", &self.0);
|
||||
f.field("addr", &self.addr());
|
||||
f.field("flags", &self.flags());
|
||||
f.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sv39: Page-Based 39-bit Virtual-Memory System.
|
||||
pub type PageTable = PageTableImpl<PageTableLevel3, Rv64PTE>;
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
//! Kernel configuration.
|
||||
|
||||
use uefi::proto::console::gop::ModeInfo;
|
||||
use uefi::table::boot::MemoryDescriptor;
|
||||
|
||||
/// Kernel configuration passed by kernel when calls [`crate::primary_init_early()`].
|
||||
#[derive(Debug)]
|
||||
pub struct KernelConfig {
|
||||
pub cmdline: &'static str,
|
||||
pub initrd_start: u64,
|
||||
pub initrd_size: u64,
|
||||
|
||||
pub memory_map: &'static [&'static MemoryDescriptor],
|
||||
pub phys_to_virt_offset: usize,
|
||||
|
||||
pub fb_mode: ModeInfo,
|
||||
pub fb_addr: u64,
|
||||
pub fb_size: u64,
|
||||
|
||||
pub acpi_rsdp: u64,
|
||||
pub smbios: u64,
|
||||
pub ap_fn: fn() -> !,
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
//! CPU information.
|
||||
|
||||
use raw_cpuid::CpuId;
|
||||
|
||||
hal_fn_impl! {
|
||||
impl mod crate::hal_fn::cpu {
|
||||
fn cpu_id() -> u8 {
|
||||
CpuId::new()
|
||||
.get_feature_info()
|
||||
.unwrap()
|
||||
.initial_local_apic_id() as u8
|
||||
}
|
||||
|
||||
fn cpu_frequency() -> u16 {
|
||||
static CPU_FREQ_MHZ: spin::Once<u16> = spin::Once::new();
|
||||
*CPU_FREQ_MHZ.call_once(|| {
|
||||
const DEFAULT: u16 = 4000;
|
||||
CpuId::new()
|
||||
.get_processor_frequency_info()
|
||||
.map(|info| info.processor_base_frequency())
|
||||
.unwrap_or(DEFAULT)
|
||||
.max(DEFAULT)
|
||||
})
|
||||
}
|
||||
|
||||
fn reset() -> ! {
|
||||
info!("shutdown...");
|
||||
loop {
|
||||
use zcore_drivers::io::{Io, Pmio};
|
||||
Pmio::<u16>::new(0x604).write(0x2000);
|
||||
super::interrupt::wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue