Compare commits

...

441 Commits
net ... master

Author SHA1 Message Date
Luoyuan Xiao d74dfad991
Merge pull request #302 from elliott10/master
Enable PCI scan and e1000 driver in RISCV
2022-05-30 16:29:30 +08:00
Luoyuan Xiao b9e13aaab1 Merge remote-tracking branch 'remotes/origin/master' 2022-05-30 16:28:39 +08:00
shzhxh 6d951829b6 According to the result of CI test, change the status of some testcases from OK to PARTIAL 2022-05-30 16:19:19 +08:00
Luoyuan Xiao 6ab43eb9d8 To meet test CI conditions 2022-05-27 14:21:33 +08:00
Luoyuan Xiao 0f1ea71bfb Enable PCI scan in RISCV and x86_64
Get e1000e network driver to work in RISCV
Test net testcases
2022-05-27 13:33:14 +08:00
Luoyuan Xiao 3daccb7e42 Merge remote-tracking branch 'remotes/origin/master' 2022-05-26 17:53:58 +08:00
Luoyuan Xiao 00b10bd880 Update tests subdir
Fixed to pass cargo clippy
2022-05-26 16:31:09 +08:00
Luoyuan Xiao 42eb5c0708
Merge pull request #301 from YdrMaster/main
适用于 libos 的 musl libc so 不再依赖 git lfs
2022-05-21 15:18:02 +08:00
YdrMaster 0aa57d8021 fix: 提前退出 2022-05-21 13:35:24 +08:00
YdrMaster b67cbc2398 feat(ci): 重新打开依赖 lfs 的测例 2022-05-21 12:47:42 +08:00
YdrMaster 302ecf1990 feat(ci): 适用于 libos 的 musl libc so 不再依赖 git lfs 2022-05-21 12:27:40 +08:00
Luoyuan Xiao 0e984e9777
Merge pull request #285 from DeathWish5/master
Singal bug fix and Executor update
2022-05-21 10:20:40 +08:00
DeathWish5 6ef8dc7a59 Merge branch 'master' of github.com:rcore-os/zCore 2022-05-20 20:46:29 +08:00
DeathWish5 f1ffe1d60e cargo fmt and clippy 2022-05-20 20:45:35 +08:00
DeathWish5 bba9878981 bug fix: some signal testcases may fail 2022-05-20 19:54:33 +08:00
Luoyuan Xiao 19f5cf712f Add e1000 driver, and some test cases
Modify smoltcp reference link
2022-05-20 14:33:30 +08:00
workerwork 6b30fe8b40 fix: github ci 2022-05-19 15:21:26 +08:00
workerwork 3b9b255ec9 fix: github ci 2022-05-19 15:21:26 +08:00
workerwork 181b577cae refactor: github ci 2022-05-19 15:21:26 +08:00
DeathWish5 e41bf17a84 feat: add init of x86 apic timer 2022-05-18 23:24:38 +08:00
DeathWish5 c3ab0d38c4 bug fix: unknown syscall may happened. log: add some debug log 2022-05-18 17:13:13 +08:00
DeathWish5 19bc2b7991 Merge branch 'master' of github.com:rcore-os/zCore 2022-05-17 13:40:55 +08:00
Luoyuan Xiao 13fb9eebbc
Merge pull request #296 from YdrMaster/main
增加基于 xtask 的调试操作
2022-05-16 09:48:53 +08:00
YdrMaster 4dd51126b6 fix(driver): 跳过不支持的中断号,不要退出 2022-05-15 19:28:30 +08:00
YdrMaster 939b6395c5 refactor(drivers): 使用重构的设备树解析 2022-05-15 18:54:50 +08:00
YdrMaster f542b3cc6f fix(xtask): 适应 riscv64
让 xtask 不要影响 rust-analyzer 在 riscv64 target 下的扫描
2022-05-14 20:08:24 +08:00
YdrMaster 1e74f6a988 style 2022-05-14 08:53:07 +08:00
YdrMaster 794c807e34 feat(xtask): 增加 riscv64 gdb 2022-05-13 18:50:30 +08:00
YdrMaster 6f4dfd7273 fix(xtask): rename 无法跨 mount 文件系统 2022-05-13 16:59:25 +08:00
YdrMaster e1de630c4f feat(xtask): 增加 asm 命令,将内核反汇编输出到文件 2022-05-13 14:57:02 +08:00
YdrMaster 6749cd5179 refactor(xtask): 选择架构改用参数 2022-05-13 13:34:25 +08:00
YdrMaster 3fcc1f42f3 feat(xtask): 下载 rustsbi 2022-05-12 22:06:55 +08:00
Ydr 3b706cf262
Merge branch 'rcore-os:master' into main 2022-05-12 22:04:27 +08:00
YdrMaster 9d8316b9b6 feat(xtask): 从顶层启动 qemu 2022-05-12 19:43:45 +08:00
Ydr f2aaa8d8f6
Merge pull request #294 from YdrMaster/main
修复 d1 的启动问题
2022-05-12 18:46:10 +08:00
Zhenxing 134a31f0c3
Merge pull request #293 from elliott10/master
To accommodate large user programs
2022-05-12 17:32:42 +08:00
YdrMaster 3d0186d95e feat(xtask): cargo check-style 现在和 CI/build 一致 2022-05-12 17:01:37 +08:00
YdrMaster ebd4de4682 style 2022-05-12 16:56:55 +08:00
Luoyuan Xiao 9490e5d7a8 To accommodate large user programs
increased user stack, kernel stack and heap
2022-05-12 16:35:35 +08:00
YdrMaster b6bca47e94 fix(d1): 修复在 d1 无法启动的问题 2022-05-12 15:10:53 +08:00
Luoyuan Xiao 0f658c8158
Merge pull request #292 from YdrMaster/main
完善 xtask
2022-05-12 14:19:22 +08:00
YdrMaster 72c2191039 feat(xtask): 为常用 busybox 子命令建立符号链接 2022-05-11 19:26:23 +08:00
YdrMaster 6ba9602e8f feat(xtask): 重构 rootfs -> image 过程 2022-05-11 19:08:25 +08:00
YdrMaster 7de17ef115 temp 2022-05-11 13:57:59 +08:00
YdrMaster 922312a430 cleanup 2022-05-11 11:37:49 +08:00
YdrMaster fa155fe95b fix: 修正 libos 找 rootfs 的方式 2022-05-11 11:18:45 +08:00
YdrMaster e70eecb5ba refactor(xtask): miniroofs.tar.* 撤离 prebuilt 目录 2022-05-11 10:24:09 +08:00
YdrMaster 64bf09c596 fix(xtask): 下载时创建父目录 2022-05-11 09:51:56 +08:00
Ydr 9f991c6b4c
Merge branch 'rcore-os:master' into main 2022-05-11 09:37:34 +08:00
YdrMaster b8f66237d7 feat(xtask): 统一 rootfs 存放,libc-test 成为子模块 2022-05-11 09:36:50 +08:00
Luoyuan Xiao 359066bd89
Merge pull request #291 from shzhxh/master-merge
update module: tests , change status of some testcases.
2022-05-10 19:48:31 +08:00
Luoyuan Xiao 0fd2263a46
Merge pull request #290 from YdrMaster/main
完善 xtask
2022-05-10 19:48:17 +08:00
YdrMaster 35306f14c1 fix: rust-cache 不好用,放弃,准备缓存 lfs 2022-05-10 19:13:47 +08:00
YdrMaster 55cc6438f7 fix: 改正 git lfs 用法,只下载必要的文件
todo:lfs 内置于 xtask,用什么拉什么
2022-05-10 18:53:31 +08:00
YdrMaster 0b6aa1f958 cleanup 2022-05-10 18:31:43 +08:00
YdrMaster c55c72de43 feat(xtask): 直接内联编译 fs-fuse 功能,不必再安装应用程序 2022-05-10 18:22:51 +08:00
YdrMaster 05bd6b0943 feat(xtask): 封装 make 和 tar 2022-05-10 16:57:08 +08:00
YdrMaster 3434c151a7 feat(xtask): 先 git clone 到随机临时位置 2022-05-10 16:29:42 +08:00
YdrMaster cfb86710f4 feat(xtask): 命令自动构造错误信息 2022-05-10 15:31:03 +08:00
shzhxh 26912df809 update module: tests .
change status of some testcases.
2022-05-10 14:56:08 +08:00
YdrMaster 74f7a23bd6 build: update deps 2022-05-10 14:07:40 +08:00
YdrMaster e4c1b8a397 feat: 封装 git cargo 命令 2022-05-10 13:20:56 +08:00
YdrMaster 207449af67 feat(xtask): 下载文件先放到临时位置
feat(xtask): libc-test 和 image 递归 rootfs

docs: 增加说明文档
2022-05-09 20:16:07 +08:00
DeathWish5 e3fa672f59 merge master 2022-05-09 16:14:36 +08:00
Zhenxing 8b36294ffe
Merge pull request #288 from YdrMaster/dev-ci
优化 CI

    增加 rust cache 缓存依赖项
    将多个架构的 QEMU 一起缓存以节约总缓存占用
    Build 测试集矩阵化
    clippy 可复用 build 的结果,build 后立即 clippy 以节约时间
