Compare commits

..

14 Commits

Author SHA1 Message Date
刘丰源 6e3444acb9 try handle tlb miss, but fail 2020-08-02 00:42:35 +08:00
刘丰源 f902d6bfe7 impl timer 2020-07-31 12:28:07 +08:00
刘丰源 1c8d17fb1b fix mips commit page error 2020-07-30 22:47:08 +08:00
刘丰源 73f447077c init other 2020-07-30 15:20:43 +08:00
刘丰源 26704a253e fix merge error 2020-07-29 23:49:29 +08:00
刘丰源 fc034c534a merge master 2020-07-29 23:35:53 +08:00
刘丰源 8833f8c1ca reimpl RegExt to CtxExt, fix compile error 2020-07-29 23:19:54 +08:00
刘丰源 be7b2d5b28 fix mipsel, now can print... 2020-07-29 21:50:51 +08:00
刘丰源 b32e2527c7 impl mips putfmt(serial still can not work...) 2020-07-26 12:36:55 +08:00
刘丰源 690694a17e handle mips page fault 2020-07-25 17:58:33 +08:00
刘丰源 9e63d2810d mips mem init 2020-07-24 22:00:46 +08:00
刘丰源 e2926e144e try mipsel serial, but fail... qaq 2020-07-24 16:20:33 +08:00
刘丰源 9ae7dce43e init mipsel dtb 2020-07-24 11:15:29 +08:00
刘丰源 ea1aa37bc6 fix compile error 2020-07-22 21:45:53 +08:00
389 changed files with 9224 additions and 30277 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,149 +0,0 @@
name: Build CI
on:
push:
pull_request:
schedule:
- cron: '0 22 * * *' # every day at 22:00 UTC
env:
rust_toolchain: nightly-2022-01-20
jobs:
workspace:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
override: true
components: rust-src, rustfmt, clippy
- name: Check format
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --all-features
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-features
- name: Build docs
uses: actions-rs/cargo@v1
with:
command: doc
args: --all-features --no-deps
build-aarch64:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
override: true
target: aarch64-unknown-linux-gnu
- uses: actions-rs/cargo@v1
with:
command: build
use-cross: true
args: --target aarch64-unknown-linux-gnu --workspace --exclude linux-syscall --exclude zcore-loader --exclude zcore
build-user:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Pull prebuilt images
run: git lfs pull -I prebuilt/zircon/x64/libc.so,prebuilt/zircon/x64/libfdio.so,prebuilt/zircon/x64/libunwind.so,prebuilt/zircon/x64/libzircon.so,prebuilt/zircon/x64/Scrt1.o
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
target: x86_64-fuchsia
- name: Build Zircon user programs
run: cd zircon-user && make build MODE=release
test-libos:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
mode: [linux, zircon]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --package zcore --features "${{ matrix.mode }} libos"
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --package zcore --features "${{ matrix.mode }} libos"
test-bare-metal:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
arch: [x86_64, riscv64]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, clippy
- uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
- name: Build ${{ matrix.arch }} bare-metal zircon
if: matrix.arch == 'x86_64'
run: cd zCore && make build ARCH=${{ matrix.arch }}
- name: Clippy ${{ matrix.arch }} bare-metal zircon
if: matrix.arch == 'x86_64'
run: cd zCore && make clippy ARCH=${{ matrix.arch }}
- name: Build ${{ matrix.arch }} bare-metal linux
if: matrix.arch == 'riscv64'
run: cd zCore && make build ARCH=${{ matrix.arch }} LINUX=1
- name: Clippy ${{ matrix.arch }} bare-metal linux
if: matrix.arch == 'riscv64'
run: cd zCore && make clippy ARCH=${{ matrix.arch }} LINUX=1

View File

@ -1,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

170
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,170 @@
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-2020-06-04
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
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-2020-06-04
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-2020-06-04
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-2020-06-04
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'
- name: Cache grcov
uses: actions/cache@v1
with:
path: ~/.cargo/bin/grcov
key: ${{ runner.os }}-grcov
- name: Gather coverage data
id: coverage
uses: actions-rs/grcov@v0.1
- 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-2020-06-04
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
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
- 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

View File

@ -1,281 +0,0 @@
name: Test CI
on:
push:
pull_request:
schedule:
- cron: '0 22 * * *' # every day at 22:00 UTC
env:
rust_toolchain: nightly-2022-01-20
qemu_version: 7.0.0
jobs:
unit-test:
name: Unit Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Run unit test
uses: actions-rs/cargo@v1
with:
command: test
args: --no-fail-fast
env:
CARGO_INCREMENTAL: '0'
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort'
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort'
- name: Cache grcov
uses: actions/cache@v3
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-grcov
- name: Gather coverage data
id: coverage
uses: actions-rs/grcov@v0.1
# FIXME: 'error from lcovParse: Failed to parse string'
# - name: Coveralls upload
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: ${{ steps.coverage.outputs.report }}
bench-test:
name: Bench Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Run bench test
uses: actions-rs/cargo@v1
with:
command: bench
zircon-core-test-libos:
name: Zircon Core Test Libos
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Pull prebuilt images
run: |
git lfs pull -I prebuilt/zircon/x64/core-tests.zbi
git lfs pull -I prebuilt/zircon/x64/libzircon-libos.so
git lfs pull -I prebuilt/zircon/x64/userboot-libos.so
- name: Install python dependencies
run: .github/scripts/install-deps.sh
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 zircon_core_test.py --libos --fast --no-failed
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 zircon_core_test.py --libos
linux-libc-test-libos:
name: Linux Libc Test Libos
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Install dependencies
run: .github/scripts/install-deps.sh musl-tools musl-dev
- name: Prepare rootfs
run: make libc-test
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_libc_test.py --libos --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_libc_test.py --libos
zircon-core-test-baremetal:
name: Zircon Core Test Baremetal
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- name: Pull prebuilt images
run: |
git lfs pull -I prebuilt/zircon/x64/core-tests.zbi
git lfs pull -I prebuilt/zircon/x64/libzircon.so
git lfs pull -I prebuilt/zircon/x64/userboot.so
- name: Install dependencies
run: .github/scripts/install-deps.sh ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-x86_64 --version
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 zircon_core_test.py --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 zircon_core_test.py
linux-libc-test-baremetal:
name: Linux Libc Test Baremetal
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
arch: [x86_64, riscv64]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- if: matrix.arch == 'riscv64'
uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
- name: Install dependencies
run: .github/scripts/install-deps.sh musl-tools musl-dev ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
run: make libc-test ARCH=${{ matrix.arch }} && make image ARCH=${{ matrix.arch }}
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_libc_test.py --arch ${{ matrix.arch }} --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_libc_test.py --arch ${{ matrix.arch }}
linux-other-test-baremetal:
name: Linux Other Test Baremetal
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
arch: [x86_64, riscv64]
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview, rustfmt, clippy
- if: matrix.arch == 'riscv64'
uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
- name: Install dependencies
run: .github/scripts/install-deps.sh musl-tools musl-dev ninja-build
- name: Cache QEMU
id: cache-qemu
uses: actions/cache@v3
with:
path: qemu-${{ env.qemu_version }}
key: qemu-${{ env.qemu_version }}-x86_64-riscv64
- name: Download and Compile QEMU
if: steps.cache-qemu.outputs.cache-hit != 'true'
run: .github/scripts/install-qemu.sh ${{ env.qemu_version }}
- name: Install QEMU
run: |
cd qemu-${{ env.qemu_version }} && sudo make install
qemu-system-${{ matrix.arch }} --version
- name: Prepare rootfs
run: make other-test ARCH=${{ matrix.arch }} && make image ARCH=${{ matrix.arch }}
- name: Run fast tests
if: github.event_name != 'schedule'
run: cd tests && python3 linux_other_test.py --arch ${{ matrix.arch }} --fast
- name: Run full tests
if: github.event_name == 'schedule'
run: cd tests && python3 linux_other_test.py --arch ${{ matrix.arch }}

