Compare commits

..

580 Commits

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
chyyuu ebd09da782
Merge pull request #212 from chyyuu/master
update scripts\linux\baremetal-test-fail.txt (with 55 failed libc-test app)
2021-08-15 18:30:23 +08:00
Yu Chen 8040866cb2 update README.md 2021-08-15 09:34:37 +08:00
Yu Chen 1b29ebb0cd update linux/baremetal-test-fail.txt. Now num of failed is 55 2021-08-15 09:18:55 +08:00
Yu Chen f2bbbd6791 change LOG=warn in scripts/baremetal-libc-test-ones.py 2021-08-15 09:17:34 +08:00
chyyuu 443e8e635a
Merge pull request #210 from chyyuu/master
Fix bug:  add function: hal_current_pgtable in kernel-hal-unix for LibOS mode
2021-08-14 19:13:53 +08:00
Yu Chen 3cb6a175b8 add function: hal_current_pgtable in kernel-hal-unix for LibOS mode 2021-08-14 17:50:52 +08:00
Yu Chen 8cca170dae update scripts/linux/baremetal-test-fail-rv64.txt, now only 54 failed rv64 libc-test apps 2021-08-14 17:28:50 +08:00
Yu Chen cb963dc6a3 update .gitignore for some useless file built by CI 2021-08-14 16:16:17 +08:00
Yu Chen 1744d66031 Merge remote-tracking branch 'zyr/riscv-dynamic' 2021-08-14 16:07:46 +08:00
DeathWish5 116800404d Fix bug of ELF loader, hack MMUFlags to avoid mprotect 2021-08-14 15:04:47 +08:00
Yu Chen cf27a8b6a8 add scripts/baremetal-libc-test-find-failed-riscv64.py for producing cripts/linux/baremetal-test-fail-rv64.txt 2021-08-14 14:35:24 +08:00
Yu Chen 32a8fd1f4a add baremetal rv64 CI test in rustc20210727.yml 2021-08-14 13:07:09 +08:00
Yu Chen 8781fb3e12 update fialed app for baremetal-rv64 in baremetal-test-fail-rv64.txt 2021-08-14 13:02:46 +08:00
Yu Chen 38edbea85d update README.md for baremetal CI 2021-08-14 11:40:14 +08:00
Yu Chen c02d760302 add baremetal-libc-test-ones-riscv64.py & baremetal-test-ones-rv64.txt for some app tests 2021-08-14 10:40:37 +08:00
chyyuu f332594dc3
Merge pull request #208 from shzhxh/master-s
add baremetal test CI for riscv
2021-08-13 23:30:43 +08:00
shzhxh 292319c5e4 add baremetal test CI for riscv 2021-08-13 21:45:31 +08:00
chyyuu d6e251a4e2
Merge pull request #206 from elliott10/xly-master
Parsing kernel command line from device tree to CI test
2021-08-13 20:42:36 +08:00
elliott10 83edc1e06a
Parsing kernel command line from device tree to CI test 2021-08-13 16:45:52 +08:00
chyyuu b515d7f247
Merge pull request #205 from shzhxh/master-s
add test case for rv64
2021-08-13 16:18:35 +08:00
shzhxh 762e0df13a add test case for rv64 2021-08-13 13:51:14 +08:00
GCYYfun c9c4722893
Merge pull request #203 from rcore-os/new
add prebuilt riscv rootfs in Makefile
2021-08-12 18:31:36 +08:00
GCYY 710419c9e5 add prebuilt riscv rootfs in Makefile 2021-08-12 16:50:07 +08:00
chyyuu 3a9281723a
Merge pull request #202 from SummerVibes/master
fix(main.rs in zCore dir): fix bugs in debug mode
2021-08-11 10:43:17 +08:00
Runji Wang 8bc6fbaf27
fix slice mutability 2021-08-11 00:28:01 +08:00
xuebling 98c36997a0 docs(Quick start for RISCV64): add command that make image
After practice, I found the "Quick start for RISCV64" in README.md was
not quite right, we need to get riscv64.img first.
2021-08-10 19:26:05 +08:00
xuebling 21217926ac Merge remote-tracking branch 'origin/master' 2021-08-10 19:18:53 +08:00
xuebling 963a287c27 fix(main.rs in zCore dir): fix parameter of main() under the linux feature 2021-08-10 19:18:20 +08:00
XueBling 3a9553a7df
Update zCore/src/main.rs
Co-authored-by: Runji Wang <wrj19@mails.tsinghua.edu.cn>
2021-08-10 19:14:22 +08:00
XueBling 3a37a19405
Merge branch 'rcore-os:master' into master 2021-08-10 18:17:46 +08:00
xuebling a39799d758 fix(main.rs in zCore dir): fix bugs in debug mode
Fix bug encountered when run command 'make run arch=riscv64
linux=1',this bug only appears in debug mode.
This bug appears because
from_raw_parts_mut() will validate ptr in debug mode.
2021-08-10 18:16:15 +08:00
chyyuu 5f87b12fa7
Merge pull request #201 from chyyuu/master
fix bugs for load&exec linux static exec programs.
2021-08-10 10:41:20 +08:00
Yu Chen b632f23452 Update linux/baremetal-test-fail.txt. Now the num of failed libc-tests is 83 2021-08-10 08:05:44 +08:00
Yu Chen 364f54cb38 cargo fmt 2021-08-10 04:01:38 +08:00
Yu Chen 5f2c564dc9 fix bug for load&exec static elf program 2021-08-10 03:19:03 +08:00
Yu Chen 11a1d25bb4 update scripts/baremetal-libc-test-ones.py 2021-08-10 03:17:21 +08:00
Yu Chen 41f083dfda add scripts for test special libc-test apps. e.g. 'cd script; python3 baremetal-libc-test-ones.py' 2021-08-09 22:33:39 +08:00
chyyuu bf3ff3090e
Merge pull request #200 from chyyuu/master
Updaet baremetal CI
2021-08-09 13:55:28 +08:00
Yu Chen 7efb623730 typo fix 2021-08-09 13:05:31 +08:00
Yu Chen 4296320656 update baremetal CI 2021-08-09 11:57:56 +08:00
Yu Chen 03e3bd4c92 workaround github action's error "Failed to get "write" lock. Is another process using the image [target/x86_64/release/disk.qcow2]?"
Detail info:

make[1]: Entering directory '/home/runner/work/zCore/zCore/zCore'
cp rboot.conf target/x86_64/release/esp/EFI/Boot/rboot.conf
timeout --foreground 8s  qemu-system-x86_64 -smp 1 -machine q35 -cpu Haswell,+smap,-check,-fsgsbase -drive if=pflash,format=raw,readonly,file=../rboot/OVMF.fd -drive format=raw,file=fat:rw:target/x86_64/release/esp -drive format=qcow2,file=target/x86_64/release/disk.qcow2,id=disk,if=none -device ich9-ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -serial mon:stdio -m 4G -nic none -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none -nographic
qemu-system-x86_64: -drive format=qcow2,file=target/x86_64/release/disk.qcow2,id=disk,if=none: Failed to get "write" lock
Is another process using the image [target/x86_64/release/disk.qcow2]?
make[1]: *** [Makefile:185: baremetal-test] Error 1
make[1]: Leaving directory '/home/runner/work/zCore/zCore/zCore'