2022-05-09 08:26:40 +08:00
Ydr 77ff4eafeb
Merge branch 'rcore-os:master' into main 2022-05-08 22:20:35 +08:00
Luoyuan Xiao 803bbd9962
Merge pull request #287 from YdrMaster/dev-xtask
增加 xtask 模块
2022-05-08 22:12:46 +08:00
YdrMaster cf4629ec9d ci(test): cache qemu dir 2022-05-08 22:02:22 +08:00
YdrMaster 0afb263958 ci(build): use matrix 2022-05-08 18:35:54 +08:00
YdrMaster ce199ad9a7 ci: test matrix 2022-05-08 17:33:51 +08:00
YdrMaster 04ab8a380b build: 统一 make 参数形式 2022-05-08 16:47:19 +08:00
YdrMaster f4d395500d ci(build): rust-cache 2022-05-08 10:48:53 +08:00
YdrMaster 8947cecf3c ci(test): cache all qemu in one tar 2022-05-08 10:42:16 +08:00
YdrMaster 231f1a1266 ci(test): toolchain & rust-cache 2022-05-08 10:06:22 +08:00
YdrMaster eeb318cc9a ci: update checkout@v3 2022-05-06 18:06:15 +08:00
YdrMaster 7c9abada74 fix(ci): make 2022-05-06 17:28:28 +08:00
YdrMaster 261344d381 ci: cache compiled qemu 2022-05-06 17:03:33 +08:00
YdrMaster fee725d5f9 try: 降低 alphine 版本看 remquol 现象 2022-05-06 13:10:55 +08:00
YdrMaster 6e29f418f9 docs(xtask): 更新 README.md 2022-05-06 12:49:53 +08:00
YdrMaster bb7604c434 feat: 整合 image 2022-05-06 11:50:24 +08:00
YdrMaster 2f7685f23a feat: 整合 libc-test 2022-05-06 11:03:34 +08:00
YdrMaster 9f878044c9 feat: 分解 riscv-image,与 x86_64 实现流程一致 2022-05-06 08:52:14 +08:00
YdrMaster d9531cec75 try: 整合 libc-test 2022-05-06 07:12:18 +08:00
YdrMaster c72952df94 try: 合并 收缩 2022-05-05 22:13:05 +08:00
YdrMaster 5d93a4dbee try: 合并 Makefile 任务 2022-05-05 18:04:52 +08:00
Ydr c309b03d08
Merge branch 'rcore-os:master' into dev-xtask 2022-05-05 17:49:32 +08:00
Luoyuan Xiao f5efe3b8c4
Merge pull request #284 from YdrMaster/dev-boot-riscv
完全移除 riscv 启动流程部分的汇编文件
2022-05-05 17:29:28 +08:00
YdrMaster 6fb1f20f97 feat: image x86_64 前半部分 2022-05-05 17:26:40 +08:00
YdrMaster c307eb8620 feat: clone libc-test 2022-05-05 15:17:10 +08:00
YdrMaster 42f2f0affb fix: set ARCH env 2022-05-05 12:48:03 +08:00
YdrMaster 0af4593e45 try: modify Makefile 2022-05-05 12:10:10 +08:00
YdrMaster e239d7ceb5 feat: 支持修改全局 git 代理 2022-05-05 11:06:51 +08:00
YdrMaster 1688fd9651 Revert "try: 修改 Makefile"
This reverts commit e67a7301e3.
2022-05-05 11:05:52 +08:00
YdrMaster e67a7301e3 try: 修改 Makefile 2022-05-04 13:21:06 +08:00
YdrMaster ef6c6adf37 docs(xtask):
- 为一些工具函数增加文档
- 增加项目愿景和日志
2022-05-04 11:37:41 +08:00
YdrMaster cfca262b55 feat: 增加下载交叉编译工具 2022-05-04 02:29:51 +08:00
DeathWish5 ca19f4fa8f minor update: update git commit of 2022-05-03 15:41:05 +08:00
DeathWish5 33f9ec9333 minor update. Makefile: typo in debugrun. .gdbinit: start from jump_higher 2022-05-03 15:35:42 +08:00
YdrMaster 51af7f4ed1 feat: 完成 riscv-rootfs 2022-05-02 22:48:51 +08:00
YdrMaster 4e3b53469c feat: 实现安装 rcore-fs-fuse、下载 prebuilt 2022-05-02 19:44:47 +08:00
YdrMaster 5af92cb723 fix: Build CI 2022-05-02 18:17:26 +08:00
YdrMaster ecea18fe1f build: try xtask 2022-05-02 16:02:09 +08:00
YdrMaster 909765bfb2 build: 移除 boot 文件夹 2022-05-02 04:06:55 +08:00
YdrMaster 2ea758a810 feat: 从设备树解析 smp 2022-05-01 14:34:31 +08:00
YdrMaster b3b113bc00 refactor(zCore): 为内核支持 2021 风格的 print 2022-04-30 20:08:25 +08:00
YdrMaster 4f6e3b2a50 refactor: 启动页表定义分离到独立的文件 2022-04-29 12:52:40 +08:00
YdrMaster 0181726f14 refactor: 依赖 r0 库清零 bss 2022-04-29 11:53:31 +08:00
YdrMaster aa86a412cd refactor: 利用链接脚本传递全局符号,移除 global_asm 2022-04-29 10:45:46 +08:00
YdrMaster 1efacbffc9 refactor: naked_function
利用 naked_fuction 内联汇编,减少全局符号表内容,并移除独立的汇编文件
2022-04-29 09:58:04 +08:00
Luoyuan Xiao 4711359b38
Merge pull request #283 from DeathWish5/master
Bug fix: multi-core boot will dead-lock in qemu
2022-04-28 12:19:45 +08:00
DeathWish5 fdd852abfc comment: minor update 2022-04-27 17:10:53 +08:00
DeathWish5 42d7540439 buf fix: net::init() should not be called in secondary_init 2022-04-25 22:44:34 +08:00
Zhenxing 9bad3e20d2
Merge pull request #282 from shzhxh/master-merge
update submodule : tests, some bugs of pthread are fixed
2022-04-25 16:18:09 +08:00
shzhxh 84b4143d45 update submodule : tests. some bugs of pthread are fixed. 2022-04-25 09:07:17 +08:00
Luoyuan Xiao 5741f12e5f
Merge pull request #280 from YdrMaster/dev-boot-riscv
整理 riscv 启动逻辑,并为汇编增加注释
2022-04-22 17:00:12 +08:00
YdrMaster 010f316a22 refactor(zCore): riscv 各开发板兼容 2022-04-22 04:54:59 +08:00
YdrMaster fde18af386 refactor(zCore): 启动过程完全 rust 化
todo: 适配 d1
2022-04-22 00:29:52 +08:00
Ydr e9b863b59d
Merge branch 'rcore-os:master' into dev-boot-riscv 2022-04-21 17:16:44 +08:00
Luoyuan Xiao 3544db899f
Merge pull request #279 from OSLab-zCore/master
Update pthread implementation
2022-04-21 16:03:28 +08:00
YdrMaster b1c89d7cfe perf(zCore): 优化启动栈设置操作 2022-04-21 05:03:19 +08:00
YdrMaster 0fc50e5996 build: 修改一些配置文件 2022-04-21 04:46:25 +08:00
Zichun Yu 0b390fd825 Remove 91chi.fun 2022-04-20 13:59:52 +08:00
YdrMaster e78ead64ee Merge branch 'rcore-os-master' into dev-boot-riscv 2022-04-20 11:17:11 +08:00
YdrMaster c59d6aa88c Merge branch 'master' of https://github.com/rcore-os/zCore into rcore-os-master 2022-04-20 11:16:01 +08:00
YdrMaster 1750123a36 docs(zCore): 补全 riscv 启动过程注释 2022-04-20 09:41:43 +08:00
YdrMaster 0a4f6dbf38 refactor(zCore): 整理 riscv 启动流程 2022-04-20 04:53:04 +08:00
Zichun Yu b51c8ebea3 Merge lsm-yzc-merge 2022-04-19 23:10:34 +08:00
Zichun Yu 3734039a5e Merge branch 'lsm-yzc-merge' of github.com:OSLab-zCore/zCore into lsm-yzc-merge 2022-04-19 21:16:12 +08:00
Zichun Yu 55299ff1da Update tests commit 2022-04-19 21:15:57 +08:00
Yiren Zhang b1eb0c8d64
Merge pull request #277 from DeathWish5/zcore-thread-and-lock
Update `executor` and `lock` to enable kernel interrupt and thread based scheduling.
2022-04-19 20:26:26 +08:00
DeathWish5 b700eec2d6 delete unused code, format some code 2022-04-19 19:37:54 +08:00
liusm18 d2bc0b5fb7 Fix align issues in riscv 2022-04-19 17:34:35 +08:00
Zichun Yu b052a9666b Update tests commit 2022-04-19 15:42:34 +08:00
Zichun Yu 8497aeea23 Merge master 2022-04-19 15:40:12 +08:00
Zichun Yu 4746413d3a Update tests commit 2022-04-19 11:12:14 +08:00
DeathWish5 565abc371d merge master 2022-04-18 15:29:29 +08:00
liusm18 b3efbcfe09 Fix a coding style issue: add safety docs for unsafe function 2022-04-17 12:04:17 +08:00
liusm18 59c6de2d74 Fix a code format 2022-04-17 11:53:10 +08:00
liusm18 a191f57ff3 Fix some codeing style issues 2022-04-17 11:43:01 +08:00
Zichun Yu 296766831a Add UserInPtr in thread.rs 2022-04-16 12:29:14 +08:00
Zichun Yu e3066c23ae Change format in thread.rs 2022-04-16 11:56:59 +08:00
Zichun Yu 08ab919633 Reset tests commit 2022-04-16 11:44:18 +08:00
Zichun Yu 7a35b4fd5c Fix test.yml --no-failed 2022-04-16 11:38:56 +08:00
Zichun Yu 4d346d263e Fix conflicts 2022-04-16 11:19:54 +08:00
Zichun Yu dd3f2790e9 Update pthread tests 2022-04-15 20:16:06 +08:00
Zichun Yu 5d3c254f47 Fix conflicts 2022-04-15 19:22:13 +08:00
Zichun Yu 4028d6cb1a Fix conflicts 2022-04-15 19:18:39 +08:00
liusm18 c1e5bc0897 Fix align issues 2022-04-15 19:12:04 +08:00
liusm18 cd0f4ee40d Add tips for hard code path 2022-04-15 18:18:55 +08:00
liusm18 028ecea082 Add x86_64 support 2022-04-15 18:15:14 +08:00
Zichun Yu 7b7911a2dc Fix Makefile LINUX 2022-04-15 17:08:19 +08:00
DeathWish5 980ccb164c delete some unused debug code 2022-04-13 23:23:00 +08:00
Zichun Yu 7eff7cf67b Fix for CI 2022-04-13 21:30:14 +08:00
DeathWish5 0d1da32724 cargo fmt and clippy 2022-04-13 19:59:55 +08:00
Zichun Yu 2cb5d05b9a Fix for CI 2022-04-13 19:52:40 +08:00
Zichun Yu 2cb5cae22b Fix for CI 2022-04-13 19:42:42 +08:00
Zichun Yu 861bac0498 Fix for CI 2022-04-13 19:31:29 +08:00
Zichun Yu 7deff3e73b Fix for CI 2022-04-13 17:32:41 +08:00
DeathWish5 0a28678a9f switch future when TimerInt happend in user mode, switch executor when TiemrInt happened in kernel mode 2022-04-13 16:58:52 +08:00
Zhenxing 5389c418fa
Merge pull request #275 from shzhxh/master-merge
Some update of CI test
1. Create a new set of testcases called linux-other-test, to contain non-libc testcases.
2. Change linux-libc-test-riscv64 from prebuild binary to cross compiling.
2022-04-13 10:53:35 +08:00
DeathWish5 16206b1a52 merge master 2022-04-12 22:56:29 +08:00
DeathWish5 d585b87b09 add .gdbinit for debugrun 2022-04-12 20:42:54 +08:00
shzhxh dbbde19546 change testcase libc-test-riscv64 from prebuilt to cross-compile 2022-04-12 17:48:16 +08:00
shzhxh 689a43fc30 update submodule : tests, to separate non-libc testcases from libc testcases 2022-04-12 17:23:28 +08:00
Zichun Yu de77f11988 Remove hard codes & Fix warn 2022-04-11 19:15:21 +08:00
liusm18 3b8eaa17be Remove hard codes in handle_signal() 2022-04-11 14:32:13 +08:00
Luoyuan Xiao d8e4efad25
Merge pull request #274 from YdrMaster/dev-io
refactor(drivers): 修改 device io
2022-04-11 12:35:38 +08:00
liusm18 008175ce2b Remove some useless comments 2022-04-11 11:10:39 +08:00
liusm18 fa16615683 Remove some hard codes for sys_kill tkill tgkill 2022-04-11 10:35:54 +08:00
YdrMaster 99f21ba488 refactor(drivers): 修改 io
- 根据 wikipedia,pio 改名 pmio
- 为 mmio 读写增加内存屏障
- 为 io 补充文档
2022-04-09 23:14:45 +08:00
DeathWish5 41a0ce76d2 update tests 2022-04-09 21:47:34 +08:00
DeathWish5 3e165fe444 update lock of buddy_system_allocator 2022-04-09 15:59:54 +08:00
Zichun Yu 2888c273b2 Polish code 2022-04-09 10:04:29 +08:00
DeathWish5 a266160212 implement intrrupt for kernel_hal::libos 2022-04-06 23:13:29 +08:00
DeathWish5 8b6f0c0e00 remove kernel-sync feature 2022-04-06 17:23:51 +08:00
DeathWish5 9156ebc6dd bug fix: x86_64 rsp aliged error 2022-04-06 16:41:58 +08:00
Zichun Yu 3c1e8f66a3 Fix conflicts 2022-04-05 20:32:45 +08:00
liusm18 9caed02182 Implement signals 2022-04-05 07:44:57 +08:00
Zichun Yu 767e9dd745 Fix pthread_cond & pthread_tsd & ... 2022-04-04 19:30:52 +08:00
DeathWish5 d6687d9a65 bug fix: pthread_mutex page fault 2022-04-03 18:38:55 +08:00
Zichun Yu 65eec60ac6 Try to fix mutex and robust (still have bugs) 2022-04-03 00:04:48 +08:00
liusm18 a55017a967 Implement sys_tgkill 2022-04-01 10:54:06 +08:00
Luoyuan Xiao fcc46ebfc5
Merge pull request #253 from rcore-os/mergeBranch
Update some socket function about file
2022-03-31 22:19:45 +08:00
Luoyuan Xiao 06579b597f
Makefile configure the socket test
Makefile configure the socket test
2022-03-31 20:07:50 +08:00
Luoyuan Xiao cc9e06fc16 Check build errors
Fix warning: Linking two modules of different data layouts
2022-03-31 19:38:16 +08:00
liusm18 9961dba5e2 Implement tkill syscall 2022-03-31 17:27:59 +08:00
Luoyuan Xiao 6e1cc2297d
Merge branch 'master' into mergeBranch 2022-03-31 15:56:30 +08:00
Zhenxing 8cc8898de2
Merge pull request #272 from shzhxh/master-merge
change the url of libc-test, in order to fix some compile error.
2022-03-31 15:27:13 +08:00
shzhxh 25a389f06a change the url of libc-test, in order to fix some compile error.
update submodule : tests, change status of some testcases.
2022-03-31 11:24:52 +08:00
Luoyuan Xiao 9358545f1e
Merge pull request #255 from elliott10/master
Supporting PCI devices scanning based on rCore and Fix build error
2022-03-31 10:49:56 +08:00
elliott10 98a85ae9cb Fix riscv64 build error
error: instruction requires the following: M (Integer Multiplication and Division)
    mul t0, t0, a0
    ^