18
.gitignore vendored
View File

@ -1,18 +1,12 @@
**/.*
!.github/
!.cargo/
!.vscode/settings.json
/target
**/target
**/*.rs.bk
*.img
*.bin
*.log
Cargo.lock
/ignored
/rootfs
zCore/src/platform/riscv/kernel-vars.ld
/prebuilt/linux/alpine*
.idea
.DS_Store
__pycache__
/zCore/src/arch/mipsel/boot/linker.ld
*.gen.s
*.dtb

6
.gitmodules vendored
View File

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

10
.vscode/settings.json vendored
View File

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

View File

@ -1,17 +1,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",
]

View File

@ -1,53 +1,12 @@
# 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
.PHONY: rootfs
.PHONY: help setup update rootfs libc-test other-test image check doc clean
prebuilt/linux/$(ROOTFS_TAR):
wget $(ROOTFS_URL) -O $@
# print top level help
help:
cargo xtask help
# setup git lfs and git submodules
setup:
cargo setup
# update toolchain and dependencies
update:
cargo update-all
# put rootfs for linux mode
rootfs:
cargo rootfs --arch $(ARCH)
# put libc tests into rootfs
libc-test:
cargo libc-test --arch $(ARCH)
# put other tests into rootfs
other-test:
cargo other-test --arch $(ARCH)
# build image from rootfs
image:
cargo image --arch $(ARCH)
# check code style
check:
cargo check-style
# 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.
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

237
README.md
View File

@ -1,49 +1,30 @@
# zCore
[![CI](https://github.com/rcore-os/zCore/workflows/CI/badge.svg?branch=master)](https://github.com/rcore-os/zCore/actions)
[![Docs](https://img.shields.io/badge/docs-alpha-blue)](https://rcore-os.github.io/zCore/)
[![Docs](https://img.shields.io/badge/docs-alpha-blue)](https://rcore-os.github.io/zCore/zircon_object/)
[![Coverage Status](https://coveralls.io/repos/github/rcore-os/zCore/badge.svg?branch=master)](https://coveralls.io/github/rcore-os/zCore?branch=master)
Reimplement [Zircon][zircon] microkernel in safe Rust as a userspace program!
## Manual
[This](docs/Manual.md) is a new simple chinese manual.
## Dev Status
🚧 Working In Progress
- 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:
```sh
git clone https://github.com/rcore-os/zCore --recursive
cd zCore
git lfs install
git lfs pull
```
@ -53,211 +34,58 @@ 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 Zircon on bare-metal (zCore):
To debug, set the `LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
```sh
cd zCore && make run mode=release [graphic=on] [accel=1]
```
#### Run native Zircon program (shell) in zircon-libos mode:
Build and run your own Zircon user programs:
- step 1: Compile and Run Zircon shell
```sh
# See template in zircon-user
cd zircon-user && make zbi mode=release
```sh
cargo run --release --features "zircon libos" -- prebuilt/zircon/x64/bringup.zbi
```
# Run your programs in zCore
cd zCore && make run mode=release user=1
```
The `graphic` and `LOG` options are the same as Linux.
### Run zcore in bare-metal mode
#### 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.*'
cd zCore && make test mode=release [accel=1] test_filter='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.
cd script && python3 core-tests.py
```
#### 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.*'
```
Run all (non-panicked) core-tests for CI:
```sh
pip3 install pexpect
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
##
```
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)
Check `test-result.txt` for results.
## Components
@ -277,6 +105,3 @@ make doc
| Thread Management | `executor` | `async-std::task` |
| Exception Handling | Interrupt | Signal |
### Small Goal & Little Plans
- <https://github.com/rcore-os/zCore/wiki/Plans>

View File

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

View File

@ -1,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
/ #
```

View File

@ -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>
![](./structure.svg)
### 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-barearch下添加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的寄存器进行设置设置中断源的优先级分07级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>
在移植过程中,得到老师和童鞋们的很多帮助!感谢!

View File

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

View File

@ -1,270 +0,0 @@
// 解析设备树,创建已知的设备并为它们注册中断。
//
// 涉及到中断的设备包括:
//
// - 接收中断的中断控制器
// - 发出中断的设备
//
// 有效的中断控制器应该具有下列三个属性:
//
// - `interrupt-controller`: 指示这是一个中断控制器
// - `interrupt-cells`: 只是要向此控制器注册中断需要几个参数
// - `phandle`: 向此控制器注册中断时使用的一个号码,如果没有设备需要向它注册,可能不存在
//
// 设备注册中断需要 `interrupts_extended` 属性,这是一个 `Vec<u32>`,形式为 `[{phandle, ...,}*]`
// 即控制器引用和控制器指定数量的参数。
//! Probe devices and create drivers from device tree.
//!
//! Specification: <https://github.com/devicetree-org/devicetree-specification/releases/download/v0.3/devicetree-specification-v0.3.pdf>.
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use super::IoMapper;
use crate::{
utils::devicetree::{
parse_interrupts, parse_reg, Devicetree, InheritProps, InterruptsProp, Node, StringList,
},
Device, DeviceError, DeviceResult, VirtAddr,
};
const MODULE: &str = "device-tree";
type DevWithInterrupt = (Device, InterruptsProp);
/// 设备树中中断控制器特有的属性
struct IntcProps {
phandle: u32,
interrupt_cells: u32,
}
/// 查找表保存的中断控制器信息
struct Intc {
index: usize,
cells: usize,
}
/// A builder to probe devices and create drivers from device tree.
pub struct DevicetreeDriverBuilder<M: IoMapper> {
dt: Devicetree,
io_mapper: M,
}
impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Prepare to parse DTB from the given virtual address.
pub fn new(dtb_base_vaddr: VirtAddr, io_mapper: M) -> DeviceResult<Self> {
Ok(Self {
dt: Devicetree::from(dtb_base_vaddr)?,
io_mapper,
})
}
/// Parse the device tree from root, and returns an array of [`Device`] it found.
pub fn build(&self) -> DeviceResult<Vec<Device>> {
let mut intc_map = BTreeMap::new(); // phandle -> intc
let mut dev_list = Vec::new(); // devices
// 解析设备树
self.dt.walk(&mut |node, comp, props| {
debug!(
"{MODULE}: parsing node {:?} with compatible {comp:?}",
node.name
);
// parse interrupt controller
let res = if node.has_prop("interrupt-controller") {
self.parse_intc(node, comp, props).map(|(dev, intc)| {
intc_map.insert(
intc.phandle,
Intc {
index: dev_list.len(),
cells: intc.interrupt_cells as _,
},
);
dev
})
} else {
// parse other device
match comp {
#[cfg(feature = "virtio")]
c if c.contains("virtio,mmio") => self.parse_virtio(node, props),
c if c.contains("allwinner,sunxi-gmac") => {
self.parse_ethernet(node, comp, props)
}
c if c.contains("ns16550a") || c.contains("allwinner,sun20i-uart") => {
self.parse_uart(node, comp, props)
}
_ => Err(DeviceError::NotSupported),
}
};
match res {
Ok(dev) => dev_list.push(dev),
Err(DeviceError::NotSupported) => {}
Err(err) => warn!("{MODULE}: failed to parsing node {:?}: {err:?}", node.name),
}
});
// 注册中断
for (device, interrupts_extended) in &dev_list {
let mut extended = interrupts_extended.as_slice();
// 分解 interrupts_extended
while let [phandle, irq_num, ..] = extended {
if let Some(Intc { index, cells }) = intc_map.get(phandle) {
let (intc, _) = &dev_list[*index];
extended = &extended[1 + cells..];
if let Device::Irq(irq) = intc {
if *irq_num != 0xffff_ffff {
info!("{MODULE}: register interrupts for {intc:?}: {device:?}, irq_num={irq_num}");
if irq.register_device(*irq_num as _, device.inner()).is_ok() {
irq.unmask(*irq_num as _)?;
}
}
} else {
warn!("{MODULE}: node with phandle {phandle:#x} is not an interrupt-controller");
return Err(DeviceError::InvalidParam);
}
} else {
warn!(
"{MODULE}: no such node with phandle {phandle:#x} as the interrupt-parent"
);
return Err(DeviceError::InvalidParam);
}
}
}
// 丢弃中断信息
Ok(dev_list.into_iter().map(|(dev, _)| dev).collect())
}
}
#[allow(dead_code)]
#[allow(unused_imports)]
#[allow(unused_variables)]
#[allow(unreachable_code)]
impl<M: IoMapper> DevicetreeDriverBuilder<M> {
/// Parse nodes for interrupt controllers.
fn parse_intc(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<(DevWithInterrupt, IntcProps)> {
let phandle = node
.prop_u32("phandle")
.map_err(|_| DeviceError::InvalidParam)?;
let interrupt_cells = node
.prop_u32("#interrupt-cells")
.map_err(|_| DeviceError::InvalidParam)?;
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
use crate::irq::*;
let dev = Device::Irq(match comp {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
c if c.contains("riscv,cpu-intc") => Arc::new(riscv::Intc::new()),
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
c if c.contains("riscv,plic0") => Arc::new(riscv::Plic::new(base_vaddr?)),
_ => return Err(DeviceError::NotSupported),
});
Ok((
(dev, interrupts_extended),
IntcProps {
phandle,
interrupt_cells,
},
))
}
/// Parse nodes for virtio devices over MMIO.
#[cfg(feature = "virtio")]
fn parse_virtio(&self, node: &Node, props: &InheritProps) -> DeviceResult<DevWithInterrupt> {
use crate::virtio::*;
use virtio_drivers::{DeviceType, VirtIOHeader};
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
})?;
let header = unsafe { &mut *(base_vaddr as *mut VirtIOHeader) };
if !header.verify() {
return Err(DeviceError::NotSupported);
}
info!(
"{MODULE}: detected virtio device: vendor_id={:#X}, type={:?}",
header.vendor_id(),
header.device_type()
);
let dev = match header.device_type() {
DeviceType::Block => Device::Block(Arc::new(VirtIoBlk::new(header)?)),
DeviceType::GPU => Device::Display(Arc::new(VirtIoGpu::new(header)?)),
DeviceType::Input => Device::Input(Arc::new(VirtIoInput::new(header)?)),
DeviceType::Console => Device::Uart(Arc::new(VirtIoConsole::new(header)?)),
_ => return Err(DeviceError::NotSupported),
};
Ok((dev, interrupts_extended))
}
/// Parse nodes for Ethernet devices.
fn parse_ethernet(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
info!("Ethernet gmac init ...");
let irq_num = interrupts_extended[1];
use crate::net::*;
let dev = Device::Net(match comp {
#[cfg(target_arch = "riscv64")]
c if c.contains("allwinner,sunxi-gmac") => {
Arc::new(rtlx_init(irq_num as usize, |paddr, size| {
self.io_mapper.query_or_map(paddr, size)
})?)
}
_ => return Err(DeviceError::NotSupported),
});
Ok((dev, interrupts_extended))
}
/// Parse nodes for UART devices.
fn parse_uart(
&self,
node: &Node,
comp: &StringList,
props: &InheritProps,
) -> DeviceResult<DevWithInterrupt> {
let interrupts_extended = parse_interrupts(node, props)?;
let base_vaddr = parse_reg(node, props).and_then(|(paddr, size)| {
self.io_mapper
.query_or_map(paddr as usize, size as usize)
.ok_or(DeviceError::NoResources)
});
use crate::uart::*;
let dev = Device::Uart(match comp {
c if c.contains("ns16550a") => {
Arc::new(unsafe { Uart16550Mmio::<u8>::new(base_vaddr?) })
}
c if c.contains("allwinner,sun20i-uart") => {
Arc::new(unsafe { Uart16550Mmio::<u32>::new(base_vaddr?) })
}
_ => return Err(DeviceError::NotSupported),
});
Ok((dev, interrupts_extended))
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,22 +0,0 @@
//! External interrupt request and handle.
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] {
mod riscv_intc;
mod riscv_plic;
/// Implementation of risc-v interrupt controller.
#[doc(cfg(any(target_arch = "riscv32", target_arch = "riscv64")))]
pub mod riscv {
pub use super::riscv_intc::{Intc, ScauseIntCode};
pub use super::riscv_plic::Plic;
}
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
mod x86_apic;
/// Implementation of x86 Advanced Programmable Interrupt Controller.
#[doc(cfg(any(target_arch = "x86", target_arch = "x86_64")))]
pub mod x86 {
pub use super::x86_apic::Apic;
}
}
}

View File

@ -1,131 +0,0 @@
use lock::Mutex;
use riscv::register::sie;
use crate::prelude::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
use crate::{DeviceError, DeviceResult};
use alloc::format;
use alloc::string::String;
use core::sync::atomic::{AtomicU8, Ordering};
const S_SOFT: usize = 1;
const S_TIMER: usize = 5;
const S_EXT: usize = 9;
static INTC_NUM: AtomicU8 = AtomicU8::new(0);
#[repr(usize)]
pub enum ScauseIntCode {
SupervisorSoft = S_SOFT,
SupervisorTimer = S_TIMER,
SupervisorExternal = S_EXT,
}
pub struct Intc {
name: String,
soft_handler: Mutex<Option<IrqHandler>>,
timer_handler: Mutex<Option<IrqHandler>>,
ext_handler: Mutex<Option<IrqHandler>>,
}
impl Intc {
pub fn new() -> Self {
Self {
name: format!("riscv-intc-cpu{}", INTC_NUM.fetch_add(1, Ordering::Relaxed)),
soft_handler: Mutex::new(None),
timer_handler: Mutex::new(None),
ext_handler: Mutex::new(None),
}
}
fn with_handler<F>(&self, cause: usize, op: F) -> DeviceResult
where
F: FnOnce(&mut Option<IrqHandler>) -> DeviceResult,
{
match cause {
S_SOFT => op(&mut self.soft_handler.lock()),
S_TIMER => op(&mut self.timer_handler.lock()),
S_EXT => op(&mut self.ext_handler.lock()),
_ => {
error!("invalid SCAUSE value {:#x}!", cause);
Err(DeviceError::InvalidParam)
}
}
}
}
impl Default for Intc {
fn default() -> Self {
Self::new()
}
}
impl Scheme for Intc {
fn name(&self) -> &str {
self.name.as_str()
}
fn handle_irq(&self, cause: usize) {
self.with_handler(cause, |opt| {
if let Some(h) = opt {
h();
} else {
warn!("no registered handler for SCAUSE {}!", cause);
}
Ok(())
})
.unwrap();
}
}
impl IrqScheme for Intc {
fn is_valid_irq(&self, cause: usize) -> bool {
matches!(cause, S_SOFT | S_TIMER | S_EXT)
}
fn mask(&self, cause: usize) -> DeviceResult {
unsafe {
match cause {
S_SOFT => sie::clear_ssoft(),
S_TIMER => sie::clear_stimer(),
S_EXT => sie::clear_sext(),
_ => return Err(DeviceError::InvalidParam),
}
}
Ok(())
}
fn unmask(&self, cause: usize) -> DeviceResult {
unsafe {
match cause {
S_SOFT => sie::set_ssoft(),
S_TIMER => sie::set_stimer(),
S_EXT => sie::set_sext(),
_ => return Err(DeviceError::InvalidParam),
}
}
Ok(())
}
fn register_handler(&self, cause: usize, handler: IrqHandler) -> DeviceResult {
self.with_handler(cause, |opt| {
if opt.is_some() {
Err(DeviceError::AlreadyExists)
} else {
*opt = Some(handler);
Ok(())
}
})
}
fn unregister(&self, cause: usize) -> DeviceResult {
self.with_handler(cause, |opt| {
if opt.is_some() {
*opt = None;
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
})
}
}

View File

@ -1,171 +0,0 @@
use core::arch::asm;
use core::ops::Range;
use lock::Mutex;
use crate::io::{Io, Mmio};
use crate::prelude::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult};
const IRQ_RANGE: Range<usize> = 1..1024;
const PLIC_PRIORITY_BASE: usize = 0x0;
const PLIC_ENABLE_BASE: usize = 0x2080;
const PLIC_CONTEXT_BASE: usize = 0x20_1000;
const PLIC_CONTEXT_THRESHOLD: usize = 0x0;
const PLIC_CONTEXT_CLAIM: usize = 0x4 / core::mem::size_of::<u32>();
const PLIC_ENABLE_HART_OFFSET: usize = 0x100 / core::mem::size_of::<u32>();
const PLIC_PRIORITY_HART_OFFSET: usize = 0x2000 / core::mem::size_of::<u32>();
const PLIC_CONTEXT_CLAIM_HART_OFFSET: usize = 0x2000 / core::mem::size_of::<u32>();
struct PlicUnlocked {
priority_base: &'static mut Mmio<u32>,
enable_base: &'static mut Mmio<u32>,
context_base: &'static mut Mmio<u32>,
manager: IrqManager<1024>,
}
pub struct Plic {
inner: Mutex<PlicUnlocked>,
}
impl PlicUnlocked {
/// Toggle irq enable on the current hart.
fn toggle(&mut self, irq_num: usize, enable: bool) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
let hart_id = cpu_id() as usize;
let mmio = self
.enable_base
.add(PLIC_ENABLE_HART_OFFSET * hart_id + irq_num / 32);
let mask = 1 << (irq_num % 32);
if enable {
mmio.write(mmio.read() | mask);
} else {
mmio.write(mmio.read() & !mask);
}
}
/// Ask the PLIC what type of interrupt is occurred on the current hart.
fn pending_irq(&mut self) -> Option<usize> {
let hart_id = cpu_id() as usize;
let irq_num = self
.context_base
.add(PLIC_CONTEXT_CLAIM_HART_OFFSET * hart_id + PLIC_CONTEXT_CLAIM)
.read() as usize;
if irq_num == 0 {
None
} else {
Some(irq_num)
}
}
/// Tell the PLIC we've served this IRQ.
fn eoi(&mut self, irq_num: usize) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
let hart_id = cpu_id() as usize;
self.context_base
.add(PLIC_CONTEXT_CLAIM + PLIC_CONTEXT_CLAIM_HART_OFFSET * hart_id)
.write(irq_num as _);
}
/// Set the priority for the irq_num.
fn set_priority(&mut self, irq_num: usize, priority: u8) {
debug_assert!(IRQ_RANGE.contains(&irq_num));
self.priority_base.add(irq_num).write(priority as _);
}
/// Set current hart's priority threshold to 0.
fn set_threshold(&mut self, threshold: u8) {
let hart_id = cpu_id() as usize;
self.context_base
.add(PLIC_PRIORITY_HART_OFFSET * hart_id + PLIC_CONTEXT_THRESHOLD)
.write(threshold as _);
}
fn init_hart(&mut self) {
self.set_threshold(0);
}
}
impl Plic {
pub fn new(base: usize) -> Self {
let mut inner = PlicUnlocked {
priority_base: unsafe { Mmio::<u32>::from_base(base + PLIC_PRIORITY_BASE) },
enable_base: unsafe { Mmio::<u32>::from_base(base + PLIC_ENABLE_BASE) },
context_base: unsafe { Mmio::<u32>::from_base(base + PLIC_CONTEXT_BASE) },
manager: IrqManager::new(IRQ_RANGE),
};
inner.init_hart();
Self {
inner: Mutex::new(inner),
}
}
}
impl Scheme for Plic {
fn name(&self) -> &str {
"riscv-plic"
}
fn handle_irq(&self, _unused: usize) {
let mut inner = self.inner.lock();
while let Some(irq_num) = inner.pending_irq() {
if inner.manager.handle(irq_num).is_err() {
warn!("no registered handler for IRQ {}!", irq_num);
}
trace!("riscv plic handle irq: {}", irq_num);
inner.eoi(irq_num);
}
}
}
impl IrqScheme for Plic {
fn is_valid_irq(&self, irq_num: usize) -> bool {
IRQ_RANGE.contains(&irq_num)
}
fn mask(&self, irq_num: usize) -> DeviceResult {
if self.is_valid_irq(irq_num) {
self.inner.lock().toggle(irq_num, false);
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
}
fn unmask(&self, irq_num: usize) -> DeviceResult {
if self.is_valid_irq(irq_num) {
self.inner.lock().toggle(irq_num, true);
Ok(())
} else {
Err(DeviceError::InvalidParam)
}
}
fn register_handler(&self, irq_num: usize, handler: IrqHandler) -> DeviceResult {
let mut inner = self.inner.lock();
inner.manager.register_handler(irq_num, handler).map(|_| {
inner.set_priority(irq_num, 7);
})
}
fn unregister(&self, irq_num: usize) -> DeviceResult {
self.inner.lock().manager.unregister_handler(irq_num)
}
fn init_hart(&self) {
self.inner.lock().init_hart();
}
}
fn cpu_id() -> u8 {
let mut cpu_id;
unsafe {
asm!("mv {0}, tp", out(reg) cpu_id);
}
cpu_id
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,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)
}

View File

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

View File

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

View File

@ -1,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;

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,36 @@
[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.5"
git-version = "0.3"
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" }
# trapframe = "0.4.1"
trapframe = { git = "https://github.com/rcore-os/trapframe-rs", rev = "fed9668" }
device_tree = { git = "https://github.com/rcore-os/device_tree-rs", rev = "eee2c23" }
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.11"
uart_16550 = "0.2.6"
raw-cpuid = "8.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.0.0"
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"], rev = "c62af46" }
[target.'cfg(target_arch = "mips")'.dependencies]
mips = { git = "https://github.com/Harry-Chen/rust-mips", rev = "3b828a2" }

View File

@ -0,0 +1,192 @@
use crate::drivers::IRQ_MANAGER;
use mips::addr::*;
use mips::paging::PageTable as MIPSPageTable;
use mips::registers::cp0;
use trapframe::TrapFrame;
/// Initialize interrupt
pub fn intr_init() {
unsafe {
trapframe::init();
}
let mut status = cp0::status::read();
// Enable IPI
// status.enable_soft_int0();
// status.enable_soft_int1();
// Enable serial interrupt
status.enable_hard_int2();
// Enable clock interrupt in timer::init
// status.enable_hard_int5();
cp0::status::write(status);
info!("interrupt: init end");
}
#[export_name = "hal_page_fault"]
pub fn is_page_fault(trap: usize) -> bool {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause { bits: trap as u32 };
match cause.cause() {
E::TLBModification | E::TLBLoadMiss | E::TLBStoreMiss => true,
_ => false,
}
}
#[export_name = "hal_is_syscall"]
pub fn is_syscall(trap: usize) -> bool {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause { bits: trap as u32 };
match cause.cause() {
E::Syscall => true,
_ => false,
}
}
#[export_name = "hal_is_intr"]
pub fn is_intr(trap: usize) -> bool {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause { bits: trap as u32 };
match cause.cause() {
E::Interrupt => true,
_ => false,
}
}
#[export_name = "hal_is_timer_intr"]
pub fn is_timer_intr(trap: usize) -> bool {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause { bits: trap as u32 };
match cause.cause() {
E::Interrupt => trap & (1 << 30) != 0,
_ => false,
}
}
#[export_name = "hal_is_reserved_inst"]
pub fn is_reserved_inst(trap: usize) -> bool {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause { bits: trap as u32 };
match cause.cause() {
E::ReservedInstruction => true,
_ => false,
}
}
#[export_name = "hal_wait_for_interrupt"]
pub fn wait_for_interrupt() {
cp0::status::enable_interrupt();
cp0::status::disable_interrupt();
}
#[export_name = "hal_irq_enable"]
pub fn irq_enable(_irq: u32) {
// unimplemented!()
warn!("unimplemented irq_enable");
}
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
use cp0::cause::Exception as E;
let cause = cp0::cause::Cause {
bits: tf.cause as u32,
};
info!("Exception @ CPU{}: {:?} ", 0, cause.cause());
match cause.cause() {
E::Interrupt => interrupt_dispatcher(tf),
// E::Syscall => syscall(tf),
E::TLBModification => page_fault(tf),
E::TLBLoadMiss => page_fault(tf),
E::TLBStoreMiss => page_fault(tf),
UNKNOWN => {
error!("Unhandled Exception @ CPU{}: {:?} ", 0, UNKNOWN);
}
}
trace!("Interrupt end");
}
fn interrupt_dispatcher(tf: &mut TrapFrame) {
let cause = cp0::cause::Cause {
bits: tf.cause as u32,
};
let pint = cause.pending_interrupt();
trace!(" Interrupt {:08b} ", pint);
if (pint & 0b100_000_00) != 0 {
timer();
} else if (pint & 0b011_111_00) != 0 {
for i in 0..6 {
if (pint & (1 << i)) != 0 {
IRQ_MANAGER.read().try_handle_interrupt(Some(i));
}
}
} else {
ipi();
}
}
fn ipi() {
debug!("IPI");
cp0::cause::reset_soft_int0();
cp0::cause::reset_soft_int1();
}
pub fn timer() {
super::timer::set_next();
crate::timer_tick();
}
fn page_fault(tf: &mut TrapFrame) {
// TODO: set access/dirty bit
let addr = tf.vaddr;
// info!("\nEXCEPTION: Page Fault @ {:#x}", addr);
let virt_addr = VirtAddr::new(addr);
error!("{:x}", super::memory::get_page_table());
let root_table = unsafe { &mut *(super::memory::get_page_table() as *mut MIPSPageTable) };
let tlb_result = root_table.lookup(addr);
match tlb_result {
Ok(tlb_entry) => {
trace!(
"PhysAddr = {:x}/{:x}",
tlb_entry.entry_lo0.get_pfn() << 12,
tlb_entry.entry_lo1.get_pfn() << 12
);
let tlb_valid = if virt_addr.page_number() & 1 == 0 {
tlb_entry.entry_lo0.valid()
} else {
tlb_entry.entry_lo1.valid()
};
if !tlb_valid {
panic!("hhh");
// if !crate::memory::handle_page_fault(addr) {
// extern "C" {
// fn _copy_user_start();
// fn _copy_user_end();
// }
// if tf.epc >= _copy_user_start as usize && tf.epc < _copy_user_end as usize {
// debug!("fixup for addr {:x?}", addr);
// tf.epc = crate::read_user_fixup as usize;
// return;
// }
// }
}
tlb_entry.write_random()
}
Err(()) => {
// if !crate::memory::handle_page_fault(addr) {
// extern "C" {
// fn _copy_user_start();
// fn _copy_user_end();
// }
// if tf.epc >= _copy_user_start as usize && tf.epc < _copy_user_end as usize {
// debug!("fixup for addr {:x?}", addr);
// tf.epc = crate::read_user_fixup as usize;
// return;
// }
// }
panic!("...");
}
}
}

View File

@ -0,0 +1,10 @@
//! Input/output for mipsel.
use crate::drivers::SERIAL_DRIVERS;
use core::fmt::Arguments;
pub fn putfmt(fmt: Arguments) {
let mut drivers = SERIAL_DRIVERS.write();
if let Some(serial) = drivers.first_mut() {
serial.write(format!("{}", fmt).as_bytes());
}
}

View File

@ -0,0 +1,15 @@
#[allow(dead_code)]
extern "C" {
fn _root_page_table_buffer();
fn _root_page_table_ptr();
}
pub unsafe fn set_page_table(vmtoken: usize) {
use mips::tlb::TLBEntry;
TLBEntry::clear_all();
*(_root_page_table_ptr as *mut usize) = vmtoken;
}
pub fn get_page_table() -> usize {
unsafe { *(_root_page_table_ptr as *mut usize) }
}

View File

@ -0,0 +1,65 @@
pub mod interrupt;
mod io;
mod memory;
mod timer;
pub use interrupt::*;
pub use io::*;
pub use memory::*;
pub use timer::*;
use super::super::{Frame, PhysAddr};
use mips::paging::PageTable as MIPSPageTable;
pub struct Config {}
pub fn init(_config: Config) {
intr_init();
unsafe {
set_page_table(0xFFFF_FFFF);
}
timer_init();
}
#[export_name = "hal_apic_local_id"]
pub fn apic_local_id() -> u8 {
// unimplemented!()
0
}
/// Page Table
#[repr(C)]
pub struct PageTableImpl {
root_paddr: PhysAddr,
}
impl PageTableImpl {
#[export_name = "hal_pt_current"]
pub fn current() -> Self {
PageTableImpl {
root_paddr: get_page_table() & 0x7fffffff,
}
}
/// 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 table = unsafe { &mut *(root_frame.paddr as *mut MIPSPageTable) };
table.zero();
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) }
// unimplemented!()
// }
}

View File

@ -0,0 +1,32 @@
use core::time::Duration;
use log::*;
use mips::registers::cp0;
static mut TICK: u64 = 0;
const TIMEBASE: u32 = 250000;
/// Enable timer interrupt
pub fn timer_init() {
// Enable supervisor timer interrupt
cp0::status::enable_hard_int5(); // IP(7), timer interrupt
cp0::count::write_u32(0);
set_next();
info!("timer: init end");
}
/// Set the next timer interrupt
pub fn set_next() {
// 100Hz @ QEMU
cp0::count::write_u32(0);
cp0::compare::write_u32(TIMEBASE);
unsafe {
TICK += 1;
}
}
#[export_name = "hal_timer_now"]
pub fn timer_now() -> Duration {
let mut curr_time = unsafe { TICK * TIMEBASE as u64 };
curr_time += cp0::count::read_u32() as u64;
Duration::from_nanos(curr_time * 10)
}

View File

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

View File

@ -0,0 +1,151 @@
use super::*;
use kernel_hal::{PageTableTrait, PhysAddr, VirtAddr};
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() {}

View File

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

View File

@ -0,0 +1,382 @@
#![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()
}
#[export_name = "hal_wait_for_interrupt"]
pub fn wait_for_interrupt() {
x86_64::instructions::interrupts::enable_interrupts_and_hlt();
x86_64::instructions::interrupts::disable();
}
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;

View File

@ -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
}

View File

@ -0,0 +1,499 @@
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::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, *},
},
};
use kernel_hal::vdso::{Features, VdsoConstants};
mod acpi_table;
pub mod interrupt;
mod keyboard;
pub use super::super::phys_to_virt;
/// 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(err) => {
debug!(
"unmap failed: {:x?} err={:x?} in {:#x?}",
vaddr, err, self.root_paddr
);
return Err(());
}
}
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(());
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>) -> 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 FnOnce() + 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);
for callback in STDIN_CALLBACK.lock().drain(..) {
callback();
}
}
#[export_name = "hal_serial_set_callback"]
pub fn serial_set_callback(callback: Box<dyn FnOnce() + 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
}

View File

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

View File

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

View File

@ -0,0 +1,59 @@
pub mod device_tree;
pub mod irq;
pub mod serial;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::RwLock;
pub use serial::SerialDriver;
#[derive(Debug, Eq, PartialEq)]
pub enum DeviceType {
Net,
Gpu,
Input,
Block,
Rtc,
Serial,
Intc,
}
pub trait Driver: Send + Sync {
// if interrupt belongs to this driver, handle it and return true
// return false otherwise
// irq number is provided when available
// driver should skip handling when irq number is mismatched
fn try_handle_interrupt(&self, irq: Option<usize>) -> bool;
// return the correspondent device type, see DeviceType
fn device_type(&self) -> DeviceType;
// get unique identifier for this device
// should be different for each instance
fn get_id(&self) -> String;
// trait casting
// fn as_net(&self) -> Option<&dyn NetDriver> {
// None
// }
// fn as_block(&self) -> Option<&dyn BlockDriver> {
// None
// }
// fn as_rtc(&self) -> Option<&dyn RtcDriver> {
// None
// }
}
lazy_static! {
// NOTE: RwLock only write when initializing drivers
pub static ref DRIVERS: RwLock<Vec<Arc<dyn Driver>>> = RwLock::new(Vec::new());
// pub static ref NET_DRIVERS: RwLock<Vec<Arc<dyn NetDriver>>> = RwLock::new(Vec::new());
// pub static ref BLK_DRIVERS: RwLock<Vec<Arc<dyn BlockDriver>>> = RwLock::new(Vec::new());
// pub static ref RTC_DRIVERS: RwLock<Vec<Arc<dyn RtcDriver>>> = RwLock::new(Vec::new());
pub static ref SERIAL_DRIVERS: RwLock<Vec<Arc<dyn SerialDriver>>> = RwLock::new(Vec::new());
pub static ref IRQ_MANAGER: RwLock<irq::IrqManager> = RwLock::new(irq::IrqManager::new(true));
}

View File

@ -0,0 +1,11 @@
use super::Driver;
pub mod uart16550;
pub trait SerialDriver: Driver {
// read one byte from tty
fn read(&self) -> u8;
// write bytes to tty
fn write(&self, data: &[u8]);
}

View File

@ -0,0 +1,166 @@
//! 16550 serial adapter driver for malta board
#![allow(dead_code)]
use super::SerialDriver;
use crate::drivers::device_tree::{DEVICE_TREE_INTC, DEVICE_TREE_REGISTRY};
use crate::drivers::IRQ_MANAGER;
use crate::drivers::SERIAL_DRIVERS;
use crate::drivers::{DeviceType, Driver, DRIVERS};
use crate::{
phys_to_virt,
util::{read, write},
};
use alloc::{string::String, sync::Arc};
// use core::fmt::{Arguments, Result, Write};
use device_tree::Node;
// use spin::Mutex;
pub struct SerialPort {
base: usize,
multiplier: usize,
}
impl Driver for SerialPort {
fn try_handle_interrupt(&self, _irq: Option<usize>) -> bool {
unimplemented!()
// if let Some(c) = self.getchar_option() {
// crate::trap::serial(c);
// true
// } else {
// false
// }
}
fn device_type(&self) -> DeviceType {
DeviceType::Serial
}
fn get_id(&self) -> String {
format!("com_{}", self.base)
}
}
impl SerialPort {
fn new(base: usize, shift: usize) -> SerialPort {
let mut res = SerialPort {
base: 0,
multiplier: 1 << shift,
};
res.init(base);
res
}
pub fn init(&mut self, base: usize) {
self.base = base;
// Turn off the FIFO
write(self.base + COM_FCR * self.multiplier, 0 as u8);
// Set speed; requires DLAB latch
write(self.base + COM_LCR * self.multiplier, COM_LCR_DLAB);
// write(self.base + COM_DLL * self.multiplier, (115200 / 9600) as u8);
// write(self.base + COM_DLM * self.multiplier, 0 as u8);
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
write(
self.base + COM_LCR * self.multiplier,
COM_LCR_WLEN8 & !COM_LCR_DLAB,
);
// No modem controls
write(self.base + COM_MCR * self.multiplier, 0 as u8);
// Enable rcv interrupts
write(self.base + COM_IER * self.multiplier, COM_IER_RDI);
}
/// non-blocking version of putchar()
pub fn putchar(&self, c: u8) {
for _ in 0..100 {
if (read::<u8>(self.base + COM_LSR * self.multiplier) & COM_LSR_TXRDY) == 0 {
break;
}
}
write(self.base + COM_TX * self.multiplier, c);
}
/// blocking version of getchar()
pub fn getchar(&mut self) -> u8 {
loop {
if (read::<u8>(self.base + COM_LSR * self.multiplier) & COM_LSR_DATA) == 0 {
break;
}
}
let c = read::<u8>(self.base + COM_RX * self.multiplier);
match c {
255 => b'\0', // null
c => c,
}
}
/// non-blocking version of getchar()
pub fn getchar_option(&self) -> Option<u8> {
match read::<u8>(self.base + COM_LSR * self.multiplier) & COM_LSR_DATA {
0 => None,
_ => Some(read::<u8>(self.base + COM_RX * self.multiplier) as u8),
}
}
}
impl SerialDriver for SerialPort {
fn read(&self) -> u8 {
self.getchar_option().unwrap_or(0)
}
fn write(&self, data: &[u8]) {
for byte in data {
self.putchar(*byte);
}
}
}
const COM_RX: usize = 0; // In: Receive buffer (DLAB=0)
const COM_TX: usize = 0; // Out: Transmit buffer (DLAB=0)
const COM_DLL: usize = 0; // Out: Divisor Latch Low (DLAB=1)
const COM_DLM: usize = 1; // Out: Divisor Latch High (DLAB=1)
const COM_IER: usize = 1; // Out: Interrupt Enable Register
const COM_IER_RDI: u8 = 0x01; // Enable receiver data interrupt
const COM_IIR: usize = 2; // In: Interrupt ID Register
const COM_FCR: usize = 2; // Out: FIFO Control Register
const COM_LCR: usize = 3; // Out: Line Control Register
const COM_LCR_DLAB: u8 = 0x80; // Divisor latch access bit
const COM_LCR_WLEN8: u8 = 0x03; // Wordlength: 8 bits
const COM_MCR: usize = 4; // Out: Modem Control Register
const COM_MCR_RTS: u8 = 0x02; // RTS complement
const COM_MCR_DTR: u8 = 0x01; // DTR complement
const COM_MCR_OUT2: u8 = 0x08; // Out2 complement
const COM_LSR: usize = 5; // In: Line Status Register
const COM_LSR_DATA: u8 = 0x01; // Data available
const COM_LSR_TXRDY: u8 = 0x20; // Transmit buffer avail
const COM_LSR_TSRE: u8 = 0x40; // Transmitter off
pub fn init_dt(dt: &Node) {
let addr = dt.prop_usize("reg").unwrap();
let shift = dt.prop_u32("reg-shift").unwrap_or(0) as usize;
let base = phys_to_virt(addr);
info!("Init uart16550 at {:#x}", base);
let com = Arc::new(SerialPort::new(base, shift));
let mut found = false;
let irq_opt = dt.prop_u32("interrupts").ok().map(|irq| irq as usize);
DRIVERS.write().push(com.clone());
SERIAL_DRIVERS.write().push(com.clone());
if let Ok(intc) = dt.prop_u32("interrupt-parent") {
if let Some(irq) = irq_opt {
if let Some(manager) = DEVICE_TREE_INTC.write().get_mut(&intc) {
manager.register_local_irq(irq, com.clone());
info!("registered uart16550 to intc");
found = true;
}
}
}
if !found {
info!("registered uart16550 to root");
IRQ_MANAGER.write().register_opt(irq_opt, com);
}
}
pub fn driver_init() {
DEVICE_TREE_REGISTRY.write().insert("ns16550a", init_dt);
}

244
kernel-hal-bare/src/lib.rs Normal file
View File

@ -0,0 +1,244 @@
//! 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)]
#![feature(naked_functions)]
#![deny(warnings)]
#[macro_use]
extern crate log;
#[macro_use]
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 mod drivers;
pub mod util;
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 }
}
}
#[cfg(not(target_arch = "mips"))]
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
unsafe { PMEM_BASE + paddr }
}
/// MIPS is special
#[cfg(target_arch = "mips")]
pub fn phys_to_virt(paddr: usize) -> usize {
const PHYSICAL_MEMORY_OFFSET: usize = 0x8000_0000;
if paddr <= PHYSICAL_MEMORY_OFFSET {
PHYSICAL_MEMORY_OFFSET + paddr
} else {
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());
}
}
/// 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);
}
}
/// Zero `target` frame.
#[export_name = "hal_frame_zero"]
pub fn frame_zero_in_range(target: PhysAddr, start: usize, end: usize) {
assert!(start < PAGE_SIZE && end <= PAGE_SIZE);
trace!("frame_zero: {:#x}", target);
unsafe {
core::ptr::write_bytes(phys_to_virt(target + start) as *mut u8, 0, end - start);
}
}
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);
}
#[naked]
pub unsafe extern "C" fn read_user_fixup() -> usize {
return 1;
}
/// 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;
}

View File

@ -0,0 +1,15 @@
use core::ptr::{read_volatile, write_volatile};
#[inline(always)]
pub fn write<T>(addr: usize, content: T) {
let cell = (addr) as *mut T;
unsafe {
write_volatile(cell, content);
}
}
#[inline(always)]
pub fn read<T>(addr: usize) -> T {
let cell = (addr) as *const T;
unsafe { read_volatile(cell) }
}

View File

@ -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.5"
git-version = "0.3"
trapframe = { git = "https://github.com/rcore-os/trapframe-rs", rev = "fed9668" }

446
kernel-hal-unix/src/lib.rs Normal file
View File

@ -0,0 +1,446 @@
#![feature(asm, global_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<'_>) -> 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 extern "C" 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_00000000;
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());
}
}
/// 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);
}
}
/// Zero `target` frame.
#[export_name = "hal_frame_zero"]
pub fn frame_zero(target: PhysAddr) {
trace!("frame_zero: {:#x}", target);
assert!(target + PAGE_SIZE < PMEM_SIZE);
ensure_mmap_pmem();
unsafe {
core::ptr::write_bytes(phys_to_virt(target) as *mut u8, 0, 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 = 0x400_00000; // 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 FnOnce() + Send + Sync>>> = Mutex::new(Vec::new());
}
/// Put a char by serial interrupt handler.
fn serial_put(x: u8) {
STDIN.lock().unwrap().push_back(x);
for callback in STDIN_CALLBACK.lock().unwrap().drain(..) {
callback();
}
}
#[export_name = "hal_serial_set_callback"]
pub fn serial_set_callback(callback: Box<dyn FnOnce() + 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();
}
}

View File

@ -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);
}
}
}
}

View File

@ -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 = { git = "https://github.com/rcore-os/trapframe-rs", rev = "fed9668" }
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.0.0"

View File

@ -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,
}

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