some maybe useful info:
https://github.com/hashicorp/packer/issues/10223
2021-08-09 11:00:25 +08:00
Yu Chen d858d48cf6 fix bug for baremetal CI 2021-08-09 10:17:06 +08:00
Yu Chen bd618fdc32 fix bug for baremetal CI 2021-08-09 10:02:32 +08:00
Yu Chen b36904bb7a fix bug for baremetal CI 2021-08-09 09:47:14 +08:00
chyyuu 25ab5d33f1
Merge pull request #199 from chyyuu/master
fix&update baremetal x86-64 zcore for linux CI
2021-08-09 08:29:31 +08:00
Yu Chen b8f8150409 tune the baremetal CI 2021-08-09 07:32:59 +08:00
Yu Chen be909d18c7 update .gitignore for baremetal CI 2021-08-09 07:04:30 +08:00
Yu Chen 346a0c54f2 fix&update baremetal x86-64 zcore for linux CI 2021-08-09 06:41:29 +08:00
chyyuu fda1cb04b3
Merge pull request #197 from chyyuu/master
add baremetal-x86-64 CI test
2021-08-08 20:39:13 +08:00
Yu Chen 4f432b1f31 fix for baremetal CI 2021-08-08 20:18:46 +08:00
Yu Chen 25ef50e3d4 fix for baremetal CI 2021-08-08 20:07:15 +08:00
Yu Chen bcf2d84d1e fix for baremetal CI 2021-08-08 20:01:38 +08:00
Yu Chen 2280562d37 fix for baremetal CI 2021-08-08 19:58:02 +08:00
Yu Chen 4ff438d8fe fix for baremetal CI 2021-08-08 19:50:35 +08:00
Yu Chen 530780fe47 fix for baremetal CI 2021-08-08 19:44:12 +08:00
Yu Chen 676d8ab0af add CI for baremetal zcore with linux x86-64 syscall using musl libc-test 2021-08-08 19:33:45 +08:00
Yu Chen 36fb9da937 update for braremetal zcore baremetal x86-64 CI 2021-08-08 17:34:45 +08:00
Yu Chen a81017e746 Merge branch 'master' of https://github.com/rcore-os/zCore 2021-08-06 15:50:17 +08:00
Runji Wang aa751565b1
Merge pull request #196 from rcore-os/fix
Fix compile error and clean up
2021-08-06 15:00:57 +08:00
Runji Wang 73b7238bc1 code clean up 2021-08-06 14:49:26 +08:00
Runji Wang 08c9932351 linux-syscall: fix build script 2021-08-06 14:17:34 +08:00
Runji Wang f08f467cfe fix deprecated QEMU option 'ide-drive'. close #195 2021-08-06 13:59:00 +08:00
chyyuu 87e199c28d
update CI (#193) 2021-08-05 11:21:04 +08:00
Yu Chen 0571d833cf Merge branch 'master' of https://github.com/rcore-os/zCore 2021-08-05 10:02:39 +08:00
Yu Chen 39ef067d01 update CI 2021-08-05 10:02:25 +08:00
chyyuu 2b32cbf9a7
Merge pull request #191 from chyyuu/master
add support for riscv64
2021-08-01 09:13:15 +08:00
Yu Chen a3abe79683 fix CI syntax bug 2021-08-01 09:04:41 +08:00
Yu Chen 8a2876a989 udpate CI for problem 'Pull request: some checks haven’t completed yet' in github 2021-08-01 09:00:17 +08:00
Yu Chen d399a066b3 update README.md and Makefile 2021-07-31 09:52:28 +08:00
Yu Chen 016e12333c remove two zcore core-tests apps in test-check-passed.txt for CI passing 2021-07-31 00:27:03 +08:00
Yu Chen 5e78ad8f34 update CI 2021-07-31 00:11:09 +08:00
Yu Chen b9d92075cf update for CI 2021-07-30 23:52:10 +08:00
Yu Chen cbd87b06d8 update, and 'cargo fmt --all -- --check' passed 2021-07-30 22:19:49 +08:00
DeathWish5 ccbcc8a23c bug fix, linux-loader can run 2021-07-30 21:23:12 +08:00
DeathWish5 39c6c590e9 run with libos will end with core dump 2021-07-30 20:07:06 +08:00
DeathWish5 48d82586b5 x86_64/riscv64 with linux=1 can run 2021-07-30 18:15:50 +08:00
DeathWish5 5bb1bdec94 try to merge riscv64 to master 2021-07-30 16:28:53 +08:00
Yu Chen ecebbabe29 update rboot git-submodule 2021-07-30 14:53:50 +08:00
Yu Chen 114fa60675 update CI rustcnightly.yml for rboot 2021-07-30 14:44:08 +08:00
Yu Chen 037e12feb1 fix typo in rustcnightly.yml 2021-07-30 14:27:27 +08:00
Yu Chen 6648b7a554 add CI for rustc-20210727 & nightly 2021-07-30 14:14:16 +08:00
Yu Chen b00b0b383b add some info for prebuild/linux/libc-libos.so 2021-07-30 10:23:11 +08:00
Yu Chen 00b8ff8815 update CI/CD workflow# every day at 3:40 2021-07-29 23:26:56 +08:00
Yu Chen ba83f985cb try in default branch 2021-07-29 23:09:23 +08:00
DeathWish5 a2f4fafa5f schedule test 5 2021-07-29 22:28:18 +08:00
DeathWish5 2fae28d268 schedule test 4 2021-07-29 22:20:58 +08:00
DeathWish5 8600ec08a1 minor bug fix 2021-07-29 22:01:15 +08:00
DeathWish5 34b737651a ignore roundl.exe, schedule test 3 2021-07-29 21:55:39 +08:00
DeathWish5 ac125e293d schedule test 2 2021-07-29 21:23:24 +08:00
DeathWish5 d52cbd63ee schedule test 2021-07-29 21:17:01 +08:00
DeathWish5 f2bfa7d61a update rboot toolchain 2021-07-29 19:02:48 +08:00
DeathWish5 f8e9cdfcdf use nightly toolchain 2021-07-29 18:50:09 +08:00
DeathWish5 d421504841 dummy change, just to trigger ci 2021-07-29 18:31:03 +08:00
DeathWish5 25560abda2 lock toolchain 2021-07-29 18:07:47 +08:00
DeathWish5 eda8a68cbb ci-test v0.11 2021-07-29 17:23:06 +08:00
DeathWish5 4b90f13e18 ci-test v0.1 2021-07-29 17:03:04 +08:00
Luoyuan Xiao e41432e992
Merge pull request #188 from DeathWish5/rv64
update toolchain
2021-07-28 20:21:15 +08:00
DeathWish5 453c58d340 Remove an unsafe code 2021-07-28 20:08:04 +08:00
DeathWish5 edb4a65f69 Update toolchain to 2021-7-27, format some code 2021-07-28 19:49:02 +08:00
elliott10 06f1d5c482
add memory identity mapping when booting 2021-07-28 17:45:20 +08:00
Yu Chen 2b6d6535f8 Now zCore-linux can compiling, but can not run 2021-07-28 15:52:43 +08:00
Yu Chen f51db8b786 update uart_16550 to ver. 0.2.15 for rustc nightly 2021-07-28 00:06:55 +08:00
Yu Chen e6e9a85a7c x86_64 crate can not pass 'cargo clippy --all-features', so del '--all-features' 2021-07-28 00:05:41 +08:00
Yu Chen 939b63f898 fix bugs in shown in 'cargo clippy --all-features' 2021-07-27 22:11:09 +08:00
Yu Chen d9e22795e5 disable RVM crate temporarily 2021-07-27 21:46:35 +08:00
Yu Chen 3f3613ec93 update Cargo.tomls for rustc 1.56.0-nightly (08095fc1f 2021-07-26) 2021-07-27 11:25:38 +08:00
Luoyuan Xiao 4002db573e
Update README.md 2021-05-14 13:41:35 +08:00
elliott10 72d1a9a4b4
clean unused code 2021-05-14 12:52:34 +08:00
elliott10 fa5bb18150
Reconstruct kernel virtual memory
0xffffffff_00000000 --> 0x00000000
0xffffffff_80000000 --> 0x80000000
2021-05-13 23:57:12 +08:00
elliott10 3fbd77e15e
run ... 2021-04-21 20:07:05 +08:00
elliott10 b6d6ba4890
Using virtio-drivers lib 2021-04-20 12:46:10 +08:00
elliott10 2abe33fbe2
By refreshing TLB, Fix the problem of the child process memory access errors after fork 2021-04-16 17:21:48 +08:00
elliott10 3191f05b5e
pt restore 2021-04-14 17:09:01 +08:00
elliott10 d0e8946553 Workaround running busybox sh 2021-04-07 22:20:48 +08:00
elliott10 3d637495c1 handle riscv64 irq 2021-04-04 15:30:13 +08:00
elliott10 8fba7026f6 fix riscv64 syscall handle 2021-03-30 22:35:45 +08:00
elliott10 5d486739d5 Run a User program hello_world 2021-03-30 21:46:09 +08:00
elliott10 1d1e6f49cf Load a Linux user program: busybox 2021-03-25 09:21:05 +08:00
elliott10 1c3db8bdc2 Compile through zCore linux required components 2021-03-23 12:04:59 +08:00
elliott10 39ad5c7a1e let virtio init and probe 2021-03-12 18:22:57 +08:00
elliott10 f37497774a fixed page table 2021-03-12 11:59:47 +08:00
elliott10 6ca4c1100a Merge branch 'riscv64-k210' of github.com:elliott10/zCore into main 2021-03-10 15:03:53 +08:00
elliott10 909e1b5745 add virtio files 2021-03-10 15:02:14 +08:00
elliott10 836eddc916 Rebuild the pagetable of virtual memory and remap kernel 2021-03-10 15:00:04 +08:00
Luoyuan Xiao c3f63fbfd4
Update Porting zCore to RiscV64 (Qemu).md 2021-03-04 20:59:54 +08:00
Luoyuan Xiao eece79757c
Update Porting zCore to RiscV64 (Qemu).md 2021-03-04 20:59:10 +08:00
Luoyuan Xiao 3a0664321b
Update Porting zCore to RiscV64 (Qemu).md 2021-03-04 20:50:43 +08:00
Luoyuan Xiao 862d4ecb5a
Update Porting zCore to RiscV64 (Qemu).md 2021-03-04 20:49:56 +08:00
Luoyuan Xiao fc6d023942
Update Porting zCore to RiscV64 (Qemu).md 2021-03-04 20:40:55 +08:00
Luoyuan Xiao f9e708913a
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 20:00:05 +08:00
Luoyuan Xiao 2d8ea7bcc4
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 19:56:27 +08:00
Luoyuan Xiao 6978ff312a
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 16:54:32 +08:00
Luoyuan Xiao 6ba6e5d4c2
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 16:32:34 +08:00
Luoyuan Xiao 55e6cd265b
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 16:01:11 +08:00
Luoyuan Xiao 6aa1a0fc85
Update Porting zCore to RiscV64 (Qemu).md 2021-02-25 14:57:04 +08:00
Luoyuan Xiao 56d69b45b6
Create Porting zCore to RiscV64 (Qemu).md 2021-02-25 12:06:14 +08:00
elliott10 58d745a01e enable riscv64 interrupt and plic 2021-01-27 19:52:58 +08:00
elliott10 4c8faef162 First of all, compiling passed, and print something 2021-01-22 20:43:16 +08:00
elliott10 c91eca8892 riscv64 init 2021-01-22 20:40:22 +08:00
338 changed files with 24286 additions and 7307 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

32
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Deploy docs
on:
push:
pull_request:
env:
rust_toolchain: nightly-2022-01-20
jobs:
doc:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
- name: Build docs
run: |
cargo doc --no-deps --all-features
.github/scripts/add-doc-index.sh
- name: Deploy to Github Pages
if: ${{ github.ref == 'refs/heads/master' }}
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: target/doc

View File

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

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

18
.gitignore vendored
View File

@ -1,8 +1,18 @@
/target
**/.*
!.github/
!.cargo/
!.vscode/settings.json
**/target
**/*.rs.bk
*.img
*.bin
*.log
Cargo.lock
/ignored
/rootfs
/prebuilt/linux/alpine*
.idea
scripts/linux/test-result.txt
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,17 +1,17 @@
[workspace]
members = [
"drivers",
"kernel-hal",
"zircon-object",
"zircon-syscall",
"zircon-loader",
"linux-object",
"linux-syscall",
"linux-loader",
"kernel-hal-unix",
"kernel-hal",
"loader",
"zCore",
"xtask",
]
default-members = ["xtask"]
exclude = ["zircon-user", "rboot"]
exclude = [
"zircon-user",
"zCore",
"rboot",
]
[profile.release]
lto = true

View File

@ -1,62 +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)
# 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
rootfs: prebuilt/linux/$(ROOTFS_TAR)
rm -rf rootfs && mkdir -p rootfs
tar xf $< -C rootfs
cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
@for VAR in $(BASENAMES); do gcc $(TEST_DIR)$$VAR.c -o $(DEST_DIR)$$VAR $(CFLAG); done
# put 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
@mkdir -p $(TMP_ROOTFS)
@tar xf $< -C $(TMP_ROOTFS)
@cp $(TMP_ROOTFS)/lib/ld-musl-x86_64.so.1 rootfs/lib/
@rcore-fs-fuse $@ rootfs zip
@cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
# 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
rv64-image: rcore-fs-fuse
@echo building riscv64.img
@rm -rf rootfs
@mkdir rootfs
@mkdir rootfs/bin
ifeq ($(wildcard prebuilt/linux/riscv64/busybox),)
@mkdir -p prebuilt/linux/riscv64
@wget https://github.com/rcore-os/busybox-prebuilts/raw/master/busybox-1.30.1-riscv64/busybox -O prebuilt/linux/riscv64/busybox
endif
@cp prebuilt/linux/riscv64/busybox rootfs/bin/
@@rcore-fs-fuse riscv64.img rootfs zip
@qemu-img resize riscv64.img +50M
# clean targets
clean:
cargo clean
rm -rf rootfs
rm -rf ignored/target
find zCore -maxdepth 1 -name "*.img" -delete
rt-test:
cd rootfs/x86_64 && git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/clrkwllms/rt-tests --depth 1
cd rootfs/x86_64/rt-tests && make
echo x86 gcc build rt-test,now need manual modificy.

235
README.md
View File

@ -1,24 +1,42 @@
# 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
- 2020.04.16: Zircon console is working on zCore! 🎉
## Quick start for RISCV64
```sh
make riscv-image
cd zCore
make run ARCH=riscv64 LINUX=1
```
## Getting started
Environments
* [Rust toolchain](http://rustup.rs)
* [QEMU](https://www.qemu.org)
* [Git LFS](https://git-lfs.github.com)
- [Rust toolchain](http://rustup.rs)
- [QEMU](https://www.qemu.org)
- [Git LFS](https://git-lfs.github.com)
### Developing environment info
- current rustc -- rustc 1.60.0-nightly (5e57faa78 2022-01-19)
- current rust-toolchain -- nightly-2022-01-20
- current qemu -- 5.2.0 -> 6.2.0
Clone repo and pull prebuilt fuchsia images:
@ -35,55 +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
ASYNC_STD_THREAD_COUNT=1 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:
@ -94,15 +174,91 @@ cd scripts && python3 core-tests.py
# Check `zircon/test-result.txt` for results.
```
#### x86-64 Linux related
Run Linux musl libc-tests for CI:
```sh
make rootfs && make libc-test
cd scripts && python3 libc-tests.py
# Check `linux/test-result.txt` for results.
## Prepare rootfs with libc-test apps
make baremetal-test-img
## Build zCore kernel
cd zCore && make build MODE=release LINUX=1 ARCH=x86_64
## Testing
cd scripts && python3 baremetal-libc-test.py
##
```
You can use [`scripts/baremetal-libc-test-ones.py`](./scripts/baremetal-libc-test-ones.py) & [`scripts/linux/baremetal-test-ones.txt`](./scripts/linux/baremetal-test-ones.txt) to test specified apps.
[`scripts/linux/baremetal-test-fail.txt`](./scripts/linux/baremetal-test-fail.txt) includes all failed x86-64 apps (We need YOUR HELP to fix bugs!)
#### riscv-64 Linux related
Run Linux musl libc-tests for CI:
```sh
## Prepare rootfs with libc-test & oscomp apps
make riscv-image
## Build zCore kernel & Testing
cd scripts && python3 baremetal-test-riscv64.py
##
```
You can use[scripts/baremetal-libc-test-ones-riscv64.py](./scripts/baremetal-libc-test-ones-riscv64.py) & [`scripts/linux/baremetal-test-ones-rv64.txt`](scripts/linux/baremetal-test-ones-rv64.txt)to test
specified apps.
[`scripts/linux/baremetal-test-fail-riscv64.txt`](./scripts/linux/baremetal-test-fail-riscv64.txt)includes all failed riscv-64 apps (We need YOUR HELP to fix bugs!)
## Graph/Game
snake game: <https://github.com/rcore-os/rcore-user/blob/master/app/src/snake.c>
### Step1: compile usr app
We can use musl-gcc compile it in x86_64 mode
### Step2: change zcore for run snake app first.
change zCore/zCore/main.rs L176
vec!["/bin/busybox".into(), "sh".into()]
TO
vec!["/bin/snake".into(), "sh".into()]
### Step3: prepare root fs image, run zcore in linux-bare-metal mode
exec:
```sh
cd zCore #zCore ROOT DIR
make rootfs
cp ../rcore-user/app/snake rootfs/bin #copy snake ELF file to rootfs/bin
make image # build rootfs image
cd zCore #zCore kernel dir
make run MODE=release LINUX=1 GRAPHIC=on
```
Then you can play the game.
Operation
- Keyboard
- `W`/`A`/`S`/`D`: Move
- `R`: Restart
- `ESC`: End
- Mouse
- `Left`: Speed up
- `Right`: Slow down
- `Middle`: Pause/Resume
## Doc
```
make doc
```
### RISC-V 64 porting info
- [porting riscv64 doc](./docs/porting-rv64.md)
## Components
### Overview
@ -121,5 +277,6 @@ cd scripts && python3 libc-tests.py
| Thread Management | `executor` | `async-std::task` |
| Exception Handling | Interrupt | Signal |
### Some plans
- https://github.com/rcore-os/zCore/wiki/Plans
### Small Goal & Little 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
/ #
```

91
docs/porting-rv64.md Normal file
View File

@ -0,0 +1,91 @@
# Porting zCore to RiscV64 (Qemu)
### 2021-03-04
<br>之前的内存部分,使用的比较简单的页表映射大页的方式;
<br>在加载Qemu文件系统镜像到内存中解开时使用就需要对虚拟内存、virtio-blk-device以及SimpleFileSystem完全初始化好
<br>其中有些可以比较方便地调用crate库
<br>对中间抽象层kernel-hal的内存操作相关函数的实现
* PageTable 新建页表或获取页表;
* trait PageTableTrait从虚拟地址到物理地址的映射、查询等;
* hal_frame_alloc物理帧实现
* pmem_write/read物理地址访问;
<br>其中把rCore中的可复用的页表构建PageTableImpl实际映射的MemoryArea移到zCore中
<br>在底下bare层的函数变量暴露给启动部分使用修复一些错误
<br>从目前的运行结果看,应该是页表部分应该是切换好了;
```
[1.2317591s DEBUG 0 0:0] switch table 8000000000080220 -> 8000000000080a49
```
### zCore系统结构<br>
![](./structure.svg)
### riscv64初始移植路径<br>
按照:<br>
|Qemu riscv64|-|kernel-hal-bare|-|kernel-hal|-|zircon-object/ linux-object|-|linux-syscall|-|linux-loader|-|busybox|
|-|-|-|-|-|-|-|-|-|-|-|-|-|
### 实现思路
* 分析Makefile的结构弄清楚包括编译和运行的命令执行流
* 结合Qemu virt和opensbi以及编译出所需文件格式的kernel也包括用户文件系统生成由x86_64到riscv64这些命令过程写入Makefile
* 分析Cargo.toml的结构理解各个features和依赖库crates的关系如有些是可选的
<br><br>
* 创建关于riscv64的Rust target-spec-json编译目标规格描述.json
* 创建riscv64对应的链接脚本.ld以及启动入口汇编.asm
* 重要的修改部分在kernel-hal-barearch下添加riscv架构包括硬件初始化和对opensbi接口调用的封装
<br><br>
* 先定一个小目标让OS跑起来打印初始一段字符
* 这时会有大量的编译错误提示需要解决Rust错误提示很详细会建议如何来修正
- 报错可能来自多个方面包括riscv64在Cargo.toml中的依赖库非必需的crates先关掉添加架构相关所需要的
- kernel-hal-bare中缺失的待实现的接口可见kernel-hal中的定义参考了arch/x86_64的函数
- 与target_arch由x86_64移植到riscv64的cfg其相关的函数或变量需在代码中补上
- 变量及函数的作用范围等需要注意
* Rust的条件编译cfg`#[cfg(target_arch = "x86_64")]`也需要为riscv64实现一份
* 要让OS能打印要把串口输出初始化
- 有两种方式一种是调用opensbi的打印接口一种是MMIO的方式初始化串口输出
- 然后实现fmt::Write和宏println
<br><br>
* 关于中断可通过调用crate riscv方便的进行指令和寄存器操作
* 陷入trap填入riscv64上下文切换的汇编;
* 初始化S态各种中断包括时钟中断和plic外部中断在这里的Qemu和K210开发板会有不同
- K210对指令`rdtime`报错非法指令且无法通过tval取得指令值故K210无法通过riscv::register::time::read()读当前时间Qemu无此问题
- 通过联合opensbi调试riscv当硬件决定触发时钟中断时会将sip寄存器的STIP位设置为1当一条指令执行完毕后如果发现STIP为1此时如果sie 的STIE位也为1会进入S态时钟中断的处理程序
- 当在M态不进行时钟中断委派到S态时Qemu的M态可接收到S态时钟中断
- K210的M态无法收到S态中断时钟中断和软件中断可以委派到S态来收, 而PLIC外部中断即使委派了也不行, 真是大坑! 同时也要感谢前面童鞋踩过坑的提示;
* PLIC外部中断是uart串口输出和virtio-blk-device加载文件系统的关键部分
- Qemu UART0_IRQ=10, K210的串口ID为33
- 通过MMIO地址对平台级中断控制器PLIC的寄存器进行设置设置中断源的优先级分07级7是最高级设置中断target的全局阀值0..7] <= threshold会被屏蔽
- 使能target中某个给定ID的中断中断ID可查找qemu/include/hw/riscv/virt.h注意一个核的不同权限模式是不同Target算出的Target不同则操作的地址也不同跨步0x80
|Target:| 0 | 1 | 2 | | 3 | 4 | 5 |
|-|-|-|-|-|-|-|-|
|Hart0:| M | S | U | Hart1:| M | S | U |
这里基于opensbi后一般运行于Hart0 S态故为Target1
* PLIC中断初始化完成后初始化串口中断Qemu virt串口基地址是0x1000_0000而K210是0x38000000
- 串口中断处理函数,在每个字符从键盘输入时,输出打印出来;
* 接着处理虚拟内存和文件系统
* 由Qemu启动opensbi装载kernel并引导_start函数初始化日志log打印物理内存初始化进入硬件初始化
* 后以slice的方式载入ramfs文件系统到内存指定地址打开该SimpleFileSystem的文件系统并通过linux_loader调用用户程序busybox执行
* 解析由rcore-fs-fuse生成的Simple FileSystem通过SimpleFileSystem::open()来打开内存中的文件系统,读取文件和目录;
* 最后通过linux_loader::run busybox sh
* 之前这部分工作由uefi bootloader的rboot把initramfs放到内存的指定地址
* 内存要初始化好这里使用Qemu的virtio块设备故也需要初始化好
移植未完...<br>
系统运行效果演示:<br>
在移植过程中,得到老师和童鞋们的很多帮助!感谢!

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,31 +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.7.0"
kernel-hal = { path = "../kernel-hal" }
naive-timer = "0.1.0"
lazy_static = { version = "1.4", features = ["spin_no_std" ] }
[target.'cfg(target_arch = "x86_64")'.dependencies]
x86_64 = "0.13.2"
uart_16550 = "=0.2.12"
raw-cpuid = "9.0"
pc-keyboard = "0.5"
apic = { git = "https://github.com/rcore-os/apic-rs", rev = "fb86bd7" }
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" }
rcore-console = { git = "https://github.com/rcore-os/rcore-console", default-features = false, rev = "a980897b" }
acpi = "1.1"
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"], rev = "c62af46" }

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,151 +0,0 @@
use super::*;
use kernel_hal::{PageTableTrait, PhysAddr, VirtAddr, Result};
use riscv::addr::Page;
use riscv::paging::{PageTableFlags as PTF, *};
use riscv::register::satp;
/// Page Table
#[repr(C)]
pub struct PageTableImpl {
root_paddr: PhysAddr,
}
impl PageTableImpl {
/// Create a new `PageTable`.
#[allow(clippy::new_without_default)]
#[export_name = "hal_pt_new"]
pub fn new() -> Self {
let root_frame = Frame::alloc().expect("failed to alloc frame");
let root_vaddr = phys_to_virt(root_frame.paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
root.zero();
let current =
phys_to_virt(satp::read().frame().start_address().as_usize()) as *const PageTable;
map_kernel(root_vaddr as _, current as _);
trace!("create page table @ {:#x}", root_frame.paddr);
PageTableImpl {
root_paddr: root_frame.paddr,
}
}
#[cfg(target_arch = "riscv32")]
fn get(&mut self) -> Rv32PageTable<'_> {
let root_vaddr = phys_to_virt(self.root_paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
Rv32PageTable::new(root, phys_to_virt(0))
}
#[cfg(target_arch = "riscv64")]
fn get(&mut self) -> Rv39PageTable<'_> {
let root_vaddr = phys_to_virt(self.root_paddr);
let root = unsafe { &mut *(root_vaddr as *mut PageTable) };
Rv39PageTable::new(root, phys_to_virt(0))
}
}
impl PageTableTrait for PageTableImpl {
/// Map the page of `vaddr` to the frame of `paddr` with `flags`.
#[export_name = "hal_pt_map"]
fn map(&mut self, vaddr: VirtAddr, paddr: PhysAddr, flags: MMUFlags) -> Result<()> {
let mut pt = self.get();
let page = Page::of_addr(vaddr);
let frame = riscv::addr::Frame::of_addr(riscv::addr::PhysAddr::new(paddr));
pt.map_to(page, frame, flags.to_ptf(), &mut FrameAllocatorImpl)
.unwrap()
.flush();
trace!("map: {:x?} -> {:x?}, flags={:?}", vaddr, paddr, flags);
Ok(())
}
/// Unmap the page of `vaddr`.
#[export_name = "hal_pt_unmap"]
fn unmap(&mut self, vaddr: VirtAddr) -> Result<()> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
pt.unmap(page).unwrap().1.flush();
trace!("unmap: {:x?}", vaddr);
Ok(())
}
/// Change the `flags` of the page of `vaddr`.
#[export_name = "hal_pt_protect"]
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<()> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
pt.update_flags(page, flags.to_ptf()).unwrap().flush();
trace!("protect: {:x?}, flags={:?}", vaddr, flags);
Ok(())
}
/// Query the physical address which the page of `vaddr` maps to.
#[export_name = "hal_pt_query"]
fn query(&mut self, vaddr: VirtAddr) -> Result<riscv::addr::PhysAddr> {
let mut pt = self.get();
let page = Page::of_addr(riscv::addr::VirtAddr::new(vaddr));
let res = pt.ref_entry(page);
trace!("query: {:x?} => {:x?}", vaddr, res);
match res {
Ok(entry) => Ok(entry.addr()),
Err(_) => Err(()),
}
}
/// Get the physical address of root page table.
#[export_name = "hal_pt_table_phys"]
fn table_phys(&self) -> PhysAddr {
self.root_paddr
}
}
pub unsafe fn set_page_table(vmtoken: usize) {
#[cfg(target_arch = "riscv32")]
let mode = satp::Mode::Sv32;
#[cfg(target_arch = "riscv64")]
let mode = satp::Mode::Sv39;
satp::set(mode, 0, vmtoken >> 12);
}
trait FlagsExt {
fn to_ptf(self) -> PTF;
}
impl FlagsExt for MMUFlags {
fn to_ptf(self) -> PTF {
let mut flags = PTF::VALID;
if self.contains(MMUFlags::READ) {
flags |= PTF::READABLE;
}
if self.contains(MMUFlags::WRITE) {
flags |= PTF::WRITABLE;
}
if self.contains(MMUFlags::EXECUTE) {
flags |= PTF::EXECUTABLE;
}
if self.contains(MMUFlags::USER) {
flags |= PTF::USER;
}
flags
}
}
struct FrameAllocatorImpl;
impl FrameAllocator for FrameAllocatorImpl {
fn alloc(&mut self) -> Option<riscv::addr::Frame> {
Frame::alloc().map(|f| {
let paddr = riscv::addr::PhysAddr::new(f.paddr);
riscv::addr::Frame::of_addr(paddr)
})
}
}
impl FrameDeallocator for FrameAllocatorImpl {
fn dealloc(&mut self, frame: riscv::addr::Frame) {
Frame {
paddr: frame.start_address().as_usize(),
}
.dealloc()
}
}
pub fn init() {}

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

View File

@ -1,222 +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(linkage)]
#![deny(warnings)]
#[macro_use]
extern crate log;
extern crate alloc;
#[macro_use]
extern crate lazy_static;
use alloc::boxed::Box;
use core::time::Duration;
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use kernel_hal::defs::*;
use kernel_hal::vdso::*;
use kernel_hal::UserContext;
use naive_timer::Timer;
use spin::Mutex;
pub mod arch;
pub use self::arch::*;
#[allow(improper_ctypes)]
extern "C" {
fn hal_pt_map_kernel(pt: *mut u8, current: *const u8);
fn hal_frame_alloc() -> Option<PhysAddr>;
fn hal_frame_dealloc(paddr: &PhysAddr);
#[link_name = "hal_pmem_base"]
static PMEM_BASE: usize;
}
#[repr(C)]
pub struct Thread {
thread: usize,
}
impl Thread {
#[export_name = "hal_thread_spawn"]
pub fn spawn(
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
vmtoken: usize,
) -> Self {
struct PageTableSwitchWrapper {
inner: Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>,
vmtoken: usize,
}
impl Future for PageTableSwitchWrapper {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe {
arch::set_page_table(self.vmtoken);
}
self.inner.lock().as_mut().poll(cx)
}
}
executor::spawn(PageTableSwitchWrapper {
inner: Mutex::new(future),
vmtoken,
});
Thread { thread: 0 }
}
#[export_name = "hal_thread_set_tid"]
pub fn set_tid(_tid: u64, _pid: u64) {}
#[export_name = "hal_thread_get_tid"]
pub fn get_tid() -> (u64, u64) {
(0, 0)
}
}
#[export_name = "hal_context_run"]
pub fn context_run(context: &mut UserContext) {
context.run();
}
/// Map kernel for the new page table.
///
/// `pt` is a page-aligned pointer to the root page table.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn map_kernel(pt: *mut u8, current: *const u8) {
unsafe {
hal_pt_map_kernel(pt, current);
}
}
#[repr(C)]
pub struct Frame {
paddr: PhysAddr,
}
impl Frame {
pub fn alloc() -> Option<Self> {
unsafe { hal_frame_alloc().map(|paddr| Frame { paddr }) }
}
pub fn dealloc(&mut self) {
unsafe {
hal_frame_dealloc(&self.paddr);
}
}
#[export_name = "hal_zero_frame_paddr"]
pub fn zero_frame_addr() -> PhysAddr {
#[repr(align(0x1000))]
struct Page([u8; PAGE_SIZE]);
static ZERO_PAGE: Page = Page([0u8; PAGE_SIZE]);
unsafe { ZERO_PAGE.0.as_ptr() as usize - PMEM_BASE }
}
}
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
unsafe { PMEM_BASE + paddr }
}
/// Read physical memory from `paddr` to `buf`.
#[export_name = "hal_pmem_read"]
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
trace!("pmem_read: addr={:#x}, len={:#x}", paddr, buf.len());
unsafe {
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
}
}
/// Write physical memory to `paddr` from `buf`.
#[export_name = "hal_pmem_write"]
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
trace!("pmem_write: addr={:#x}, len={:#x}", paddr, buf.len());
unsafe {
buf.as_ptr()
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
}
}
/// Zero physical memory at `[paddr, paddr + len)`
#[export_name = "hal_pmem_zero"]
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
unsafe {
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
}
}
/// Copy content of `src` frame to `target` frame
#[export_name = "hal_frame_copy"]
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
trace!("frame_copy: {:#x} <- {:#x}", target, src);
unsafe {
let buf = phys_to_virt(src) as *const u8;
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
}
}
lazy_static! {
pub static ref NAIVE_TIMER: Mutex<Timer> = Mutex::new(Timer::default());
}
#[export_name = "hal_timer_set"]
pub fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>) {
NAIVE_TIMER.lock().add(deadline, callback);
}
#[export_name = "hal_timer_tick"]
pub fn timer_tick() {
let now = arch::timer_now();
NAIVE_TIMER.lock().expire(now);
}
/// Initialize the HAL.
pub fn init(config: Config) {
unsafe {
trapframe::init();
}
arch::init(config);
}
#[cfg(test)]
mod tests {
use super::*;
#[no_mangle]
extern "C" fn hal_pt_map_kernel(_pt: *mut u8, _current: *const u8) {
unimplemented!()
}
#[no_mangle]
extern "C" fn hal_frame_alloc() -> Option<PhysAddr> {
unimplemented!()
}
#[no_mangle]
extern "C" fn hal_frame_dealloc(_paddr: &PhysAddr) {
unimplemented!()
}
#[export_name = "hal_pmem_base"]
static PMEM_BASE: usize = 0;
}

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.7.0"

View File

@ -1,445 +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
}
/// 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,14 +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
[features]
default = ["libos"]
smp = []
libos = [
"nix",
"tempfile",
"async-std",
"bitmap-allocator",
"zcore-drivers/mock",
]
graphic = ["zcore-drivers/graphic"]
loopback = []
[dependencies]
bitflags = "1.2"
trapframe = "0.7.0"
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"
acpi = "1.1"
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(())
}

View File

@ -0,0 +1,35 @@
//! Interrupts management.
use riscv::{asm, register::sstatus};
hal_fn_impl! {
impl mod crate::hal_fn::interrupt {
fn wait_for_interrupt() {
let enable = sstatus::read().sie();
if !enable {
unsafe { sstatus::set_sie() };
}
unsafe { asm::wfi(); }
if !enable {
unsafe { sstatus::clear_sie() };
}
}
fn handle_irq(cause: usize) {
trace!("Handle irq cause: {}", cause);
crate::drivers::all_irq().first_unwrap().handle_irq(cause)
}
fn intr_on() {
unsafe { sstatus::set_sie() };
}
fn intr_off() {
unsafe { sstatus::clear_sie() };
}
fn intr_get() -> bool {
sstatus::read().sie()
}
}
}

View File

@ -0,0 +1,49 @@
use alloc::vec::Vec;
use core::ops::Range;
use crate::addr::{align_down, align_up};
use crate::utils::init_once::InitOnce;
use crate::{PhysAddr, KCONFIG, PAGE_SIZE};
pub fn free_pmem_regions() -> Vec<Range<PhysAddr>> {
extern "C" {
fn end();
}
static FREE_PMEM_REGIONS: InitOnce<Vec<Range<PhysAddr>>> = InitOnce::new();
FREE_PMEM_REGIONS.init_once(|| {
let initrd = super::INITRD_REGION.as_ref();
let min_start = align_up(end as usize + PAGE_SIZE) - KCONFIG.phys_to_virt_offset;
let mut regions = Vec::new();
for r in super::MEMORY_REGIONS.iter() {
let (start, end) = (align_up(r.start.max(min_start)), align_down(r.end));
if start >= end {
continue;
}
if let Some(initrd) = initrd {
// no overlap at all
if initrd.end <= start || end <= initrd.start {
regions.push(start..end);
continue;
}
// no overlap on the left
if start < initrd.start {
regions.push(start..align_down(initrd.start));
}
// no overlap on the right
if initrd.end < end {
regions.push(align_up(initrd.end)..end);
}
} else {
regions.push(start..end);
}
}
regions
});
FREE_PMEM_REGIONS.clone()
}
pub fn frame_flush(_target: crate::PhysAddr) {
unimplemented!()
}

View File

@ -0,0 +1,74 @@
mod drivers;
mod trap;
pub mod config;
pub mod cpu;
pub mod interrupt;
pub mod mem;
pub mod sbi;
pub mod timer;
pub mod vm;
use alloc::{string::String, vec::Vec};
use core::ops::Range;
use zcore_drivers::utils::devicetree::Devicetree;
use crate::{mem::phys_to_virt, utils::init_once::InitOnce, PhysAddr};
static CMDLINE: InitOnce<String> = InitOnce::new_with_default(String::new());
static INITRD_REGION: InitOnce<Option<Range<PhysAddr>>> = InitOnce::new_with_default(None);
static MEMORY_REGIONS: InitOnce<Vec<Range<PhysAddr>>> = InitOnce::new_with_default(Vec::new());
pub const fn timer_interrupt_vector() -> usize {
trap::SUPERVISOR_TIMER_INT_VEC
}
pub fn cmdline() -> String {
CMDLINE.clone()
}
pub fn init_ram_disk() -> Option<&'static mut [u8]> {
INITRD_REGION.as_ref().map(|range| unsafe {
core::slice::from_raw_parts_mut(phys_to_virt(range.start) as *mut u8, range.len())
})
}
pub fn primary_init_early() {
let dt = Devicetree::from(phys_to_virt(crate::KCONFIG.dtb_paddr)).unwrap();
if let Some(cmdline) = dt.bootargs() {
info!("Load kernel cmdline from DTB: {:?}", cmdline);
CMDLINE.init_once_by(cmdline.into());
}
if let Some(time_freq) = dt.timebase_frequency() {
info!("Load CPU clock frequency from DTB: {} Hz", time_freq);
super::cpu::CPU_FREQ_MHZ.init_once_by((time_freq / 1_000_000) as u16);
}
if let Some(initrd_region) = dt.initrd_region() {
info!("Load initrd regions from DTB: {:#x?}", initrd_region);
INITRD_REGION.init_once_by(Some(initrd_region));
}
if let Ok(regions) = dt.memory_regions() {
info!("Load memory regions from DTB: {:#x?}", regions);
MEMORY_REGIONS.init_once_by(regions);
}
}
pub fn primary_init() {
vm::init();
drivers::init().unwrap();
// We should set first time interrupt before run into first user program
// timer::init();
}
pub fn timer_init() {
timer::init();
}
pub fn secondary_init() {
vm::init();
drivers::intc_init().unwrap();
let plic = crate::drivers::all_irq()
.find("riscv-plic")
.expect("IRQ device 'riscv-plic' not initialized!");
plic.init_hart();
}

View File

@ -0,0 +1,106 @@
#![allow(dead_code)]
// Legacy Extensions (EIDs 0x00 - 0x0F)
const SBI_SET_TIMER: usize = 0;
const SBI_CONSOLE_PUTCHAR: usize = 1;
const SBI_CONSOLE_GETCHAR: usize = 2;
const SBI_CLEAR_IPI: usize = 3;
const SBI_SEND_IPI: usize = 4;
const SBI_REMOTE_FENCE_I: usize = 5;
const SBI_REMOTE_SFENCE_VMA: usize = 6;
const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
const SBI_SHUTDOWN: usize = 8;
// Hart State Management Extension
const HSM_EID: usize = 0x48534D;
const SBI_HART_START_FID: usize = 0; // SBI Verson=0.2
const SBI_HART_STOP_FID: usize = 1; // SBI Verson=0.2
const SBI_HART_GET_STATUS_FID: usize = 2; // SBI Verson=0.2
const SBI_HART_SUSPEND_FID: usize = 3; // SBI Verson=0.3
// SBI Error Code
pub const SBI_SUCCESS: usize = 0;
pub const SBI_ERR_FAILED: usize = usize::MAX; // -1
pub const SBI_ERR_NOT_SUPPORTED: usize = usize::MAX - 1; // -2
pub const SBI_ERR_INVALID_PARAM: usize = usize::MAX - 2; // -3
pub const SBI_ERR_DENIED: usize = usize::MAX - 3; // -4
pub const SBI_ERR_INVALID_ADDRESS: usize = usize::MAX - 4; // -5
pub const SBI_ERR_ALREADY_AVAILABLE: usize = usize::MAX - 5; // -6
pub const SBI_ERR_ALREADY_STARTED: usize = usize::MAX - 6; // -7
pub const SBI_ERR_ALREADY_STOPPED: usize = usize::MAX - 7; // -8
#[inline(always)]
fn sbi_call(eid: usize, fid: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let ret;
unsafe {
core::arch::asm!("ecall",
in("a0") arg0,
in("a1") arg1,
in("a2") arg2,
in("a6") fid,
in("a7") eid,
lateout("a0") ret,
);
}
ret
}
pub fn console_putchar(ch: usize) -> usize {
sbi_call(SBI_CONSOLE_PUTCHAR, 0, ch, 0, 0)
}
pub fn console_getchar() -> usize {
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0, 0)
}
pub fn set_timer(stime_value: u64) -> usize {
#[cfg(target_pointer_width = "32")]
return sbi_call(
SBI_SET_TIMER,
0,
stime_value as usize,
(stime_value >> 32),
0,
);
#[cfg(target_pointer_width = "64")]
sbi_call(SBI_SET_TIMER, 0, stime_value as usize, 0, 0)
}
pub fn clear_ipi() -> usize {
sbi_call(SBI_CLEAR_IPI, 0, 0, 0, 0)
}
pub fn send_ipi(sipi_value: usize) -> usize {
sbi_call(SBI_SEND_IPI, 0, sipi_value, 0, 0)
}
pub fn shutdown() -> ! {
sbi_call(SBI_SHUTDOWN, 0, 0, 0, 0);
unreachable!();
}
/// executing the target hart in supervisor-mode at address
/// specified by start_addr parameter
///
/// The opaque parameter is a XLEN-bit value which will be
/// set in the a1 register when the hart starts executing
/// at start_addr.
pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> usize {
sbi_call(HSM_EID, SBI_HART_START_FID, hartid, start_addr, opaque)
}
/// stop executing the calling hart in supervisor-mode and return
/// its ownership to the SBI implementation.
pub fn hart_stop() -> usize {
sbi_call(HSM_EID, SBI_HART_STOP_FID, 0, 0, 0)
}
hal_fn_impl! {
impl mod crate::hal_fn::console {
fn console_write_early(s: &str) {
for c in s.bytes() {
console_putchar(c as usize);
}
}
}
}

View File

@ -0,0 +1,19 @@
use core::time::Duration;
fn get_cycle() -> u64 {
riscv::register::time::read() as u64
}
pub(super) fn timer_set_next() {
let cycles =
super::cpu::cpu_frequency() as u64 * 1_000_000 / super::super::timer::TICKS_PER_SEC;
super::sbi::set_timer(get_cycle() + cycles);
}
pub(super) fn init() {
timer_set_next();
}
pub(crate) fn timer_now() -> Duration {
Duration::from_nanos(get_cycle() * 1000 / super::cpu::cpu_frequency() as u64)
}

View File

@ -0,0 +1,46 @@
use riscv::register::scause;
use trapframe::TrapFrame;
use crate::context::TrapReason;
pub(super) const SUPERVISOR_TIMER_INT_VEC: usize = 5; // scause::Interrupt::SupervisorTimer
fn breakpoint(sepc: &mut usize) {
info!("Exception::Breakpoint: A breakpoint set @0x{:x} ", sepc);
//sepc为触发中断指令ebreak的地址
//防止无限循环中断让sret返回时跳转到sepc的下一条指令地址
*sepc += 2
}
pub(super) fn super_timer() {
super::timer::timer_set_next();
crate::timer::timer_tick();
//发生外界中断时epc的指令还没有执行故无需修改epc到下一条
}
pub(super) fn super_soft() {
super::sbi::clear_ipi();
info!("Interrupt::SupervisorSoft!");
}
#[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
let scause = scause::read();
trace!("kernel trap happened: {:?}", TrapReason::from(scause));
trace!(
"sepc = 0x{:x} pgtoken = 0x{:x}",
tf.sepc,
crate::vm::current_vmtoken()
);
match TrapReason::from(scause) {
TrapReason::SoftwareBreakpoint => breakpoint(&mut tf.sepc),
TrapReason::PageFault(vaddr, flags) => crate::KHANDLER.handle_page_fault(vaddr, flags),
TrapReason::Interrupt(vector) => {
crate::interrupt::handle_irq(vector);
if vector == SUPERVISOR_TIMER_INT_VEC {
executor::handle_timeout();
}
}
other => panic!("Undefined trap: {:x?} {:#x?}", other, tf),
}
}

View File

@ -0,0 +1,244 @@
//! Virtual memory operations.
use core::fmt::{Debug, Formatter, Result};
use core::slice;
use lock::Mutex;
use riscv::{asm, register::satp};
use crate::utils::page_table::{GenericPTE, PageTableImpl, PageTableLevel3};
use crate::{mem::phys_to_virt, MMUFlags, PhysAddr, VirtAddr, KCONFIG};
lazy_static! {
static ref KERNEL_PT: Mutex<PageTable> = Mutex::new(init_kernel_page_table().unwrap());
}
/// remap kernel ELF segments with 4K page
fn init_kernel_page_table() -> PagingResult<PageTable> {
extern "C" {
fn stext();
fn etext();
fn srodata();
fn erodata();
fn sdata();
fn edata();
fn sbss();
fn ebss();
fn bootstack();
fn bootstacktop();
}
let mut pt = PageTable::new();
let mut map_range = |start: VirtAddr, end: VirtAddr, flags: MMUFlags| -> PagingResult {
pt.map_cont(
crate::addr::align_down(start),
crate::addr::align_up(end - start),
start - KCONFIG.phys_to_virt_offset,
flags | MMUFlags::HUGE_PAGE,
)
};
map_range(
stext as usize,
etext as usize,
MMUFlags::READ | MMUFlags::EXECUTE,
)?;
map_range(srodata as usize, erodata as usize, MMUFlags::READ)?;
map_range(
sdata as usize,
edata as usize,
MMUFlags::READ | MMUFlags::WRITE,
)?;
map_range(
sbss as usize,
ebss as usize,
MMUFlags::READ | MMUFlags::WRITE,
)?;
// stack
map_range(
bootstack as usize,
bootstacktop as usize,
MMUFlags::READ | MMUFlags::WRITE,
)?;
// initrd
if let Some(initrd) = super::INITRD_REGION.as_ref() {
map_range(
phys_to_virt(initrd.start),
phys_to_virt(initrd.end),
MMUFlags::READ | MMUFlags::WRITE,
)?;
}
// physical frames
for r in crate::mem::free_pmem_regions() {
map_range(
phys_to_virt(r.start),
phys_to_virt(r.end),
MMUFlags::READ | MMUFlags::WRITE,
)?;
}
info!("initialized kernel page table @ {:#x}", pt.table_phys());
Ok(pt)
}
pub(super) fn kernel_page_table() -> &'static Mutex<PageTable> {
&KERNEL_PT
}
pub(super) fn init() {
unsafe { KERNEL_PT.lock().activate() };
}
hal_fn_impl! {
impl mod crate::hal_fn::vm {
fn activate_paging(vmtoken: PhysAddr) {
let old_token = current_vmtoken();
if old_token != vmtoken {
#[cfg(target_arch = "riscv64")]
let mode = satp::Mode::Sv39;
debug!("switch table {:x?} -> {:x?}", old_token, vmtoken);
unsafe {
satp::set(mode, 0, vmtoken >> 12);
asm::sfence_vma_all();
}
}
}
fn current_vmtoken() -> PhysAddr {
satp::read().ppn() << 12
}
fn flush_tlb(vaddr: Option<VirtAddr>) {
unsafe {
if let Some(vaddr) = vaddr {
asm::sfence_vma(0, vaddr)
} else {
asm::sfence_vma_all();
}
}
}
fn pt_clone_kernel_space(dst_pt_root: PhysAddr, src_pt_root: PhysAddr) {
let entry_range = 0x100..0x200; // 0xFFFF_FFC0_0000_0000 .. 0xFFFF_FFFF_FFFF_FFFF
let dst_table = unsafe { slice::from_raw_parts_mut(phys_to_virt(dst_pt_root) as *mut Rv64PTE, 512) };
let src_table = unsafe { slice::from_raw_parts(phys_to_virt(src_pt_root) as *const Rv64PTE, 512) };
for i in entry_range {
dst_table[i] = src_table[i];
if !dst_table[i].is_unused() {
dst_table[i].0 |= PTF::GLOBAL.bits() as u64;
}
}
}
}
}
bitflags::bitflags! {
/// Possible flags for a page table entry.
struct PTF: usize {
const VALID = 1 << 0;
const READABLE = 1 << 1;
const WRITABLE = 1 << 2;
const EXECUTABLE = 1 << 3;
const USER = 1 << 4;
const GLOBAL = 1 << 5;
const ACCESSED = 1 << 6;
const DIRTY = 1 << 7;
const RESERVED1 = 1 << 8;
const RESERVED2 = 1 << 9;
}
}
impl From<MMUFlags> for PTF {
fn from(f: MMUFlags) -> Self {
if f.is_empty() {
return PTF::empty();
}
let mut flags = PTF::VALID;
if f.contains(MMUFlags::READ) {
flags |= PTF::READABLE;
}
if f.contains(MMUFlags::WRITE) {
flags |= PTF::READABLE | PTF::WRITABLE;
}
if f.contains(MMUFlags::EXECUTE) {
flags |= PTF::EXECUTABLE;
}
if f.contains(MMUFlags::USER) {
flags |= PTF::USER;
}
flags
}
}
impl From<PTF> for MMUFlags {
fn from(f: PTF) -> Self {
let mut ret = Self::empty();
if f.contains(PTF::READABLE) {
ret |= Self::READ;
}
if f.contains(PTF::WRITABLE) {
ret |= Self::WRITE;
}
if f.contains(PTF::EXECUTABLE) {
ret |= Self::EXECUTE;
}
if f.contains(PTF::USER) {
ret |= Self::USER;
}
ret
}
}
const PHYS_ADDR_MASK: u64 = 0x003f_ffff_ffff_fc00; // 10..54
/// Sv39 and Sv48 page table entry.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Rv64PTE(u64);
impl GenericPTE for Rv64PTE {
fn addr(&self) -> PhysAddr {
((self.0 & PHYS_ADDR_MASK) << 2) as _
}
fn flags(&self) -> MMUFlags {
PTF::from_bits_truncate(self.0 as usize).into()
}
fn is_unused(&self) -> bool {
self.0 == 0
}
fn is_present(&self) -> bool {
PTF::from_bits_truncate(self.0 as usize).contains(PTF::VALID)
}
fn is_leaf(&self) -> bool {
PTF::from_bits_truncate(self.0 as usize).intersects(PTF::READABLE | PTF::EXECUTABLE)
}
fn set_addr(&mut self, paddr: PhysAddr) {
self.0 = (self.0 & !PHYS_ADDR_MASK) | ((paddr as u64 >> 2) & PHYS_ADDR_MASK);
}
fn set_flags(&mut self, flags: MMUFlags, _is_huge: bool) {
let flags = PTF::from(flags) | PTF::ACCESSED | PTF::DIRTY;
debug_assert!(flags.intersects(PTF::READABLE | PTF::EXECUTABLE));
self.0 = (self.0 & PHYS_ADDR_MASK) | flags.bits() as u64;
}
fn set_table(&mut self, paddr: PhysAddr) {
self.0 = ((paddr as u64 >> 2) & PHYS_ADDR_MASK) | PTF::VALID.bits() as u64;
}
fn clear(&mut self) {
self.0 = 0
}
}
impl Debug for Rv64PTE {
fn fmt(&self, f: &mut Formatter) -> Result {
let mut f = f.debug_struct("Rv64PTE");
f.field("raw", &self.0);
f.field("addr", &self.addr());
f.field("flags", &self.flags());
f.finish()
}
}
/// Sv39: Page-Based 39-bit Virtual-Memory System.
pub type PageTable = PageTableImpl<PageTableLevel3, Rv64PTE>;

View File

@ -0,0 +1,23 @@
//! Kernel configuration.
use uefi::proto::console::gop::ModeInfo;
use uefi::table::boot::MemoryDescriptor;
/// Kernel configuration passed by kernel when calls [`crate::primary_init_early()`].
#[derive(Debug)]
pub struct KernelConfig {
pub cmdline: &'static str,
pub initrd_start: u64,
pub initrd_size: u64,
pub memory_map: &'static [&'static MemoryDescriptor],
pub phys_to_virt_offset: usize,
pub fb_mode: ModeInfo,
pub fb_addr: u64,
pub fb_size: u64,
pub acpi_rsdp: u64,
pub smbios: u64,
pub ap_fn: fn() -> !,
}

View File

@ -0,0 +1,35 @@
//! CPU information.
use raw_cpuid::CpuId;
hal_fn_impl! {
impl mod crate::hal_fn::cpu {
fn cpu_id() -> u8 {
CpuId::new()
.get_feature_info()
.unwrap()
.initial_local_apic_id() as u8
}
fn cpu_frequency() -> u16 {
static CPU_FREQ_MHZ: spin::Once<u16> = spin::Once::new();
*CPU_FREQ_MHZ.call_once(|| {
const DEFAULT: u16 = 4000;
CpuId::new()
.get_processor_frequency_info()
.map(|info| info.processor_base_frequency())
.unwrap_or(DEFAULT)
.max(DEFAULT)
})
}
fn reset() -> ! {
info!("shutdown...");
loop {
use zcore_drivers::io::{Io, Pmio};
Pmio::<u16>::new(0x604).write(0x2000);
super::interrupt::wait_for_interrupt();
}
}
}
}

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