Check Code
2022-03-30 18:13:55 +08:00
elliott10 3f3d5e2acc Merge branch 'm' 2022-03-30 17:37:21 +08:00
Luoyuan Xiao b32d71b71c
Merge pull request #271 from YdrMaster/dev-uart
TimeSpec & uart device file
2022-03-29 21:24:16 +08:00
YdrMaster a6e227aa1f feat(drivers): 为 x86_64 支持开启第二个串口并映射到宿主系统临时文件,方便测试 2022-03-29 16:51:19 +08:00
Ydr 4cddebe2be
Merge pull request #1 from YdrMaster/dev-uart
Merge
2022-03-29 13:43:59 +08:00
Ydr 91a23d3f09
Merge branch 'rcore-os:master' into dev-uart 2022-03-29 13:33:35 +08:00
Luoyuan Xiao 69587e8049
Merge pull request #269 from YdrMaster/dev-user-ptr
refactor(kernel-hal): 尽量减少对通过 `UserPtr` 传入的参数的拷贝
2022-03-29 13:33:31 +08:00
YdrMaster 297a95d0d4 feat: 实现串口设备伪文件及其挂载 2022-03-29 12:24:33 +08:00
YdrMaster 0d5602bca0 build: 依赖新版本的 `rcore-fs` 2022-03-29 12:24:33 +08:00
YdrMaster c9d90f97de fix: 修正 TimeSpec 定义 2022-03-29 12:24:33 +08:00
YdrMaster 92fca7a6a6 fix: 在执行新应用程序之前将重要信息从旧应用程序的用户空间中拷贝出来 2022-03-29 12:05:22 +08:00
DeathWish5 090b6af383 cargo fmt and clippy 2022-03-28 17:50:16 +08:00
DeathWish5 202f6296b2 add lock support for libos mode 2022-03-28 17:39:54 +08:00
elliott10 4793db27ea Merge branch 'main' 2022-03-28 17:29:22 +08:00
DeathWish5 b517f543f8 cargo fmt and clippy 2022-03-28 17:26:07 +08:00
DeathWish5 a32e503498 fix x86-smpboot rev 2022-03-28 17:18:02 +08:00
DeathWish5 d7a9741f06 Merge branch 'master' into zcore-lock 2022-03-28 17:14:20 +08:00
DeathWish5 0bf5b1dc6c x86 out of date, upadte some crate version 2022-03-28 16:13:09 +08:00
Luoyuan Xiao c565c3299f
Merge pull request #270 from shzhxh/master
update submodule : tests, workaround of zircon-core-test-libos
2022-03-28 15:39:05 +08:00
shzhxh 491694989c update submodule : tests, workaround of zircon-core-test-libos 2022-03-28 14:15:17 +08:00
YdrMaster 4276b63ef4 docs(kernel-hal): 为 `UserPtr` 提供一份不会导出的中文文档 2022-03-25 13:16:03 +08:00
YdrMaster 3cc3374c87 refactor: 尽量减少对用户指针传入的数组的拷贝 2022-03-25 11:36:56 +08:00
YdrMaster 5783d60f9b refactor(kernel-hal): 移除全部对用户指针传入的字符串的拷贝 2022-03-25 09:15:20 +08:00
DeathWish5 f8bd0ae4a2 minor udpate 2022-03-24 18:04:08 +08:00
DeathWish5 974c5a887a delete unused comments/output 2022-03-24 17:50:34 +08:00
YdrMaster 8038eee630 fix: doc 2022-03-24 16:57:10 +08:00
YdrMaster bfe7339816 fix: 修正 zircon 专用 2022-03-24 16:51:28 +08:00
DeathWish5 3559a39b1f remove Mutex in drivers::irq::intc::{xxx_handler} 2022-03-24 15:07:05 +08:00
YdrMaster 497a70d15a style: 修改风格,减少提前 return 2022-03-24 13:06:23 +08:00
YdrMaster f5f2a6aedc docs: add docs to UserPtr 2022-03-24 12:43:04 +08:00
YdrMaster 25402e96ee test(linux-syscall): 增加编译期测试以保证 linux 定义的类型定义正确
docs(linux-syscall): 改正文档引用了非公开类型的问题
2022-03-23 16:56:21 +08:00
YdrMaster c46f80c8eb build: 依赖最新的 rcore-fs 2022-03-23 16:42:04 +08:00
YdrMaster 6fc573cec8 feat: 实现 `statfs` 和 `fstatfs` 两个系统调用 2022-03-23 16:35:37 +08:00
DeathWish5 682d274998 restore to spin_lock 2022-03-23 16:06:43 +08:00
DeathWish5 5b8c17cc2e merge master 2022-03-22 22:26:25 +08:00
DeathWish5 3bfdae5cac udpate toolchain 2022-03-22 22:22:20 +08:00
DeathWish5 e01dd6973f add rcore-user 2022-03-22 18:22:22 +08:00
DeathWish5 04f018d198 ... 2022-03-22 18:10:07 +08:00
DeathWish5 9916e19a83 refactor lock to kernel-sync 2022-03-22 17:25:43 +08:00
liusm18 d13f66b7f3 Change urls for git & modify default make params 2022-03-20 16:57:11 +08:00
GCYYfun 51b305d27f
Update lib.rs 2022-03-16 19:19:46 +08:00
GCYYfun 32fab8b318
Update lib.rs 2022-03-16 19:14:54 +08:00
GCYYfun 770242c7e9
Update net.rs 2022-03-16 19:09:43 +08:00
GCYYfun 1f387b745b
Merge branch 'master' into mergeBranch 2022-03-16 19:07:36 +08:00
YdrMaster b573dec4d7 docs(zcore-drivers): add some docs 2022-03-16 14:33:07 +08:00
YdrMaster 75faf43872 docs(zcore-drivers): add docs to top mods and types 2022-03-15 23:01:36 +08:00
YdrMaster 00e5a49b60 docs(linux-syscall):
- add doc to `ipc.rs`
- add api menu to `ipc.rs` `vm.rs` and `task.rs`
2022-03-15 11:53:44 +08:00
DeathWish5 8770e25478 ... 2022-03-14 14:17:01 +08:00
YdrMaster 56c29d7c28 docs: update `task.rs` and `vm.rs` syscall 2022-03-14 11:09:10 +08:00
YdrMaster cd4476faec docs: 根据具体实现改正 `vm.rs` 的文档 2022-03-14 09:39:52 +08:00
YdrMaster e4de7167c5 docs: add some docs to task.rs. 2022-03-13 23:16:56 +08:00
YdrMaster d90b5780c2 docs: add docs to `vm.rs`,
there are 3 syscalls in it.
2022-03-11 18:12:00 +08:00
Luoyuan Xiao 25a3556e8a
Merge pull request #262 from shzhxh/master
Update autotest & CI
2022-03-10 13:57:49 +08:00
shzhxh ea463472a5 update zcore-tests 2022-03-09 12:24:35 +08:00
DeathWish5 afc4951b23 ... 2022-03-07 21:21:41 +08:00
DeathWish5 e9a417963c add no-irq rwlock 2022-03-07 17:41:23 +08:00
DeathWish5 d6d80b0bf6 ... 2022-03-07 15:07:42 +08:00
DeathWish5 596a8a7c5f update rboot 2022-03-07 14:54:20 +08:00
Yuekai Jia 48b279c2ac Update zcore-tests 2022-03-05 14:58:47 +08:00
Yuekai Jia dc26bad362 Merge branch 'master' into zcore-tests 2022-03-05 14:54:27 +08:00
Yuekai Jia 65a6fb4664
Merge pull request #257 from rcore-os/update-toolchain
update toolchain to nightly-2022-01-20
2022-03-05 14:25:55 +08:00
Runji Wang 9b8b4e4ce1 fix cargo fmt and clippy
Signed-off-by: Runji Wang <wangrunji0408@163.com>
2022-03-02 23:57:52 +08:00
Runji Wang 928e063dd9 fix compile error after rebase
Signed-off-by: Runji Wang <wangrunji0408@163.com>
2022-03-02 23:47:20 +08:00
Runji Wang 63aff2c3dd update trapframe and fix compile for riscv
Signed-off-by: Runji Wang <wangrunji0408@163.com>
2022-03-02 23:39:13 +08:00
Runji Wang 5ed7c74091 update toolchain to 2022-01-20
Signed-off-by: Runji Wang <wangrunji0408@163.com>
2022-03-02 23:39:13 +08:00
Luoyuan Xiao 90130af787
Merge pull request #256 from pleasewhy/riscv_muticore
riscv muticore support
2022-03-02 21:01:45 +08:00
elliott10 1e5992690d Fixed an ioapic bug, which occurred when
the ioapic redirection table memory was not clean during init,
Causing x86 hardware to
fail to boot
2022-02-25 16:54:11 +08:00
DeathWish5 e153f5713e use intrrupt Mutex instead of spin::Mutex 2022-02-25 15:11:37 +08:00
Yuekai Jia d941d2c084 Update github workflows 2022-02-23 21:32:15 +08:00
Yuekai Jia 813678d3c3 Add submodule tests from https://github.com/rcore-os/zcore-tests 2022-02-23 21:22:55 +08:00
Yuekai Jia 180081bae7 tests: update Makefile 2022-02-23 21:02:12 +08:00
Yuekai Jia 7763b9d4e0 Remove old test scripts 2022-02-23 20:46:32 +08:00
Yuekai Jia dd5ebd44a0 Add support to exit qemu for bare-metal test 2022-02-23 20:41:56 +08:00
pleasewhy 63e147a304 style 2022-02-22 21:45:56 +08:00
pleasewhy a0ac9a1fb5 fix: ingore timeout argument when op is wake 2022-02-22 09:04:18 +08:00
pleasewhy 210e183257 remove check_pagefault logic in libos mod 2022-02-20 21:13:56 +08:00
pleasewhy d25a982a7a refactor: replace ZxResult with PagingResult 2022-02-20 17:57:30 +08:00
pleasewhy 2c4b2f2c70 remove linux-user 2022-02-19 21:05:03 +08:00
pleasewhy 9b10549a53 fix:
get vaddr flags from pagetable
check whether the address passed by the user is valid
2022-02-19 20:54:26 +08:00
pleasewhy c7851a58d6 fix:
check ptr from user
map vaddr if necessary
2022-02-19 09:13:01 +08:00
GCYYfun 8278dc13b3 feat: new add clock_nanosleep syscall interface in linux-syscall 2022-01-28 15:19:21 +08:00
elliott10 47612fd312 Supporting PCI devices scanning based on rCore 2022-01-27 15:31:07 +08:00
pleasewhy 4ba1cc8817 remove linux-user 2022-01-17 19:11:23 +08:00
pleasewhy 83363de827 clean unmeaning code in test ci conf 2022-01-17 19:02:14 +08:00
pleasewhy 8084a4b8bc fix: build ci can't work 2022-01-15 13:33:51 +08:00
pleasewhy c2dae469bd build ci use actions-rs/toolchain@v1 2022-01-15 13:15:44 +08:00
pleasewhy 137c6daaf5 change makefile to default args 2022-01-15 12:56:56 +08:00
pleasewhy 5a079ad054 modify ci conf: generate image when build 2022-01-15 11:10:21 +08:00
pleasewhy 4e58d0ee3e generate image when make build 2022-01-14 22:12:14 +08:00
pleasewhy 3c4b800df9 change arch to x86_64 2022-01-14 21:27:35 +08:00
pleasewhy 1c8f43ac74 style: modfiy for clippy 2022-01-14 19:26:52 +08:00
pleasewhy 1638a78fee fix: modify secondary_main return type to ! 2022-01-14 18:00:19 +08:00
pleasewhy a0ae38ed44 remove the print statement from secondary_rust_main 2022-01-13 23:04:23 +08:00
pleasewhy 4d73e52134 style: clean some codes 2022-01-13 16:18:32 +08:00
pleasewhy 627616cc72 style: cargo fmt 2022-01-13 13:42:21 +08:00
pleasewhy 4793db52ac fix: allows S-mode to access U-mode va(sstatus.SUM=1) 2022-01-13 13:36:48 +08:00
pleasewhy 05390dd7ba refactor: rewrite riscv muticode boot logic 2022-01-11 13:24:35 +08:00
GCYYfun d410fde88a feat: add route in rtlx nic 2022-01-04 14:02:39 +08:00
pleasewhy 627eb34f10 panic print backtrace info 2022-01-04 12:42:43 +08:00
pleasewhy 4369dabcd7 fix: remove the code associated with print in secondary_init 2022-01-02 16:18:51 +08:00
pleasewhy 96877b66f4 fix: riscv occasionally produces oom errors 2022-01-02 16:08:28 +08:00
pleasewhy 8b2267e380 Merge branch 'master' into muticore_scheduler 2021-12-31 19:11:24 +08:00
pleasewhy d348277340 feat: riscv-intc support muticore 2021-12-31 19:01:13 +08:00
GCYYfun 0ecd206d6f style: format code 2021-12-31 14:29:00 +08:00
GCYYfun f450d37021 fix: previous insufficient declaration period error 2021-12-31 14:25:13 +08:00
GCYYfun eec570837d fix: replace SOCKETS to get_sockets() 2021-12-31 11:59:12 +08:00
GCYYfun a983b14f06 fix: a blank line caused format error 2021-12-31 01:14:24 +08:00
GCYYfun d3ec807ae0 fix: move libos loopback init form boot to drivers 2021-12-31 01:06:21 +08:00
GCYYfun d7a86f331c fix: remove some unused code and format code 2021-12-31 00:53:27 +08:00
GCYYfun f4983a8a2e feat: support socket operation with file 2021-12-31 00:40:32 +08:00
chyyuu 4aedfd4005
Merge pull request #251 from rcore-os/mergeBranch
Initially add socket syscall function
2021-12-28 23:24:25 +08:00
GCYYfun 07379e6641 feat: enable socket test in CI 2021-12-28 19:09:49 +08:00
GCYYfun b05c08938f feat: update rboot again 2021-12-28 19:06:53 +08:00
GCYYfun 406feb58cf feat: update rboot 2021-12-28 18:44:05 +08:00
GCYYfun 9d4ba7d716 fix: add loopback in libos 2021-12-28 17:26:26 +08:00
GCYYfun f06dfca43c fix: format code 2021-12-28 17:13:51 +08:00
GCYYfun 99d960a0f2 feat: add loopback function 2021-12-28 17:05:10 +08:00
GCYYfun c2860fc5fb merge updated zcore 2021-12-28 16:27:23 +08:00
chyyuu 8723a901a9
Merge pull request #249 from elliott10/master
Add realtek rtl8211f ethernet driver and smoltcp for D1 board
2021-12-27 09:19:28 +08:00
elliott10 122c058c06 Adjust CI scripts/baremetal-test-fail-rv64.txt fail list
Reduce the number of CI rerun
2021-12-25 21:05:35 +08:00
elliott10 c6c3da31e6 Update rust toolchain to fix test CI errors 2021-12-24 16:45:24 +08:00
elliott10 ea396fc30d Parse ethernet gmac dtb node;
Add realtek rtl8211f ethernet driver;
Add smoltcp, the Interface between ethernet driver and smoltcp;
Enable ethernet gmac interrupt to response network packets, eg:ping;
Add interface between kernel-hal and drivers;
Clean up the warning code.
2021-12-17 14:53:37 +08:00
Yuekai Jia ae5a8279ec Improve code style 2021-12-07 23:48:07 +08:00
chyyuu 5672c17290
Merge pull request #245 from elliott10/master
Support D1 C906 board up and running into busybox shell
2021-12-07 23:39:48 +08:00
pleasewhy 5d62288ae0 feature: support muticore scheduler 2021-12-07 18:26:19 +08:00
elliott10 360dba2e47 Support D1 C906 board up and running into busybox shell
And fixed bugs: device tree, uart driver, update memory, etc.
2021-12-07 15:44:18 +08:00
pleasewhy 29a3fd171d fix: riscv muticore oom panic 2021-12-01 12:53:21 +08:00
pleasewhy aab20a2fbd to: muticore boot failed 2021-11-28 18:54:48 +08:00
pleasewhy 28859d252f style: clean debug log 2021-11-26 13:10:05 +08:00
pleasewhy ca6472c987 fix: change offset between cpus in riscv plic 2021-11-26 13:00:42 +08:00
pleasewhy b0795b007d to: debug riscv muticore 2021-11-25 21:16:25 +08:00
pleasewhy f3f2a5d732 chore: modify conf to compile 2021-11-24 17:46:02 +08:00
chyyuu d606d4fc5f
Merge pull request #238 from equation314/master
Refactor boot and loader
2021-11-04 10:56:47 +08:00
Yuekai Jia 0eef12ed85 kernl-hal: fix alignment in free_pmem_regions() 2021-11-04 00:54:19 +08:00
Yuekai Jia a9372b8133 Fixed some typos 2021-11-02 16:49:04 +08:00
Yuekai Jia 560a9c47ac Merge remote-tracking branch 'upstream/master' 2021-11-02 15:39:21 +08:00
Yuekai Jia 6e1bfa4af1 Get physical memory size from devicetree 2021-11-02 02:07:12 +08:00
Yuekai Jia 96d6cfd0a1 Get cmdline and initrd region from devicetree 2021-11-02 00:33:30 +08:00
XueBling b351f3fb7f
doc: add doc in driver module (#236)
* docs: add doc in x86_apic/ioapic.rs

* doc: add doc in driver module

* docs: improve docs for drivers

Co-authored-by: Yuekai Jia <equation618@gmail.com>
2021-11-01 23:35:28 +08:00
Yuekai Jia a6afbda462 Refactor kcounter mod, fix wrong order when run /boot/bin/kcounter 2021-11-01 21:49:00 +08:00
Yuekai Jia 30831ed711 Add helper functions for UserContext to kernel-hal 2021-10-31 13:04:04 +08:00
Yuekai Jia d791c52700 Move ThreadSwitchFuture from kernel-hal to zircon-object 2021-10-30 14:27:03 +08:00
Yuekai Jia e71df32bca fix unexported env USER_IMG in Makefile 2021-10-28 17:06:12 +08:00
Yuekai Jia 78322d6f31 Merge crates zircon-loader and linux-loader into one crate 2021-10-27 00:00:16 +08:00
Yuekai Jia 91d57d4409 Add more docs to kernel-hal 2021-10-26 17:53:07 +08:00
Yuekai Jia 67a0468df0 Update README 2021-10-26 02:31:55 +08:00
Yuekai Jia 9b6c1f5928 Set `libos` as the default feature, update CI 2021-10-26 01:55:48 +08:00
Yuekai Jia 169088dc31 Move the ziron libos entry point to zCore directory 2021-10-26 01:31:22 +08:00
Yuekai Jia 806c9d104b Add linux_object::fs::rcore_fs_wrapper
And add directory zCore to cargo workspace
2021-10-26 01:31:22 +08:00
Yuekai Jia 26dd63fd5a Rewrite zCore/Makefile, use uppercase input args for `make` 2021-10-26 01:31:22 +08:00
Yuekai Jia 13698ddfb0 fixed some typos in drivers 2021-10-26 01:31:14 +08:00
Yuekai Jia c2f961dce2 update log style 2021-10-23 02:33:18 +08:00
Yuekai Jia 73ca749513 move the libos entry point to zCore directory to share common codes
(linux mode only)
2021-10-23 02:06:58 +08:00
Yuekai Jia 00c025e5b0 add feature 'smp' to kernel-hal 2021-10-22 14:48:32 +08:00
Yuekai Jia 0b5bb96479 merge boot codes on all platforms for bare-metal mode 2021-10-22 02:05:16 +08:00
Yuekai Jia 6139df7667 fix interleave serial output on SMP 2021-10-21 23:58:34 +08:00
Yuekai Jia e1672fc9e3 kernel-hal: add mod boot to support SMP 2021-10-21 16:58:36 +08:00
Yuekai Jia 671c8e528f drivers: fix device tree parsing on the latest qemu 2021-10-20 17:08:52 +08:00
Yuekai Jia 7f0b007a60 fix hanging when run with KVM 2021-10-20 17:08:52 +08:00
Yuekai Jia 25f65c164d drivers: update APIs to get devices from kernel-hal 2021-10-20 17:08:51 +08:00
chyyuu 54880c9920
Merge pull request #231 from equation314/drivers
Add the standalone zcore-drivers module
2021-10-18 08:40:08 +08:00
Yuekai Jia 8c2fbdfffb drivers: minor update 2021-10-17 23:36:18 +08:00
Yuekai Jia d39127b61e Add mock mouse and keyboard support for libos mode 2021-10-17 14:20:28 +08:00
Yuekai Jia 87ec296b37 drivers: introduce EventScheme and macro impl_event_scheme 2021-10-16 19:37:46 +08:00
Yuekai Jia a9a13d4a8e update rcore-fs to 7c232ec 2021-10-15 23:05:49 +08:00
Yuekai Jia 0ba6b97f2a drivers: support device inode /dev/eventX 2021-10-15 20:48:28 +08:00
Yuekai Jia 871b78ead5 input: check mouse compatibility with input devices 2021-10-15 16:39:40 +08:00
Yuekai Jia f6895a737a display: support virtio-gpu cursor 2021-10-15 00:52:38 +08:00
Yuekai Jia 6a4a93ed28 linux: implement fcntl DUPFD_CLOEXEC 2021-10-14 04:39:42 +08:00
Yuekai Jia 55e62b95c1 linux: rewrite File struct, support sys_fcntl 2021-10-14 00:09:57 +08:00
Yuekai Jia b57745bd52 drivers: add virtio-input device, support /dev/mice 2021-10-13 20:34:05 +08:00
Yuekai Jia 5920b12cd4 drivers: add MockMemory for libos mode
And also
* speed up mmap execution time on macos
* support to mmap /dev/fb0 for libos mode
2021-10-11 10:54:21 +08:00
Yuekai Jia f02e9b7423 drivers: support to mmap /dev/fb0 2021-10-10 15:03:45 +08:00
Yuekai Jia 0412009d64 drivers: re-enable /dev/fb0 device for linux mode 2021-10-07 23:19:15 +08:00
Yuekai Jia 7bab9f397c drivers: add display drivers for x86 & riscv bare-metal 2021-10-07 20:54:08 +08:00
Yuekai Jia d8e6aca2f9 drivers: support graphic console output 2021-10-05 04:50:30 +08:00
Yuekai Jia dd835e5af7 drivers: add a mock display driver using SDL2 for libos 2021-10-02 01:00:02 +08:00
Yuekai Jia af1e156dc4 drivers: load user image by block driver again 2021-09-29 23:28:31 +08:00
Yuekai Jia f1c5d58979 drivers: put all device to DeviceList 2021-09-29 22:24:12 +08:00
Yuekai Jia 617e5d3c3c drivers: move BufferedUart to drivers 2021-09-29 18:18:08 +08:00
Yuekai Jia ea7fef6379 drivers: create virtio drivers from device tree 2021-09-29 17:05:41 +08:00
Yuekai Jia bab8b1fc1b drivers: create drivers by parsing the device tree 2021-09-28 21:51:53 +08:00
Yuekai Jia a8eb212764 driver: rewrite EventListener, add BufferedSerial to reduce uart interrupts 2021-09-27 01:40:30 +08:00
Yuekai Jia 3b1f38a3bb drivers: fix bugs of the deprecated timer IRQ number 2021-09-24 16:49:37 +08:00
Yuekai Jia aff922d3bc drivers: update some interfaces related to the interrupt 2021-09-24 00:27:54 +08:00
Yuekai Jia d13afc3d24 drivers: refactor x86 local & IO APIC drivers 2021-09-23 00:38:59 +08:00
Yuekai Jia 4021ef4c30 driver: add IRQ drivers for riscv 2021-09-19 20:37:40 +08:00
Yuekai Jia 1522d823e0 include git versions to uname output 2021-09-18 18:06:54 +08:00
Yuekai Jia 022d7f0e96 drivers: implement EventListener 2021-09-18 16:33:32 +08:00
Yuekai Jia 994f8b80f2 drivers: add mock drivers for libos
And use a unified serial driver in kernel-hal
2021-09-16 23:45:14 +08:00
Yuekai Jia 8b6be6a876 drivers: initial framework 2021-09-15 23:17:57 +08:00
Yuekai Jia 78f8de57d9
Merge pull request #229 from equation314/refactor-kernel-hal
Refactor kernel-hal
2021-09-15 21:56:07 +08:00
Yuekai Jia 4e0e5aafb9
Revert "zCore initial network implementation" (#228) 2021-09-13 22:04:28 +08:00
Yuekai Jia 01b90461e1 kernel-hal: merge commom serial code 2021-09-13 21:22:03 +08:00
Yuekai Jia a609194a33 kernel-hal: add HAL kernel page fault handler in zCore 2021-09-12 14:27:48 +08:00
Yuekai Jia 8dc240c434 kernel-hal: introduce KernelHandler
No longer use FFI to call zcore functions (e.g. `frame_alloc()`) in kernel-hal
2021-09-11 23:13:26 +08:00
Yuekai Jia ba630064b4 kernel-hal: pass KERNEL_OFFSET and so on as KernelConfig to kernel_hal
And also move hal_pt_map_kernel() to kernel_hal
2021-09-11 02:25:06 +08:00
Yuekai Jia 2b57e9e353 kernel-hal: re-implement GenericPageTable::map_cont() & unmap_cont()
Supports to map to huge pages
2021-09-10 20:17:33 +08:00
Yuekai Jia 6f1675bec3 kernel-hal: implement page table walking manually for riscv64 2021-09-10 15:57:02 +08:00
Yuekai Jia b10fc26c5b Fixed some bugs of vmar::handle_page_fault() & kernel_hal::zero_frame_addr()
And also add more passed core-tests
2021-09-10 00:01:01 +08:00
Yuekai Jia 2157d92893 kernel-hal: implement page table walking manually 2021-09-08 01:57:24 +08:00
Yuekai Jia dba14999a4 kernal_hal: move IrqManager to utils/irq_manager.rs 2021-09-07 15:51:21 +08:00
Yuekai Jia b1cc7e07fd kernel_hal: introduce interrupt::IrqManager 2021-09-07 01:58:47 +08:00
Yuekai Jia af9610c37e kernel-hal: refactor vm mod 2021-09-06 21:36:45 +08:00
Yuekai Jia 91f082ce18 kernel-hal: refactor for riscv bare-metal 2021-09-05 23:54:19 +08:00
Yuekai Jia 1097f4ac1e Update libc-tests.py 2021-09-05 17:10:27 +08:00
Yuekai Jia ceacbc9d9c kernel-hal: refactor for x86_64 bare-metal 2021-09-05 15:30:12 +08:00
Yuekai Jia 3b0e3296e8 kernel_hal: add macros to generate default functions automatically 2021-09-04 17:35:44 +08:00
Yuekai Jia 937c86f01d kernel-hal: refactor for libos 2021-09-04 17:08:05 +08:00
Yuekai Jia de809efca2
Merge pull request #226 from chyyuu/refactor-ddk-hal
Refactor ddk hal
2021-09-04 16:20:31 +08:00
chyyuu e2088bdf99
Merge pull request #225 from chyyuu/master
musl-libc-test was updated, so add /libc-test/src/regression/fgets-eo…
2021-09-04 10:51:18 +08:00
Yu Chen a7b9766857 add 4 files "execle-env-static.exe, execle-env.exe pthread_tsd-static.exe, tls_init-static.exe" in scripts/linux/baremetal-test-fail.txt,
for CI libc-test baremetal-mode pass

ERR: didn't find the file
/libc-test/src/regression/execle-env-static.exe
/libc-test/src/regression/execle-env.exe

ERR: page fault error
/libc-test/src/functional/pthread_tsd-static.exe
/libc-test/src/functional/tls_init-static.exe
2021-09-04 10:14:01 +08:00
Yu Chen d6ef339ce2 musl-libc-test was updated, so add /libc-test/src/regression/fgets-eof.exe in scripts/linux/test-allow-failed.txt for CI libc-test in libos mode can pass 2021-09-04 08:49:23 +08:00
Yu Chen 8c41e49b41 musl-libc-test was updated, so add /libc-test/src/regression/fgets-eof.exe in scripts/linux/test-allow-failed.txt for CI libc-test in libos mode can pass 2021-09-04 08:41:47 +08:00
Yuekai Jia a97a544b9d Refactor kernel_hal::InterruptManager 2021-09-03 20:34:31 +08:00
Yuekai Jia 5c33799a04 Better module usage in zircon-object/dev/pci 2021-09-03 17:57:53 +08:00
chyyuu dee715c4a2
Merge pull request #222 from chyyuu/master
add graphic/game info
2021-09-02 07:50:55 +08:00
Yu Chen 2a87dc979d add graphic/game info 2021-09-01 22:35:07 +08:00
Luoyuan Xiao b88e832166
Merge pull request #221 from elliott10/master
riscv64 D1 board running into  busybox shell
2021-09-01 12:10:33 +08:00
elliott10 d31f44feef riscv64 D1 board running into shell
Clear .bss
Ramfs support
2021-08-31 23:33:30 +08:00
elliott10 11b66859be ramfs and virtio filesystem for Linux refactoring 2021-08-31 21:13:36 +08:00
Luoyuan Xiao 2e29f7cc35
Merge pull request #219 from elliott10/master
zCore riscv64 add board type: AW D1 C906 board
2021-08-31 16:29:43 +08:00
Luoyuan Xiao cbc3915e55
Merge pull request #220 from shzhxh/master
bug fix : workaround to resolve the problem that the result of core-t…
2021-08-31 15:42:51 +08:00
shzhxh c1f1f0bfe4 bug fix : workaround to resolve the problem that the result of core-test-libos CI is unstable 2021-08-31 15:15:47 +08:00
Luoyuan Xiao 24f700e840
Merge pull request #218 from YXL76/graphic
Add input devices
2021-08-31 15:03:13 +08:00
elliott10 2694880bb0 zCore riscv64 add board type: AW D1 C906 board 2021-08-31 13:50:20 +08:00
YXL 40917dd399 fix build error 2021-08-31 11:39:44 +08:00
YXL 20157c68ce Support input devices in linux libos 2021-08-31 10:59:07 +08:00
YXL 2bd4b987a6 move `FbVarScreeninfo` and `FbFixScreeninfo` to hal-unix 2021-08-31 10:11:55 +08:00
YXL ffcb8d249a fix the input event queue is empty 2021-08-31 09:51:29 +08:00
YXL a1a3a46b5d Add input devices 2021-08-31 00:19:32 +08:00
Luoyuan Xiao c8c215d51a
Add riscv64 D1 board README DOC (#217) 2021-08-30 21:27:38 +08:00
Runji Wang 329781312f
Merge pull request #216 from YXL76/graphic
Add graphics support
2021-08-30 12:53:20 +08:00
YXL 1566634a73 fix aarch64 pointer 2021-08-30 10:52:22 +08:00
YXL c25db7df9b fix cargo clippy 2021-08-30 10:22:48 +08:00
elliott10 b793462d18 Add D1 board opensbi firmware 2021-08-30 10:22:11 +08:00
elliott10 5d3f0c3917 Add riscv64 D1 board README DOC 2021-08-29 21:02:20 +08:00
YXL 170d68140b Support framebuffer in linux libos 2021-08-29 17:54:29 +08:00
YXL 3b4017e819 Add fbdev 2021-08-29 17:45:02 +08:00
YXL 255872d1d3 support ps/2 mouse 2021-08-29 16:24:23 +08:00
YXL 41b31a8ddf Add virtio input/gpu devices 2021-08-29 16:08:18 +08:00
chyyuu 45f82c1923
Merge pull request #215 from chyyuu/master
Add libos-mode core-test  CI
2021-08-28 23:20:42 +08:00
Yu Chen 139c5aa2f4 update CI 2021-08-28 22:01:43 +08:00
Yu Chen 88bff91d26 recover baremetal core-test CI 2021-08-28 21:54:14 +08:00
Zhenxing 3c70b6545d Update unix-core-tests.py 2021-08-28 10:24:05 +08:00
DeathWish5 df8cd067cc bug fix in zircon-laoder tests 2021-08-28 10:23:59 +08:00
DeathWish5 909b1ddbb1 action .yml bug fix: use .so build for libos 2021-08-28 10:23:02 +08:00
shzhxh 7d9c99e6df add core test CI 2021-08-28 10:22:55 +08:00
DeathWish5 52414c2529 delete unused code 2021-08-28 10:22:50 +08:00
DeathWish5 23a6193783 When run on unix, 'map_range' := true. Now it can run into component_manager 2021-08-28 10:22:43 +08:00
DeathWish5 51027916c0 add test scripts for unix-coretests (scripts/unix-coretest*) rename 'scripts/core-tests' to 'scripts/baremetal-core-tests' 2021-08-28 10:22:35 +08:00
366 changed files with 22626 additions and 11879 deletions

16
.cargo/config.toml Normal file
View File

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

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

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

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

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

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

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

149
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,149 @@
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,20 +1,28 @@
name: deploy CI
name: Deploy docs
on:
on:
push:
pull_request:
env:
rust_toolchain: nightly-2022-01-20
jobs:
doc:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
- name: Build docs
run: arch=x86_64 cargo doc --no-deps --all-features
# uses: actions-rs/cargo@v1
# with:
# command: doc
# args: --no-deps --all-features
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

View File

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

View File

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

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

@ -0,0 +1,281 @@
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 }}

30
.gitignore vendored
View File

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

6
.gitmodules vendored
View File

@ -1,3 +1,9 @@
[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 Normal file
View File

@ -0,0 +1,10 @@
{
// 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,18 +1,17 @@
[workspace]
members = [
"zircon-object",
"zircon-loader",
"linux-object",
"zircon-syscall",
"linux-loader",
"kernel-hal-unix",
"drivers",
"kernel-hal",
]
exclude = [
"zircon-user",
"zCore",
"rboot",
"zircon-object",
"zircon-syscall",
"linux-object",
"linux-syscall",
"kernel-hal-bare",
"loader",
"zCore",
"xtask",
]
default-members = ["xtask"]
exclude = ["zircon-user", "rboot"]
[profile.release]
lto = true

123
Makefile
View File

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

225
README.md
View File

@ -1,11 +1,15 @@
# 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/zircon_object/)
[![Docs](https://img.shields.io/badge/docs-alpha-blue)](https://rcore-os.github.io/zCore/)
[![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
@ -13,25 +17,26 @@ Reimplement [Zircon][zircon] microkernel in safe Rust as a userspace program!
- 2020.04.16: Zircon console is working on zCore! 🎉
## Quick start for RISCV64
```
```sh
make riscv-image
cd zCore
make run arch=riscv64 linux=1
make run ARCH=riscv64 LINUX=1
```
## Getting started
Environments
* [Rust toolchain](http://rustup.rs)
* [QEMU](https://www.qemu.org)
* [Git LFS](https://git-lfs.github.com)
- [Rust toolchain](http://rustup.rs)
- [QEMU](https://www.qemu.org)
- [Git LFS](https://git-lfs.github.com)
### Developing environment info
- current rustc -- rustc 1.56.0-nightly (08095fc1f 2021-07-26)
- current rust-toolchain -- nightly-2021-07-27
- current qemu -- 5.2.0
- 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
Clone repo and pull prebuilt fuchsia images:
@ -48,57 +53,117 @@ For users in China, there's a mirror you can try:
git clone https://github.com.cnpmjs.org/rcore-os/zCore --recursive
```
Prepare Alpine Linux rootfs:
### Run zcore in libos mode
```sh
make rootfs
```
#### Run zcore in linux-libos mode
Run native Linux program (Busybox):
- step 1: Prepare Alpine Linux rootfs:
```sh
cargo run --release -p linux-loader -- /bin/busybox [args]
```
```sh
make rootfs
```
Run native Zircon program (shell):
- step 2: Compile & Run native Linux program (Busybox) in libos mode:
```sh
cargo run --release -p zircon-loader -- prebuilt/zircon/x64
```
```sh
cargo run --release --features "linux libos" -- /bin/busybox [args]
```
Run Linux shell on bare-metal (zCore):
You can also add the feature `graphic` to show the graphical output (with [sdl2](https://www.libsdl.org) installed).
```sh
make image
cd zCore && make run mode=release linux=1 [graphic=on] [accel=1]
```
To debug, set the `LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
Run Zircon on bare-metal (zCore):
#### Run native Zircon program (shell) in zircon-libos mode:
```sh
cd zCore && make run mode=release [graphic=on] [accel=1]
```
- step 1: Compile and Run Zircon shell
Build and run your own Zircon user programs:
```sh
cargo run --release --features "zircon libos" -- prebuilt/zircon/x64/bringup.zbi
```
```sh
# See template in zircon-user
cd zircon-user && make zbi mode=release
The `graphic` and `LOG` options are the same as Linux.
# Run your programs in zCore
cd zCore && make run mode=release user=1
```
### Run zcore in bare-metal mode
To debug, set `RUST_LOG` environment variable to one of `error`, `warn`, `info`, `debug`, `trace`.
#### 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]
```
## Testing
### LibOS Mode Testing
#### Zircon related
Run Zircon official core-tests:
```sh
cd zCore && make test mode=release [accel=1] test_filter='Channel.*'
pip3 install pexpect
cd scripts && python3 unix-core-testone.py 'Channel.*'
```
Run all (non-panicked) core-tests for CI:
```sh
pip3 install pexpect
cd scripts && python3 unix-core-tests.py
# Check `zircon/test-result.txt` for results.
```
#### Linux related
Run Linux musl libc-tests for CI:
```sh
make rootfs && make libc-test
cd scripts && python3 libos-libc-tests.py
# Check `linux/test-result.txt` for results.
```
### Bare-metal Mode Testing
#### Zircon related
Run Zircon official core-tests on bare-metal:
```sh
cd zCore && make test MODE=release [ACCEL=1] TEST_FILTER='Channel.*'
```
Run all (non-panicked) core-tests for CI:
@ -108,57 +173,92 @@ pip3 install pexpect
cd scripts && python3 core-tests.py
# Check `zircon/test-result.txt` for results.
```
#### Linux related
Run Linux musl libc-tests for CI:
```sh
make rootfs && make libc-test
cd scripts && python3 libc-tests.py
# Check `linux/test-result.txt` for results.
```
### Baremetal Mode Testing
#### 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
cd zCore && make build MODE=release LINUX=1 ARCH=x86_64
## Testing
cd ../scripts && python3 ./baremetal-libc-test.py
##
cd scripts && python3 baremetal-libc-test.py
##
```
You can use [`scripts/baremetal-libc-test-ones.py`](./scripts/baremetal-libc-test-ones.py) & [`scripts/linux/baremetal-test-ones.txt`](./scripts/linux/baremetal-test-ones.txt) to test specified apps.
You can use [`scripts/baremetal-libc-test-ones.py`](./scripts/baremetal-libc-test-ones.py) & [`scripts/linux/baremetal-test-ones.txt`](./scripts/linux/baremetal-test-ones.txt) to test specified apps.
[`scripts/linux/baremetal-test-fail.txt`](./scripts/linux/baremetal-test-fail.txt) includes all failed x86-64 apps (We need YOUR HELP to fix bugs!)
#### riscv-64 Linux related
Run Linux musl libc-tests for CI:
```
```sh
## Prepare rootfs with libc-test & oscomp apps
make riscv-image
## Build zCore kernel & Testing
cd ../scripts && python3 baremetal-test-riscv64.py
##
cd scripts && python3 baremetal-test-riscv64.py
##
```
You can use[ `scripts/baremetal-libc-test-ones-riscv64.py`](./scripts/baremetal-libc-test-ones-riscv64.py) & [`scripts/linux/baremetal-test-ones-rv64.txt`](scripts/linux/baremetal-test-ones-rv64.txt)to test
You can use[scripts/baremetal-libc-test-ones-riscv64.py](./scripts/baremetal-libc-test-ones-riscv64.py) & [`scripts/linux/baremetal-test-ones-rv64.txt`](scripts/linux/baremetal-test-ones-rv64.txt)to test
specified apps.
[`scripts/linux/baremetal-test-fail-riscv64.txt`](./scripts/linux/baremetal-test-fail-riscv64.txt)includes all failed riscv-64 apps (We need YOUR HELP to fix bugs!)
## Graph/Game
snake game: <https://github.com/rcore-os/rcore-user/blob/master/app/src/snake.c>
### 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
```
### riscv64 porting info
### RISC-V 64 porting info
- [porting riscv64 doc](./docs/porting-rv64.md)
## Components
### Overview
@ -178,4 +278,5 @@ make doc
| Exception Handling | Interrupt | Signal |
### Small Goal & Little Plans
- https://github.com/rcore-os/zCore/wiki/Plans
- <https://github.com/rcore-os/zCore/wiki/Plans>

86
docs/Manual.md Normal file
View File

@ -0,0 +1,86 @@
# 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=?>
```

119
docs/README-D1.md Normal file
View File

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

46
drivers/Cargo.toml Normal file
View File

@ -0,0 +1,46 @@
[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

@ -0,0 +1,270 @@
// 解析设备树,创建已知的设备并为它们注册中断。
//
// 涉及到中断的设备包括:
//
// - 接收中断的中断控制器
// - 发出中断的设备
//
// 有效的中断控制器应该具有下列三个属性:
//
// - `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

@ -0,0 +1,18 @@
//! 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>;
}

38
drivers/src/bus/mod.rs Normal file
View File

@ -0,0 +1,38 @@
#![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) }
}

327
drivers/src/bus/pci.rs Normal file
View File

@ -0,0 +1,327 @@
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

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

Binary file not shown.

View File

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

@ -0,0 +1,808 @@
//! 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);
}

7
drivers/src/input/mod.rs Normal file
View File

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

133
drivers/src/input/mouse.rs Normal file
View File

@ -0,0 +1,133 @@
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
}
}

53
drivers/src/io/mmio.rs Normal file
View File

@ -0,0 +1,53 @@
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)
};
}
}

79
drivers/src/io/mod.rs Normal file
View File

@ -0,0 +1,79 @@
// 封装对外设地址空间的访问,包括内存映射 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);
}
}

102
drivers/src/io/pmio.rs Normal file
View File

@ -0,0 +1,102 @@
// 端口映射 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));
}
}
}

22
drivers/src/irq/mod.rs Normal file
View File

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

@ -0,0 +1,131 @@
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

@ -0,0 +1,171 @@
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

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

@ -0,0 +1,204 @@
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

@ -0,0 +1,72 @@
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

@ -0,0 +1,176 @@
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();
}
}

103
drivers/src/lib.rs Normal file
View File

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

@ -0,0 +1,64 @@
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

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

View File

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

57
drivers/src/mock/input.rs Normal file
View File

@ -0,0 +1,57 @@
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
}
}

9
drivers/src/mock/mod.rs Normal file
View File

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

110
drivers/src/mock/uart.rs Normal file
View File

@ -0,0 +1,110 @@
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();
}
}
}

218
drivers/src/net/e1000.rs Normal file
View File

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

@ -0,0 +1,60 @@
// 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!()
}
}

83
drivers/src/net/mod.rs Normal file
View File

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

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

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

@ -0,0 +1,91 @@
// 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);
}

224
drivers/src/net/rtlx.rs Normal file
View File

@ -0,0 +1,224 @@
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![]));
// }

11
drivers/src/prelude.rs Normal file
View File

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

@ -0,0 +1,8 @@
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

@ -0,0 +1,218 @@
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

@ -0,0 +1,62 @@
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);
}
};
}

131
drivers/src/scheme/input.rs Normal file
View File

@ -0,0 +1,131 @@
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;
}

84
drivers/src/scheme/irq.rs Normal file
View File

@ -0,0 +1,84 @@
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!()
}
}

55
drivers/src/scheme/mod.rs Normal file
View File

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

14
drivers/src/scheme/net.rs Normal file
View File

@ -0,0 +1,14 @@
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

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

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

10
drivers/src/uart/mod.rs Normal file
View File

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

@ -0,0 +1,262 @@
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

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

@ -0,0 +1,45 @@
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

@ -0,0 +1,58 @@
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

@ -0,0 +1,105 @@
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

@ -0,0 +1,84 @@
#![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)
}
}
}

20
drivers/src/utils/mod.rs Normal file
View File

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

43
drivers/src/virtio/blk.rs Normal file
View File

@ -0,0 +1,43 @@
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

@ -0,0 +1,55 @@
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(())
}
}

77
drivers/src/virtio/gpu.rs Normal file
View File

@ -0,0 +1,77 @@
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

@ -0,0 +1,78 @@
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])
}
}
}
}

28
drivers/src/virtio/mod.rs Normal file
View File

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

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

View File

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

View File

@ -1,31 +0,0 @@
// RISCV
// Linear mapping
#[cfg(target_arch = "riscv32")]
pub const PHYSICAL_MEMORY_OFFSET: usize = 0x4000_0000;
#[cfg(target_arch = "riscv64")]
pub const PHYSICAL_MEMORY_OFFSET: usize = 0xFFFF_FFFF_0000_0000;
#[cfg(target_arch = "riscv32")]
pub const KERNEL_OFFSET: usize = 0xC000_0000;
#[cfg(target_arch = "riscv64")]
pub const KERNEL_OFFSET: usize = 0xFFFF_FFFF_8000_0000;
pub const MEMORY_OFFSET: usize = 0x8000_0000;
// TODO: get memory end from device tree
pub const MEMORY_END: usize = 0x8800_0000;
// TODO: rv64 `sh` and `ls` will crash if stack top > 0x80000000 ???
pub const USER_STACK_OFFSET: usize = 0x40000000 - USER_STACK_SIZE;
pub const USER_STACK_SIZE: usize = 0x10000;
#[cfg(target_arch = "riscv32")]
pub const KSEG2_START: usize = 0xfe80_0000;
#[cfg(target_arch = "riscv64")]
pub const KSEG2_START: usize = 0xffff_fe80_0000_0000;
pub const MAX_DTB_SIZE: usize = 0x2000;
#[cfg(target_arch = "riscv64")]
pub const ARCH: &'static str = "riscv64";
#[cfg(target_arch = "riscv32")]
pub const ARCH: &'static str = "riscv32";

View File

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

View File

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

View File

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

View File

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

View File

@ -1,130 +0,0 @@
.equ XLENB, 8
# sp + 8*a2 -> a1
.macro LOAD a1, a2
ld \a1, \a2*XLENB(sp)
.endm
.macro STORE a1, a2
sd \a1, \a2*XLENB(sp)
.endm
# int in U: sscratch = kernel_addr; int in S: sscratch = 0;
.macro SAVE_ALL
csrrw sp, sscratch, sp
bnez sp, trap_from_user
trap_from_kernel:
csrr sp, sscratch
trap_from_user:
addi sp, sp, -36*XLENB
# x0 = 0, x2 = sp
STORE x1, 1
STORE x3, 3
STORE x4, 4
STORE x5, 5
STORE x6, 6
STORE x7, 7
STORE x8, 8
STORE x9, 9
STORE x10, 10
STORE x11, 11
STORE x12, 12
STORE x13, 13
STORE x14, 14
STORE x15, 15
STORE x16, 16
STORE x17, 17
STORE x18, 18
STORE x19, 19
STORE x20, 20
STORE x21, 21
STORE x22, 22
STORE x23, 23
STORE x24, 24
STORE x25, 25
STORE x26, 26
STORE x27, 27
STORE x28, 28
STORE x29, 29
STORE x30, 30
STORE x31, 31
csrrw s0, sscratch, x0
csrr s1, sstatus
csrr s2, sepc
csrr s3, stval
csrr s4, scause
STORE s0, 2
STORE s1, 32
STORE s2, 33
STORE s3, 34
STORE s4, 35
.endm
.macro RESTORE_ALL
# s1 = sstatus, s2 = sepc
LOAD s1, 32
LOAD s2, 33
# int in Kernel, sstatus SPP = 1; int in User, sstatus SPP 0
andi s0, s1, 1 << 8
bnez s0, _to_kernel
_to_user:
addi s0, sp, 36 * XLENB
csrw sscratch, s0
_to_kernel:
csrw sstatus, s1
csrw sepc, s2
LOAD x1, 1
LOAD x3, 3
LOAD x4, 4
LOAD x5, 5
LOAD x6, 6
LOAD x7, 7
LOAD x8, 8
LOAD x9, 9
LOAD x10, 10
LOAD x11, 11
LOAD x12, 12
LOAD x13, 13
LOAD x14, 14
LOAD x15, 15
LOAD x16, 16
LOAD x17, 17
LOAD x18, 18
LOAD x19, 19
LOAD x20, 20
LOAD x21, 21
LOAD x22, 22
LOAD x23, 23
LOAD x24, 24
LOAD x25, 25
LOAD x26, 26
LOAD x27, 27
LOAD x28, 28
LOAD x29, 29
LOAD x30, 30
LOAD x31, 31
# sp
LOAD x2, 2
.endm
.section .text
.globl __alltraps
.align 4
__alltraps:
SAVE_ALL
mv a0, sp
jal rust_trap
.globl __trapret
__trapret:
RESTORE_ALL
sret

View File

@ -1,151 +0,0 @@
use super::consts::PHYSICAL_MEMORY_OFFSET;
use crate::putfmt;
use core::convert::TryInto;
use core::fmt::{Error, Write};
//use crate::console::push_stdin;
pub struct Uart {
base_address: usize,
}
// 结构体Uart的实现块
impl Uart {
pub fn new(base_address: usize) -> Self {
Uart { base_address }
}
/*
uart初始化
8-bits (LCR[1:0])
使FIFOs (FCR[0])
使(IER[0]), 使
*/
pub fn init(&mut self) {
let ptr = self.base_address as *mut u8;
unsafe {
// LCR at base_address + 3
// 置位 bit 0 bit 1
let lcr = (1 << 0) | (1 << 1);
ptr.add(3).write_volatile(lcr);
// FCR at offset 2
ptr.add(2).write_volatile(1 << 0);
//IER at offset 1
ptr.add(1).write_volatile(1 << 0);
// 设置波特率,除子,取整等
// 2.729 MHz (22,729,000 cycles per second) --> 波特率 2400 (BAUD)
// 根据NS16550a规格说明书计算出divisor
// divisor = ceil( (clock_hz) / (baud_sps x 16) )
// divisor = ceil( 22_729_000 / (2400 x 16) ) = ceil( 591.901 ) = 592
// divisor寄存器是16 bits
let divisor: u16 = 592;
//let divisor_least: u8 = divisor & 0xff;
//let divisor_most: u8 = divisor >> 8;
let divisor_least: u8 = (divisor & 0xff).try_into().unwrap();
let divisor_most: u8 = (divisor >> 8).try_into().unwrap();
// DLL和DLM会与其它寄存器共用基地址需要设置DLAB来切换选择寄存器
// LCR base_address + 3, DLAB = 1
ptr.add(3).write_volatile(lcr | 1 << 7);
//写DLL和DLM来设置波特率, 把频率22.729 MHz的时钟划分为每秒2400个信号
ptr.add(0).write_volatile(divisor_least);
ptr.add(1).write_volatile(divisor_most);
// 设置后不需要再动了, 清空DLAB
ptr.add(3).write_volatile(lcr);
}
}
pub fn simple_init(&mut self) {
let ptr = self.base_address as *mut u8;
unsafe {
// Enable FIFO; (base + 2)
ptr.add(2).write_volatile(0xC7);
// MODEM Ctrl; (base + 4)
ptr.add(4).write_volatile(0x0B);
// Enable interrupts; (base + 1)
ptr.add(1).write_volatile(0x01);
}
}
pub fn get(&mut self) -> Option<u8> {
let ptr = self.base_address as *mut u8;
unsafe {
//查看LCR, DR位为1则有数据
if ptr.add(5).read_volatile() & 0b1 == 0 {
None
} else {
Some(ptr.add(0).read_volatile())
}
}
}
pub fn put(&mut self, c: u8) {
let ptr = self.base_address as *mut u8;
unsafe {
//此时transmitter empty
ptr.add(0).write_volatile(c);
}
}
}
// 需要实现的write_str()重要函数
impl Write for Uart {
fn write_str(&mut self, out: &str) -> Result<(), Error> {
for c in out.bytes() {
self.put(c);
}
Ok(())
}
}
/*
fn unsafe mmio_write(address: usize, offset: usize, value: u8) {
//write_volatile() 是 *mut raw 的成员;
//new_pointer = old_pointer + sizeof(pointer_type) * offset
//也可使用reg.offset
let reg = address as *mut u8;
reg.add(offset).write_volatile(value);
}
fn unsafe mmio_read(address: usize, offset: usize, value: u8) -> u8 {
let reg = address as *mut u8;
//读取8 bits
reg.add(offset).read_volatile(value) //无分号可直接返回值
}
*/
pub fn handle_interrupt() {
let mut my_uart = Uart::new(0x1000_0000 + PHYSICAL_MEMORY_OFFSET);
if let Some(c) = my_uart.get() {
//CONSOLE
//push_stdin(c);
super::serial_put(c);
/*
* serial_write()
match c {
0x7f => { //0x8 [backspace] ; 而实际qemu运行[backspace]键输出0x7f, 表示del
bare_print!("{} {}", 8 as char, 8 as char);
},
10 | 13 => { // 新行或回车
bare_println!();
},
_ => {
bare_print!("{}", c as char);
},
}
*/
}
}

View File

@ -1,48 +0,0 @@
use alloc::string::String;
use core::slice;
use device_tree::{DeviceTree, Node};
//use super::virtio_mmio::virtio_probe;
use super::virtio::virtio_probe;
use super::CMDLINE;
const DEVICE_TREE_MAGIC: u32 = 0xd00dfeed;
fn walk_dt_node(dt: &Node) {
if let Ok(compatible) = dt.prop_str("compatible") {
// TODO: query this from table
if compatible == "virtio,mmio" {
virtio_probe(dt);
}
// TODO: initial other devices (16650, etc.)
}
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);
}
}
struct DtbHeader {
magic: u32,
size: u32,
}
pub fn init(dtb: usize) {
info!("DTB: {:#x}", dtb);
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) {
//trace!("DTB: {:#x?}", dt);
walk_dt_node(&dt.root);
}
}
}

View File

@ -1,104 +0,0 @@
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use rcore_fs::dev::{self, BlockDevice, DevError};
use spin::RwLock;
//pub use block::BlockDriver;
/// Block device
pub mod virtio;
/// Device tree
pub mod device_tree;
#[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
}
*/
}
/////////
pub trait BlockDriver: Driver {
fn read_block(&self, _block_id: usize, _buf: &mut [u8]) -> bool {
unimplemented!("not a block driver")
}
fn write_block(&self, _block_id: usize, _buf: &[u8]) -> bool {
unimplemented!("not a block driver")
}
}
/////////
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 BLK_DRIVERS: RwLock<Vec<Arc<dyn BlockDriver>>> = RwLock::new(Vec::new());
//pub static ref IRQ_MANAGER: RwLock<irq::IrqManager> = RwLock::new(irq::IrqManager::new(true));
}
pub struct BlockDriverWrapper(pub Arc<dyn BlockDriver>);
impl BlockDevice for BlockDriverWrapper {
const BLOCK_SIZE_LOG2: u8 = 9; // 512
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> dev::Result<()> {
match self.0.read_block(block_id, buf) {
true => Ok(()),
false => Err(DevError),
}
}
fn write_at(&self, block_id: usize, buf: &[u8]) -> dev::Result<()> {
match self.0.write_block(block_id, buf) {
true => Ok(()),
false => Err(DevError),
}
}
fn sync(&self) -> dev::Result<()> {
Ok(())
}
}
lazy_static! {
// Write only once at boot
pub static ref CMDLINE: RwLock<String> = RwLock::new(String::new());
}

View File

@ -1,130 +0,0 @@
use crate::{frame_dealloc, hal_frame_alloc_contiguous, phys_to_virt, virt_to_phys, PAGE_SIZE};
use device_tree::util::SliceRead;
use device_tree::Node;
use log::*;
use virtio_drivers::{VirtIOBlk, VirtIOHeader};
use super::super::PHYSICAL_MEMORY_OFFSET;
pub fn virtio_probe(node: &Node) {
let reg = match node.prop_raw("reg") {
Some(reg) => reg,
_ => return,
};
let paddr = reg.as_slice().read_be_u64(0).unwrap();
//let vaddr = phys_to_virt(paddr as usize);
let size = reg.as_slice().read_be_u64(8).unwrap();
// assuming one page
assert_eq!(size as usize, PAGE_SIZE);
/* 一一映射
let vaddr = paddr;
unsafe{
PageTableImpl::active().map_if_not_exists(vaddr as usize, paddr as usize);
}
*/
let vaddr = paddr + PHYSICAL_MEMORY_OFFSET as u64;
debug!("virtio_probe, paddr:{:#x}, vaddr:{:#x}", paddr, vaddr);
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
if !header.verify() {
// only support legacy device
return;
}
info!(
"Detected virtio device with vendor id: {:#X}",
header.vendor_id()
);
info!("Device tree node {:?}", node);
match header.device_type() {
//DeviceType::Network => virtio_net::init(header),
virtio_drivers::DeviceType::Block => virtio_blk_init(header),
t => warn!("Unrecognized virtio device: {:?}", t),
}
}
/// virtio_mmio
/////////
/// virtio_blk
use alloc::string::String;
use alloc::sync::Arc;
use alloc::format;
use super::{BlockDriver, DeviceType, Driver, BLK_DRIVERS, DRIVERS};
//use crate::{sync::SpinNoIrqLock as Mutex};
use spin::Mutex;
struct VirtIOBlkDriver(Mutex<VirtIOBlk<'static>>);
impl Driver for VirtIOBlkDriver {
fn try_handle_interrupt(&self, _irq: Option<usize>) -> bool {
self.0.lock().ack_interrupt()
}
fn device_type(&self) -> DeviceType {
DeviceType::Block
}
fn get_id(&self) -> String {
format!("virtio_block")
}
fn as_block(&self) -> Option<&dyn BlockDriver> {
None
}
}
impl BlockDriver for VirtIOBlkDriver {
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
self.0.lock().read_block(block_id, buf).is_ok()
}
fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
self.0.lock().write_block(block_id, buf).is_ok()
}
}
pub fn virtio_blk_init(header: &'static mut VirtIOHeader) {
let blk = VirtIOBlk::new(header).expect("failed to init blk driver");
let driver = Arc::new(VirtIOBlkDriver(Mutex::new(blk)));
DRIVERS.write().push(driver.clone());
//IRQ_MANAGER.write().register_all(driver.clone());
BLK_DRIVERS.write().push(driver);
}
/////////
/// virtio dma alloc/dealloc
#[no_mangle]
extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
let paddr = unsafe { hal_frame_alloc_contiguous(pages, 0).unwrap() };
trace!("alloc DMA: paddr={:#x}, pages={}", paddr, pages);
paddr
}
#[no_mangle]
extern "C" fn virtio_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
for i in 0..pages {
unsafe {
frame_dealloc(&(paddr + i * PAGE_SIZE));
}
}
trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages);
0
}
#[no_mangle]
extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
phys_to_virt(paddr)
}
#[no_mangle]
extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
virt_to_phys(vaddr)
}
type VirtAddr = usize;
type PhysAddr = usize;

View File

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

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

View File

@ -1,27 +0,0 @@
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

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

View File

@ -1,251 +0,0 @@
//! 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(llvm_asm)]
#![feature(global_asm)]
#![feature(linkage)]
//#![deny(warnings)]
#[macro_use]
extern crate log;
extern crate alloc;
#[macro_use]
extern crate lazy_static;
use alloc::boxed::Box;
use core::time::Duration;
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use kernel_hal::defs::*;
use kernel_hal::vdso::*;
use kernel_hal::UserContext;
use naive_timer::Timer;
use spin::Mutex;
pub mod arch;
pub use self::arch::*;
#[allow(improper_ctypes)]
extern "C" {
fn hal_pt_map_kernel(pt: *mut u8, current: *const u8);
fn frame_alloc() -> Option<usize>;
fn hal_frame_alloc_contiguous(page_num: usize, align_log2: usize) -> Option<usize>;
fn frame_dealloc(paddr: &usize);
#[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 {
#[export_name = "hal_frame_alloc"]
pub fn alloc() -> Option<Self> {
unsafe { frame_alloc().map(|paddr| Frame { paddr }) }
}
#[export_name = "hal_frame_dealloc"]
pub fn dealloc(&mut self) {
unsafe {
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 }
}
}
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
unsafe { PMEM_BASE + paddr }
}
pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
unsafe { vaddr - PMEM_BASE }
}
/// 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}, vaddr = {:#x}",
paddr,
buf.len(),
phys_to_virt(paddr)
);
unsafe {
buf.as_ptr()
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
}
}
/// Zero physical memory at `[paddr, paddr + len)`
#[export_name = "hal_pmem_zero"]
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
unsafe {
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
}
}
/// Copy content of `src` frame to `target` frame
#[export_name = "hal_frame_copy"]
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
trace!("frame_copy: {:#x} <- {:#x}", target, src);
unsafe {
let buf = phys_to_virt(src) as *const u8;
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
}
}
/// 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);
}
/// Initialize the HAL.
pub fn init(config: Config) {
unsafe {
trapframe::init();
}
#[cfg(target_arch = "riscv64")]
trace!("hal dtb: {:#x}", config.dtb);
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

@ -1,19 +0,0 @@
[package]
name = "kernel-hal-unix"
version = "0.1.0"
authors = ["Runji Wang <wangrunji0408@163.com>"]
edition = "2018"
description = "Kernel HAL implementation on Linux and macOS."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
libc = "0.2"
tempfile = "3"
bitflags = "1.2"
lazy_static = "1.4"
kernel-hal = { path = "../kernel-hal" }
async-std = "1.9"
git-version = "0.3"
trapframe = "0.8.0"

View File

@ -1,450 +0,0 @@
#![feature(asm)]
#![feature(linkage)]
#![deny(warnings)]
#[macro_use]
extern crate log;
extern crate alloc;
use {
alloc::collections::VecDeque,
async_std::task_local,
core::{cell::Cell, future::Future, pin::Pin},
git_version::git_version,
kernel_hal::PageTableTrait,
lazy_static::lazy_static,
std::fmt::{Debug, Formatter},
std::fs::{File, OpenOptions},
std::io::Error,
std::os::unix::io::AsRawFd,
std::sync::Mutex,
std::time::{Duration, SystemTime},
tempfile::tempdir,
};
pub use kernel_hal::defs::*;
use kernel_hal::vdso::*;
pub use kernel_hal::*;
use std::io::Read;
pub use trapframe::syscall_fn_entry as syscall_entry;
#[cfg(target_os = "macos")]
include!("macos.rs");
#[repr(C)]
pub struct Thread {
thread: usize,
}
impl Thread {
#[export_name = "hal_thread_spawn"]
pub fn spawn(
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
_vmtoken: usize,
) -> Self {
async_std::task::spawn(future);
Thread { thread: 0 }
}
#[export_name = "hal_thread_set_tid"]
pub fn set_tid(tid: u64, pid: u64) {
TID.with(|x| x.set(tid));
PID.with(|x| x.set(pid));
}
#[export_name = "hal_thread_get_tid"]
pub fn get_tid() -> (u64, u64) {
(TID.with(|x| x.get()), PID.with(|x| x.get()))
}
}
task_local! {
static TID: Cell<u64> = Cell::new(0);
static PID: Cell<u64> = Cell::new(0);
}
#[export_name = "hal_context_run"]
unsafe fn context_run(context: &mut UserContext) {
context.run_fncall();
}
/// Page Table
#[repr(C)]
pub struct PageTable {
table_phys: PhysAddr,
}
impl PageTable {
/// Create a new `PageTable`.
#[allow(clippy::new_without_default)]
#[export_name = "hal_pt_new"]
pub fn new() -> Self {
PageTable { table_phys: 0 }
}
}
impl PageTableTrait for PageTable {
/// Map the page of `vaddr` to the frame of `paddr` with `flags`.
#[export_name = "hal_pt_map"]
fn map(&mut self, vaddr: VirtAddr, paddr: PhysAddr, flags: MMUFlags) -> Result<()> {
debug_assert!(page_aligned(vaddr));
debug_assert!(page_aligned(paddr));
let prot = flags.to_mmap_prot();
mmap(FRAME_FILE.as_raw_fd(), paddr, PAGE_SIZE, vaddr, prot);
Ok(())
}
/// Unmap the page of `vaddr`.
#[export_name = "hal_pt_unmap"]
fn unmap(&mut self, vaddr: VirtAddr) -> Result<()> {
self.unmap_cont(vaddr, 1)
}
/// Change the `flags` of the page of `vaddr`.
#[export_name = "hal_pt_protect"]
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<()> {
debug_assert!(page_aligned(vaddr));
let prot = flags.to_mmap_prot();
let ret = unsafe { libc::mprotect(vaddr as _, PAGE_SIZE, prot) };
assert_eq!(ret, 0, "failed to mprotect: {:?}", Error::last_os_error());
Ok(())
}
/// Query the physical address which the page of `vaddr` maps to.
#[export_name = "hal_pt_query"]
fn query(&mut self, vaddr: VirtAddr) -> Result<PhysAddr> {
debug_assert!(page_aligned(vaddr));
unimplemented!()
}
/// Get the physical address of root page table.
#[export_name = "hal_pt_table_phys"]
fn table_phys(&self) -> PhysAddr {
self.table_phys
}
#[export_name = "hal_pt_unmap_cont"]
fn unmap_cont(&mut self, vaddr: VirtAddr, pages: usize) -> Result<()> {
if pages == 0 {
return Ok(());
}
debug_assert!(page_aligned(vaddr));
let ret = unsafe { libc::munmap(vaddr as _, PAGE_SIZE * pages) };
assert_eq!(ret, 0, "failed to munmap: {:?}", Error::last_os_error());
Ok(())
}
}
#[repr(C)]
pub struct PhysFrame {
paddr: PhysAddr,
}
impl Debug for PhysFrame {
fn fmt(&self, f: &mut Formatter<'_>) -> core::result::Result<(), std::fmt::Error> {
write!(f, "PhysFrame({:#x})", self.paddr)
}
}
lazy_static! {
static ref AVAILABLE_FRAMES: Mutex<VecDeque<usize>> =
Mutex::new((PAGE_SIZE..PMEM_SIZE).step_by(PAGE_SIZE).collect());
}
impl PhysFrame {
#[export_name = "hal_frame_alloc"]
pub fn alloc() -> Option<Self> {
let ret = AVAILABLE_FRAMES
.lock()
.unwrap()
.pop_front()
.map(|paddr| PhysFrame { paddr });
trace!("frame alloc: {:?}", ret);
ret
}
#[export_name = "hal_zero_frame_paddr"]
pub fn zero_frame_addr() -> PhysAddr {
0
}
}
impl Drop for PhysFrame {
#[export_name = "hal_frame_dealloc"]
fn drop(&mut self) {
trace!("frame dealloc: {:?}", self);
AVAILABLE_FRAMES.lock().unwrap().push_back(self.paddr);
}
}
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
/// Map physical memory from here.
const PMEM_BASE: VirtAddr = 0x8_0000_0000;
PMEM_BASE + paddr
}
/// Ensure physical memory are mmapped and accessible.
fn ensure_mmap_pmem() {
FRAME_FILE.as_raw_fd();
}
/// Read physical memory from `paddr` to `buf`.
#[export_name = "hal_pmem_read"]
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
trace!("pmem read: paddr={:#x}, len={:#x}", paddr, buf.len());
assert!(paddr + buf.len() <= PMEM_SIZE);
ensure_mmap_pmem();
unsafe {
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
}
}
/// Write physical memory to `paddr` from `buf`.
#[export_name = "hal_pmem_write"]
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
trace!("pmem write: paddr={:#x}, len={:#x}", paddr, buf.len());
assert!(paddr + buf.len() <= PMEM_SIZE);
ensure_mmap_pmem();
unsafe {
buf.as_ptr()
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
}
}
/// Zero physical memory at `[paddr, paddr + len)`
#[export_name = "hal_pmem_zero"]
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
assert!(paddr + len <= PMEM_SIZE);
ensure_mmap_pmem();
unsafe {
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
}
}
/// Copy content of `src` frame to `target` frame
#[export_name = "hal_frame_copy"]
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
trace!("frame_copy: {:#x} <- {:#x}", target, src);
assert!(src + PAGE_SIZE <= PMEM_SIZE && target + PAGE_SIZE <= PMEM_SIZE);
ensure_mmap_pmem();
unsafe {
let buf = phys_to_virt(src) as *const u8;
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
}
}
/// Flush the physical frame.
#[export_name = "hal_frame_flush"]
pub fn frame_flush(_target: PhysAddr) {
// do nothing
}
const PAGE_SIZE: usize = 0x1000;
fn page_aligned(x: VirtAddr) -> bool {
x % PAGE_SIZE == 0
}
const PMEM_SIZE: usize = 0x4000_0000; // 1GiB
lazy_static! {
static ref FRAME_FILE: File = create_pmem_file();
}
fn create_pmem_file() -> File {
let dir = tempdir().expect("failed to create pmem dir");
let path = dir.path().join("pmem");
// workaround on macOS to avoid permission denied.
// see https://jiege.ch/software/2020/02/07/macos-mmap-exec/ for analysis on this problem.
#[cfg(target_os = "macos")]
std::mem::forget(dir);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.expect("failed to create pmem file");
file.set_len(PMEM_SIZE as u64)
.expect("failed to resize file");
trace!("create pmem file: path={:?}, size={:#x}", path, PMEM_SIZE);
let prot = libc::PROT_READ | libc::PROT_WRITE;
mmap(file.as_raw_fd(), 0, PMEM_SIZE, phys_to_virt(0), prot);
file
}
/// Mmap frame file `fd` to `vaddr`.
fn mmap(fd: libc::c_int, offset: usize, len: usize, vaddr: VirtAddr, prot: libc::c_int) {
// workaround on macOS to write text section.
#[cfg(target_os = "macos")]
let prot = if prot & libc::PROT_EXEC != 0 {
prot | libc::PROT_WRITE
} else {
prot
};
let ret = unsafe {
let flags = libc::MAP_SHARED | libc::MAP_FIXED;
libc::mmap(vaddr as _, len, prot, flags, fd, offset as _)
} as usize;
trace!(
"mmap file: fd={}, offset={:#x}, len={:#x}, vaddr={:#x}, prot={:#b}",
fd,
offset,
len,
vaddr,
prot,
);
assert_eq!(ret, vaddr, "failed to mmap: {:?}", Error::last_os_error());
}
trait FlagsExt {
fn to_mmap_prot(&self) -> libc::c_int;
}
impl FlagsExt for MMUFlags {
fn to_mmap_prot(&self) -> libc::c_int {
let mut flags = 0;
if self.contains(MMUFlags::READ) {
flags |= libc::PROT_READ;
}
if self.contains(MMUFlags::WRITE) {
flags |= libc::PROT_WRITE;
}
if self.contains(MMUFlags::EXECUTE) {
flags |= libc::PROT_EXEC;
}
flags
}
}
lazy_static! {
static ref STDIN: Mutex<VecDeque<u8>> = Mutex::new(VecDeque::new());
static ref STDIN_CALLBACK: Mutex<Vec<Box<dyn Fn() -> bool + Send + Sync>>> =
Mutex::new(Vec::new());
}
/// Put a char by serial interrupt handler.
fn serial_put(x: u8) {
STDIN.lock().unwrap().push_back(x);
STDIN_CALLBACK.lock().unwrap().retain(|f| !f());
}
#[export_name = "hal_serial_set_callback"]
pub fn serial_set_callback(callback: Box<dyn Fn() -> bool + Send + Sync>) {
STDIN_CALLBACK.lock().unwrap().push(callback);
}
#[export_name = "hal_serial_read"]
pub fn serial_read(buf: &mut [u8]) -> usize {
let mut stdin = STDIN.lock().unwrap();
let len = stdin.len().min(buf.len());
for c in &mut buf[..len] {
*c = stdin.pop_front().unwrap();
}
len
}
/// Output a char to console.
#[export_name = "hal_serial_write"]
pub fn serial_write(s: &str) {
eprint!("{}", s);
}
/// Get current time.
#[export_name = "hal_timer_now"]
pub fn timer_now() -> Duration {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
}
/// Set a new timer.
///
/// After `deadline`, the `callback` will be called.
#[export_name = "hal_timer_set"]
pub fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>) {
std::thread::spawn(move || {
let now = timer_now();
if deadline > now {
std::thread::sleep(deadline - now);
}
callback(timer_now());
});
}
#[export_name = "hal_vdso_constants"]
pub fn vdso_constants() -> VdsoConstants {
let tsc_frequency = 3000u16;
let mut constants = VdsoConstants {
max_num_cpus: 1,
features: Features {
cpu: 0,
hw_breakpoint_count: 0,
hw_watchpoint_count: 0,
},
dcache_line_size: 0,
icache_line_size: 0,
ticks_per_second: tsc_frequency as u64 * 1_000_000,
ticks_to_mono_numerator: 1000,
ticks_to_mono_denominator: tsc_frequency as u32,
physmem: PMEM_SIZE as u64,
version_string_len: 0,
version_string: Default::default(),
};
constants.set_version_string(git_version!(
prefix = "git-",
args = ["--always", "--abbrev=40", "--dirty=-dirty"]
));
constants
}
#[export_name = "hal_current_pgtable"]
pub fn current_page_table() -> usize {
0
}
/// 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

@ -1,93 +0,0 @@
/// 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,17 +1,66 @@
[package]
name = "kernel-hal"
version = "0.1.0"
authors = ["Runji Wang <wangrunji0408@163.com>"]
authors = [
"Runji Wang <wangrunji0408@163.com>",
"Yuekai Jia <equation618@gmail.com>",
]
edition = "2018"
description = "Kernel HAL interface definations."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "1.2"
trapframe = "0.8.0"
numeric-enum-macro = "0.2"
acpi = "1.1"
[features]
default = ["libos"]
smp = []
libos = [
"nix",
"tempfile",
"async-std",
"bitmap-allocator",
"zcore-drivers/mock",
]
graphic = ["zcore-drivers/graphic"]
#[patch.crates-io]
#trapframe = { path = "/home/xly/rust/arch-lib/trapframe-rs" }
loopback = []
[dependencies]
log = "0.4"
spin = "0.9"
cfg-if = "1.0"
bitflags = "1.3"
trapframe = "0.9.0"
git-version = "0.3"
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"

View File

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

View File

@ -0,0 +1,23 @@
//! CPU information.
use crate::utils::init_once::InitOnce;
pub(super) static CPU_FREQ_MHZ: InitOnce<u16> = InitOnce::new_with_default(10);
hal_fn_impl! {
impl mod crate::hal_fn::cpu {
fn cpu_id() -> u8 {
let mut cpu_id;
unsafe { core::arch::asm!("mv {0}, tp", out(reg) cpu_id) };
cpu_id
}
fn cpu_frequency() -> u16 {
*CPU_FREQ_MHZ
}
fn reset() -> ! {
info!("shutdown...");
super::sbi::shutdown()
}
}
}

View File

@ -0,0 +1,113 @@
use alloc::boxed::Box;
use alloc::format;
use zcore_drivers::builder::{DevicetreeDriverBuilder, IoMapper};
use zcore_drivers::irq::riscv::ScauseIntCode;
use zcore_drivers::uart::BufferedUart;
use zcore_drivers::{Device, DeviceResult};
use crate::common::vm::GenericPageTable;
use crate::{drivers, mem::phys_to_virt, CachePolicy, MMUFlags, PhysAddr, VirtAddr};
struct IoMapperImpl;
impl IoMapper for IoMapperImpl {
fn query_or_map(&self, paddr: PhysAddr, size: usize) -> Option<VirtAddr> {
let vaddr = phys_to_virt(paddr);
let mut pt = super::vm::kernel_page_table().lock();
if let Ok((paddr_mapped, _, _)) = pt.query(vaddr) {
if paddr_mapped == paddr {
Some(vaddr)
} else {
warn!(
"IoMapper::query_or_map: not linear mapping: vaddr={:#x}, paddr={:#x}",
vaddr, paddr_mapped
);
None
}
} else {
let size = crate::addr::align_up(size);
let flags = MMUFlags::READ
| MMUFlags::WRITE
| MMUFlags::HUGE_PAGE
| MMUFlags::from_bits_truncate(CachePolicy::UncachedDevice as usize);
if let Err(err) = pt.map_cont(vaddr, size, paddr, flags) {
warn!(
"IoMapper::query_or_map: failed to map {:#x?} => {:#x}, flags={:?}: {:?}",
vaddr..vaddr + size,
paddr,
flags,
err
);
None
} else {
Some(vaddr)
}
}
}
}
/// Initialize device drivers.
pub(super) fn init() -> DeviceResult {
// prase DTB and probe devices
let dev_list =
DevicetreeDriverBuilder::new(phys_to_virt(crate::KCONFIG.dtb_paddr), IoMapperImpl)?
.build()?;
// add drivers
for dev in dev_list.into_iter() {
if let Device::Uart(uart) = dev {
drivers::add_device(Device::Uart(BufferedUart::new(uart)));
} else {
drivers::add_device(dev);
}
}
#[cfg(not(feature = "loopback"))]
{
use alloc::sync::Arc;
use zcore_drivers::bus::pci;
let pci_devs = pci::init(Some(Arc::new(IoMapperImpl)))?;
for d in pci_devs.into_iter() {
drivers::add_device(d);
}
}
intc_init()?;
#[cfg(feature = "graphic")]
if let Some(display) = drivers::all_display().first() {
crate::console::init_graphic_console(display.clone());
if display.need_flush() {
// TODO: support nested interrupt to render in time
crate::thread::spawn(crate::common::future::DisplayFlushFuture::new(display, 30));
}
}
#[cfg(feature = "loopback")]
{
use crate::net;
net::init();
}
Ok(())
}
pub(super) fn intc_init() -> DeviceResult {
let irq = drivers::all_irq()
.find(format!("riscv-intc-cpu{}", crate::cpu::cpu_id()).as_str())
.expect("IRQ device 'riscv-intc' not initialized!");
// register soft interrupts handler
irq.register_handler(
ScauseIntCode::SupervisorSoft as _,
Box::new(super::trap::super_soft),
)?;
// register timer interrupts handler
irq.register_handler(
ScauseIntCode::SupervisorTimer as _,
Box::new(super::trap::super_timer),
)?;
irq.unmask(ScauseIntCode::SupervisorSoft as _)?;
irq.unmask(ScauseIntCode::SupervisorTimer as _)?;
Ok(())
}

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