Compare commits

1 Commits
Author SHA1 Message Date
admin fffb16cdf5 Initial commit 2026-07-26 20:52:56 +08:00
625 changed files with 1 additions and 242332 deletions
-8
View File
@@ -1,8 +0,0 @@
*.mp4 filter=lfs diff=lfs merge=lfs -text
# Shell scripts are executed by bash (WSL / Linux / CI). CRLF line endings
# break them outright ("$'\r': command not found", and a stray CR in a shebang
# or in `set -euo pipefail` makes the whole script unrunnable) - the packing
# step of this project's build died on exactly that. Pin LF for every checkout
# regardless of the developer's core.autocrlf setting.
*.sh text eol=lf
-40
View File
@@ -1,40 +0,0 @@
---
name: Bug 报告
about: 报告内核崩溃、驱动故障、构建失败等问题
title: "[bug] "
labels: bug
---
## 问题描述
<!-- 简要描述问题 -->
## 复现步骤
1.
2.
3.
## 预期行为
<!-- 应该发生什么 -->
## 实际行为
<!-- 实际发生了什么 -->
## 环境
- 组件:<!-- UTSM / DSK / DKM:<驱动名> / FirstInit / 构建 -->
- 运行环境:<!-- QEMU / 真机 -->
- QEMU 参数(如非默认):
## 串口日志
```
<!-- 粘贴 QEMU 串口输出,至少包含崩溃点前后上下文 -->
```
## 崩溃分析(如适用)
<!-- 三重故障 / page fault / panic 时的寄存器、栈、调用上下文 -->
-39
View File
@@ -1,39 +0,0 @@
---
name: 功能请求
about: 提议新驱动、新子系统或架构改进
title: "[feat] "
labels: enhancement
---
## 功能描述
<!-- 简要描述提议的功能 -->
## 动机
<!-- 为什么需要这个功能,解决什么问题 -->
## 涉及子系统
- [ ] UTSM 首阶段内核
- [ ] DSK 主内核
- [ ] DKM 驱动
- [ ] 内存管理
- [ ] 文件系统 / VFS
- [ ] 网络
- [ ] 调度器 (SAS-R0-PCQ)
- [ ] 恢复系统 (DRR)
- [ ] FirstInit / 用户空间
- [ ] 文档
## 设计思路
<!-- 描述实现方案,如有 ABI / 架构影响请重点说明 -->
## 路线图对应
<!-- 对应 docs/ROADMAP.md 中的哪个 Phase?(如适用) -->
## 验收标准
<!-- 完成后如何验证 -->
-52
View File
@@ -1,52 +0,0 @@
## 变更说明
<!-- 简要描述本 PR 做了什么、为什么 -->
## 变更类型
- [ ] feat — 新功能
- [ ] fix — 修复
- [ ] docs — 文档
- [ ] build — 构建系统
- [ ] refactor — 重构
- [ ] test — 测试
- [ ] chore — 杂项
## 影响范围
<!-- 勾选受影响的子系统 -->
- [ ] UTSM 首阶段内核
- [ ] DSK 主内核
- [ ] DKM 驱动(请列出具体驱动: )
- [ ] DKM ABI / kernel_api
- [ ] 文件系统 / VFS
- [ ] 网络
- [ ] FirstInit / 用户空间
- [ ] 构建 / 镜像打包
- [ ] 文档
## 提交前检查清单
- [ ] `.\build.bat` 构建成功,产出 `ISO/deshab.img`
- [ ] `.\ISO\run_qemu.bat` 启动到 `[DSK] SELFTEST PASS`
- [ ] 无新增编译 warning(`-Wall -Wextra`)
- [ ] ABI 变更已同步所有驱动本地定义(未在结构体中间插入字段)
- [ ] 新驱动已在 `manifest.json` + `limine.conf` 注册
- [ ] 设计文档(`RE/`)与模块 README 已同步
- [ ] 提交信息符合 Conventional Commits
- [ ] `LESSONS_LEARNED.md` 已追加经验记录(如适用)
## 测试方式
<!-- 描述如何验证本变更,如 QEMU 参数、预期串口输出 -->
```text
预期输出:
```
## ABI / 兼容性影响
<!-- 如果修改了 dkm_kernel_api、driver_desc、dsk_boot_context 等 ABI,请说明:
1. 新增字段是否追加到结构尾部
2. 是否影响已有驱动
3. min_kernel_abi / abi_version 是否需要变更 -->
-34
View File
@@ -1,34 +0,0 @@
*.o
*.obj
*.elf
*.drv
*.img
*.iso
*.vhd
*.raw
*.ppm
target/
*.rlib
*.rmeta
*.pdb
*.dbf
*.rgba
IDEA.md
SYSTEM/boot/linux-extra-rootfs.img
*.d
target/
# ---- 本地工作区 ----
.build_tmp/
.trae/
docs/workbuddy-planning/
# IDE 配置(JetBrains 自动生成的 .idea/.gitignore 只覆盖了部分文件)
.idea/*.iml
.idea/inspectionProfiles/
# 本地运行日志(如 run_qemu.bat > deshab_log.log 的串口输出)
*.log
deshab_log.log
-15
View File
@@ -1,15 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Rider 忽略的文件
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.Deshab-OS.iml
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
-223
View File
@@ -1,223 +0,0 @@
# Errors
Command failures and integration errors.
---
## [ERR-20260906-001] linux_compat exec returns -4 after guest park
**Logged**: 2026-09-06T23:10:00+08:00
**Command**: DSK `lxc->exec` / `exec_async` for KDE preflight and `deshab-kde-session`
**Error**: rc=0xfffffffffffffffc (-4 超时无 EXEC_EXIT)
**Context**: Linux guest already EXEC_READY + parked; virtio-gpu and extra-rootfs backends bound; chooser sent key 2. `linux_resume` returned via HLT but `linux_to_utsm` ring had no EXEC_EXIT. Guest serial interleaved (`[utsm-linux] ini[VMEXIT]`), switch_root/vdc lines not recovered. WSL host only 7.6G so QEMU is 6G with a 3.9G extra-rootfs module.
**Next**: confirm daemon recv of EXEC_REQUEST after first park; check initramfs vs switch_root; raise WSL memory before blaming KDE packages.
**Root cause (2026-09-07)**: `UTSM_IOCTL_RECV_MSG` / `SEND_MSG` in `utsm_hcall.c` called `copy_to_user(umsg.data)` / `copy_from_user(..., umsg.data)` after `umsg` was already a kernel stack copy. That is `-EFAULT`. Daemon `recv_msg` fails, parks with no `EXEC_EXIT` → host `-4`. Empty `EXEC_READY` send worked because `data_len==0` skipped the bad copy. Fix: `memcpy` within the kernel struct, then a single `copy_to_user` of the whole ioctl arg.
### Resolution
- **Resolved**: 2026-09-07T06:41:00+08:00
- **Notes**: Rebuilt bzImage + patched ESP. QEMU verify: `KDE preflight rc=0`, `exec_async rc=0`, `KDE spawned`; desktop `guest ping rc=0`.
---
## [ERR-20260907-001] virtio-blk vdb/vdc sector 0 I/O error
**Logged**: 2026-09-07T22:50:00+08:00
**Priority**: high
**Status**: in_progress
**Area**: backend
### Summary
Linux 6.6 能 probe virtio-blk(blk_size 修好后),但 `vdb`/`vdc` 读 sector 0 曾立即 I/O error。2026-09-08 已能 `EXT4-fs (vdb): mounted`,串口无 `I/O error`。
### Error
```
virtio_blk virtio2: [vdb] 1572864 512-byte logical blocks
I/O error, dev vdb, sector 0 op 0x0:(READ)
EXT4-fs (vdb): Could not load journal inode
I/O error, dev vdc, sector 0
```
### Context
e820 已改为实际映射大小后仍失败。host 已 log extra-rootfs bound。`virtio_gpa_to_host` 对请求缓冲返回 NULL、或 chain 解析漏间接描述符,最可疑。
### Suggested Fix
对 `rootfs_blk_handle_chain` 在 IOERR 路径打 GPA/len 日志;确认 desc.addr 落在 `0x05000000` RAM 窗口。
### Progress (2026-09-08)
- e820 `0x00400000-0x03FFFFFF` 标成 60MB RAM,但 EPT 只映射了 kernel/initrd 等几页。virtio-blk READ 缓冲落在空洞且 vCPU 从未 store → `virtio_gpa_to_host` 返回 NULL → IOERR。
- 修复:`virtio_gpa_to_host` 按需映射;启动时填满该窗口空洞(~34MB);`size_max` 改为 65535 **字节**;描述符链支持 INDIRECT + 多段;rootfs 内存可写。
- 验证:vdb mount OK,无 I/O error。Arch `/sbin/init`(systemd) 与 `/bin/sh` 在 glibc 同一偏移 SIGSEGV(`segfault at ffffffff`,确定性,非随机坏块)。PID 1 改为 `utsm_exec_daemon` 后 `EXEC_READY` 恢复。KDE 用户态仍起不来:host exec 的动态链接 sh/startplasma 同样崩;vdc overlay 本轮未见挂上。
### Progress (2026-09-08 later)
- **vdc 已挂上**:init 必须在 `mount --move /dev` 之前 mount vdc。现串口有 `persist moved to new root /mnt/persist` + overlay /opt /home /root。
- WRMSR IA32_FS_BASE 必须写入 `VMCS_GUEST_FS_BASE`(忽略写会留下 TLS=0)。
- musl busybox(无 SHSTK note)在 `push` 上 `segfault at 0`;改回 Arch `/bin/sh`→bash 后变成 glibc `#GP`,IP 正好是 `endbr64`(libc RX 映射 +0x175140)。
- 在 host 上 `wrmsr IA32_U_CET=0` 或临时打开 `CR4.CET` 会让 UTSM 在 Linux park 前就死掉,不要再试。
- 当前 blocker:动态链接 glibc 的 IBT landing pad(`endbr64`)在 nested VMX 下 #GP。Plasma 仍起不来。
### Progress (2026-09-10)
- 盘上 libc `0x199140` 已是 `90 90 90 90`;guest 仍在同一 RIP #GP。
- 用户态 #GP **会** VM-exit 到 L1;`guest_fetch` 读到全 0,ENDBR skip 从未命中。
- `ibt=off` 内核认;`user_shstk=off` 在 Linux 6.6 未知。
- nested KVM **不提供** VMCS CET save/load。
- `/bin/sh`→busybox.static 后:`segfault at 0` error 6(PUSH/SSP=0)。
- 不要用 `0F 1F 40 00` 替换 ENDBR。
### Progress (2026-09-10 afternoon)
- 限制 RIP 的盲 skip:glibc `0x7f…140` 连续 skip 8×4 字节后 `segfault at 7f…d000`;busybox.static `push r13` 仍写地址 0(RSP 正常,是 SHSTK/SSP=0)。
- L1 `CR4=0x20a0`(无 CET);L1 CPUID 也无 CET_SS/IBT。不能在 L1 `WRMSR` CET。
- VM-entry MSR-load `IA32_U_CET=0`:VMLAUNCH 指令成功,随后 **exit reason 34**(MSR loading failure),Linux 未 park。已停用该 list。
- QEMU 进程 `arch_prctl(ARCH_SHSTK_DISABLE)`:WSL 返回 **EINVAL**,`/proc/cpuinfo` 无 shstk/ibt。LD_PRELOAD 无效。
- Guest `rdmsrl_safe`/`wrmsrl_safe(IA32_U_CET)`:**rd=wr=-EIO**(#GP)。L2 也碰不到 CET MSR,但硬件 IBT/SHSTK 仍作用于用户态。
- CET 在 L0 生效且对 L1/L2 都不可编程。在禁止 host `wrmsr` 的前提下 Plasma 起不来。
### Progress (2026-09-10 evening)
- 去掉 #PF 拦截后 park + `desktop ready` 恢复。
- `KVM_SET_MSRS` CET 返回 **r=0**(0 个 MSR 写成功),KVM 不虚拟化 CET。
- `patch_boot_on_img.sh` 把 `/bin/sh` 改成 busybox.static 会让所有脚本在 `0x4bede0` PUSH 写地址 0 上死掉。已改回 `/bin/sh -> bash`。
- bash/glibc 用户态 #GP:RIP `0x7f…140`,`ilen=2` 或 `3`(不是 ENDBR 的 4),页表走到 hole 零页(`mem=0`)。盲 skip 4 会到 `…144` 继续 #GP。
- `CR3_LOAD_EXITING` 能拦住 `MOV CR3`(见 `0x514c000` 等高位 PGD),但故障时 tracked CR3=`0x2fa2000` 仍把 libc GVA 走到零页。
### Progress (2026-09-10 EPT misconfig)
- persist 能挂上 `/dev/vdc`;`desktop ready` 后 `EPT misconfig gpa=0x3e20000` 把 vCPU 停掉。
- guest `clear_page_erms` #GP,`RDI=0x0005088000005000`(非规范),随后 `ext4lazyinit` 在已 tainted 内核里 oops。
- 根因更像 EPT leaf 未 Ignore PAT + misconfig 杀 vCPU,而不是这一次的 `EXT4-fs error`。
### Metadata
- Reproducible: yes (glibc SIGSEGV)
- Related Files: CODE/UTSM/vmm/virtio_blk.c, CODE/UTSM/vmm/virtio_mmio.c, CODE/UTSM/vmm/linux_loader.c, CODE/linux/initramfs/init
---
## [ERR-20260910-001] verify kde_ok=2 after persist check stall
**Logged**: 2026-09-10T20:22:00+08:00
**Command**: `patch_boot_on_img.sh` + `tmp_reverify2.sh` (landing-pad rebuild)
**Error**: exit 1; `guest_ok=1 session_ok=1 kde_ok=2 sent_kde=1 desktop_ready=1`; last session kmsg `checking persist mount`; no `persist check done` / `entering KDE`
**Context**: WSL NAT warning on stdout; QEMU 480s; serial 2 spawned `deshab-kde-session`. Host KDE loop still `exec sleep`/`test -f` every frame. drm `KDE scanout w=1024` is fbdev, not Plasma.
**Next**: `run_slice` pump + persist remount kmsg; rebuild UTSM/desktop/DSK; inject session via `patch_boot_on_img.sh` (do not loop-mount extra-rootfs from drvfs).
### Follow-up (2026-09-11)
`run_slice` 打进镜像后仍 `kde_ok=2`:`checking persist mount` 后无 `vdc present`/`persist check done`。同步 `test -f` 仍 `EXEC_EXIT -4`。async 时 daemon `sched_yield` 空转占满 vCPU;init `mount --move` 失败后未 umount,二次 mount 因 vdc busy 失败。
## [ERR-20260911-001] verify6 froze at Limine, no SUMMARY
**Logged**: 2026-09-11T21:35:00+08:00
**Command**: `tmp_run_verify6.sh` (initrd+patch+qemu_verify_loop)
**Error**: serial log 20KB Limine cursor text only; no UTSM/DSK markers. Wrapper `exec >file` hid later output. QEMU gone on resume.
**Next**: re-run verify only with `stdbuf -oL` + `timeout --kill-after=15 540`.
## [ERR-20260911-002] Cursor-attached wsl.exe kills the VM in ~45s
**Logged**: 2026-09-11T22:02:00+08:00
**Command**: long-running `wsl -d Ubuntu` from Cursor Shell (even `sleep 620`)
**Error**: wsl.exe exits 1 at ~45s; dmesg `systemd-shutdow` + journal "uncleanly shut down"; QEMU dies while Limine is still `Loading module linux-extra-rootfs.img`. Serial stays ~20KB cursor text. `STATUS_STACK_BUFFER_OVERRUN` / UTF-16 NAT warning on the Windows pipe.
**Next**: run `tmp_run_verify9.sh` from Windows Terminal, not Cursor. Image already patched (initrd+daemon+session+desktop).
### Follow-up (2026-09-11 22:07)
User ran it in Windows Terminal: no tty output (script `>>file`), QEMU gone at ~1 min, serial still 20KB Limine extra-rootfs, no `timed_exit`. Same kill during 3.9G module load from drvfs, not only Cursor. Next: `tmp_run_visible.sh` (tee + heartbeat + copy image to `/home/deshab`).
## [ERR-20260911-003] persist remount failed, no startplasma
**Logged**: 2026-09-11T23:04:00+08:00
**Command**: `tmp_run_visible.sh` (native `/home/deshab/deshab-dev.img`)
**Error**: `kde_ok=2`. Session: `persist remount failed`, `ERROR: no startplasma at /mnt/persist`. Init: overlays on persist then `mount --move` fail; umount+remount also fail because overlay upperdirs keep vdc busy.
**Next**: bind-mount persist into the new root; session bind from existing `/dev/vdc` mount if remount EBUSY.
## [ERR-20260912-001] verify dies at log=21013 in Windows Terminal
**Logged**: 2026-09-12T12:40:00+08:00
**Priority**: critical
**Status**: in_progress
**Area**: infra
### Summary
`tmp_run_visible.sh` 在 WT 里 t=26s、`log=21013` 整段退出,没有 SUMMARY。Limine 仍在 Loading linux-extra-rootfs.img。
### Error
```
[verify] t=26s log=21013 guest=0 session=0 desktop=0 kde=0
PS C:\Windows\system32>
```
### Context
Native `/home/deshab/deshab-dev.img` 已完整。第一次成功启动曾在 21013 停约 80s。之后多次 26–45s 被掐。不是用户提前 Ctrl+C。
### Suggested Fix
停止 Limine 预加载 extra-rootfs;vdc 改为 ESP FAT32+AHCI 映射。
### Follow-up (2026-09-12 13:21)
Limine 已过:DSK 20s、guest+desktop。init 报 `/dev/vdc missing`:`linux_launch` 在 `dsm_load`(AHCI)之前,盘上映射没有块设备。已改为先加载 manifest stage 0–1。
### Metadata
- Reproducible: yes
- Related Files: SYSTEM/limine/limine.conf, CODE/UTSM/vmm/virtio_blk.c
- See Also: ERR-20260911-002
---
## [ERR-20260912-002] entering KDE then bind /dev failed
**Logged**: 2026-09-12T14:10:18+08:00
**Priority**: high
**Status**: in_progress
**Area**: backend
### Summary
Guest parked, persist has startplasma, session reached `entering KDE root`, then `mount --rbind /dev` failed. Verify printed `VERIFY_KDE_PLASMA_OK` because `entering KDE` was treated as Plasma.
### Error
```
[deshab-kde] persist already has startplasma
[deshab-kde] entering KDE root at /mnt/persist
[deshab-kde] ERROR: could not bind /dev into the KDE root
```
### Context
e2fsck of Arch vdb was clean. extra-rootfs `/dev` `/proc` `/sys` `/run` are 0555 empty placeholders. Session stderr is `/dev/null` (async daemon). drm `SET_SCANOUT` at 7.2s is virtio-gpu probe, not Plasma.
### Suggested Fix
chmod 0755 placeholders, `mount --bind` (not rbind), log mount stderr to kmsg, seed /dev/dri on failure. Verify `kde_ok=1` only on `starting Plasma`.
### Follow-up (2026-09-12 14:16)
Real bind error: persist `/usr/bin/mount` + vdb ld.so → `libmount.so.1: Error 74` (EBADMSG). vdb `ext4_lookup` checksum invalid on inodes 1673/1810/1826 (`/usr/bin/mount` is 1826). Use persist `ld.so --library-path` to run persist mount/chroot.
### Follow-up (2026-09-12 14:45)
Static `utsm_bindmount` binds /dev /proc /sys /run /tmp. persist `mount`/`ld.so` are unusable (SHSTK or XSAVE). e2fsck of vdb is clean; checksums are runtime virtio reads (grep=1673 mkdir=1810 mount=1826). chroot persist bash exits 127 (new glibc wants XSAVE/YMM; CPUID hides them). Overlay vdb ld.so+libc → chroot 139 (SIGSEGV). Plasma still not painted.
---
## [ERR-20260912-003] idle=poll still on guest cmdline after header edit
**Logged**: 2026-09-12T19:50:00+08:00
**Priority**: critical
**Status**: in_progress
**Area**: backend
### Summary
PARK/HLT 分流已进树,但 guest 仍带着 `nohlt idle=poll`,Xorg 等待期间 `cpu_idle_poll` 空指针,idle task panic。
### Error
```
Command line: ... no_timer_check nohlt idle=poll noapic nolapic ...
RIP: cpu_idle_poll.isra.0+0x2b
RDX: 0000000000000000
Kernel panic - not syncing: Attempted to kill the idle task!
```
### Context
`linux_loader.o` 未随头文件重编(缺 `.d`)。`SYSTEM/boot/utsm.elf` 19:34 链接仍含旧字符串。验证镜像是 `/home/deshab/deshab-dev.img`。
### Suggested Fix
删除 `vmm/linux_loader.o` 后 make;确认二进制无 `nohlt`/`idle=poll`;同时 patch ISO 与 native GPT。
### Metadata
- Reproducible: yes
- Related Files: CODE/UTSM/vmm/linux_loader.c, CODE/UTSM/include/utsm/linux_loader.h
- See Also: LRN-20260912-003
### Follow-up (2026-09-12 20:33)
删掉 `linux_loader.o` 后 cmdline 已干净,idle panic 消失。vdb `/tmp`/`/run` bind 导致只读,改为 tmpfs 后 seatd socket 出现。persist `/usr/lib/Xorg` ELF 头损坏;换官方包后 Xorg 不再 139,改为 `libXdmcp.so.6: invalid ELF header`。debugfs 确认 `libXdmcp.so.6.0.0` 在 extra-rootfs 上就不是 ELF。
---
-57
View File
@@ -1,57 +0,0 @@
# Feature Requests
Capabilities requested by the user.
---
## [FR-20260906-001] desktop chooser + KDE + Linux guest closed loop
**Logged**: 2026-09-06T22:35:00+08:00
**Status**: resolved
**Area**: dsk / linux-guest / kde
### Request
进入循环:发现 bug、修 bug、实现新功能、测试、策划下一功能。必须验证并实现:
1. 桌面选择界面启用(FUCK `session=ask`)
2. KDE / Plasma Wayland 可启动
3. Linux guest 完整启动(EXEC_READY + park)
### Notes
GPT 双分区后 DSK 不能再从主盘 LBA0 读 FUCK;必须走 Limine `fuck:config` 模块。2026-09-07 验证:`session=ask` 选择界面可用;Linux guest `EXEC_READY`+park;ioctl 修复后 KDE `exec_async` 成功 spawn。Plasma 是否画到 virtio-gpu 尚未有显示确认(无头跑 + native desktop 仍作应急 UI)。
### Resolution
- **Resolved**: 2026-09-07T06:41:00+08:00
- **Notes**: 三项闭环:chooser / guest park / KDE spawn。下一轮看 persist+dri 与 Plasma 实际出画。
---
## [FR-20260907-001] native↔KDE launchers + 2GiB ext4 + KDE scanout
**Logged**: 2026-09-07T22:00:00+08:00
**Priority**: high
**Status**: in_progress
### Progress (2026-09-07)
- GPT 已打:p1 FAT32 ESP ~5.3G + p2 ext4 **2GiB**。`ISO/deshab-dev.img` 已验证;release/realtest 与 dev 同源(`build/configs/` 缺失)。
- 原生桌面:`desktop ready`,图标来自 ESP `user/desktop`(`.lnk` + `.desktop`)。
- virtio-gpu 2D 已出画:`SET_SCANOUT 1024x768`,串口 `KDE scanout w=1024`。
- Plasma 用户态仍起不来:vdb 已能 mount,但 Arch glibc 用户态(systemd/`sh`/startplasma)确定性 SIGSEGV;`startplasma` 不在串口出现。scanout 1024 仍是内核 drm fbdev。
**Area**: dsk / linux-guest / kde / pack
### Requested Capability
构建全部镜像且 ext4 ≥ 2GiB;改到 KDE 真正出画;原生桌面能启动 KDE、KDE 能启动原生程序;两边图标都来自 `user/desktop`。
### User Context
ISO GPT 双分区(p1 FAT32 ESP + p2 ext4),不要用 AHCI SATA sidecar 测 DSK。Plasma Wayland 不发 virtio-gpu 2D SET_SCANOUT。
### Complexity Estimate
complex
### Suggested Implementation
修正 virtio-gpu 命令号对齐 Linux 6.6;X11 Plasma;`deshab-kde-session` / `deshab-native-desktop`;共享 `SYSTEM/user/desktop`。
### Metadata
- Frequency: recurring
- Related Features: FR-20260906-001
---
-530
View File
@@ -1,530 +0,0 @@
# Learnings
Corrections, insights, and knowledge gaps captured during development.
**Categories**: correction | insight | knowledge_gap | best_practice
## [LRN-20260912-003] correction
**Logged**: 2026-09-12T19:50:00+08:00
**Priority**: critical
**Status**: in_progress
**Area**: backend
### Summary
只改 `LINUX_DEFAULT_CMDLINE` 再 `ld.lld` 不够:`linux_loader.o` 若没有对应 `.d`,make 不会重编,旧 `nohlt idle=poll` 会继续打进 guest。
### Details
`linux_loader.h` 19:33 已去掉 `nohlt idle=poll`,`utsm.elf` 19:34 重新链接,但 `linux_loader.o` 仍是 9 月 10 日。串口仍是 `cpu_idle_poll` NULL deref + `Attempted to kill the idle task`。验证跑的是 `/home/deshab/deshab-dev.img`,只在体积变化时才从 ISO 整盘复制,所以必须同时 patch 两份 GPT。
### Suggested Action
改 cmdline 后强制删 `vmm/linux_loader.o` 再 make;`strings utsm.elf` 确认无 `nohlt`/`idle=poll`;`patch_boot_on_img.sh` 打 ISO **和** `/home/deshab/deshab-dev.img`。不要再加 `idle=poll`/`nohlt`。Xorg 诊断不要只写 `/dev/kmsg`(限流会吞 sed 输出),用 `log()` 打 `/dev/console`。vdb 是 ro,不能 bind `/tmp`/`/run` 进 persist(会变成只读,Xorg 建不了 socket、seatd 建不了 sock);这两个目录要挂 tmpfs。persist `/usr/lib/Xorg` 文件大小与官方包相同(2642568)但 ELF 头被砸坏(`e_entry=0`,.text 只有 14KB);官方包入口是 `0x35900`。不要改 extra-rootfs,把完好 Xorg 放 vdb `/usr/local/lib/Xorg.good` 再 bind。Xorg.good 起来后 persist 的 `libXdmcp.so.6.0.0` 不是 ELF。官方 Xorg NEEDED 闭包用 `LD_LIBRARY_PATH=/usr/local/lib/xorg-libs` 覆盖后,X socket 能起来。Plasma 立刻 127 不是 startplasma 本身缺文件:`runuser` 的传递依赖 `libcap-ng.so.0` 头坏了(PATH 若先搜 sbin,会跑到更直接依赖它的 `/usr/sbin/runuser`)。用官方 `libcap-ng.so.0` 放进 xorg-libs 后,runuser 能过,下一步是 Plasma 状态 135(128+SIGBUS),stderr 空,要看 dmesg 才知道是哪段库或 vdc 校验。不要把只有 `starting Plasma`、还没确认没有 `Plasma failed` 的窗口当成 `VERIFY_KDE_PLASMA_OK`。
### Metadata
- Source: error
- Related Files: CODE/UTSM/include/utsm/linux_loader.h, CODE/UTSM/vmm/linux_loader.c, CODE/linux/tmp_run_visible.sh
- Tags: idle, cmdline, kde, xorg
- See Also: LRN-20260912-002
---
## [LRN-20260912-002] insight
**Logged**: 2026-09-12T14:45:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
KDE extra-rootfs glibc 带 XSAVE/YMM/ZMM GNU property;L2 CPUID 隐藏 XSAVE 后 persist bash/Xorg 在 chroot 里立刻 127。不要 `objcopy --remove-section=.note.gnu.property`(会留下零长度 GNU_PROPERTY 头,interpreter 无法加载)。
### Details
vdb e2fsck 干净,运行时 `ext4_lookup` checksum 打在 grep/mkdir/mount。静态 `utsm_bindmount` 能 bind /dev。persist ld.so 直接跑会 SHSTK 写地址 0;chroot persist bash=127;叠 vdb ld.so+libc=139。
### Suggested Action
要么给 L2 做完整 XSAVE 保存/恢复(并放开 CPUID),要么 extra 换无 AVX glibc / Xfbdev 小用户态。不要再拆 GNU property section。
### Resolution
- **Resolved**: 2026-09-12T15:42:00+08:00 (XSAVE half)
- **Notes**: L2 XSAVE+AVX 已落地(XCR0=7,832B,内核 `Enabled xstate features 0x7`)。persist bash ISA needed 只是 x86-64-baseline。直接 exec persist ld.so 仍在 `_dl_find_dso_for_object` 写 -40 崩;vdb ld.so + persist bash 能起来。vdc 必须 `ro,noload` + overlay/tmpfs upper,绝不能 remount vdb/vdc rw。镜像 e2fsck 干净时仍可能 runtime checksum(mkdir/chmod/touch/libreadline)——virtio 读路径问题,不要靠给 extra-rootfs 反复 inject 修。`/usr/bin/Xorg` 是 271 字节 wrapper(会 exec suid Xorg.wrap);真服务器是 `/usr/lib/Xorg`,且会 daemonize,要用 `-keeptty` 并等 socket 而不是等父进程。guest clocksource 被标成 refined-jiffies,nanosleep 依赖 PIT IRQ0。不要再拆 GNU property。
### Metadata
- Source: conversation
- Related Files: CODE/linux/guest/deshab-kde-session, CODE/UTSM/vmm/vmexit.c, CODE/linux/initramfs/bin/utsm_bindmount.c
- Tags: kde, xsave, persist, chroot
---
---
## [LRN-20260906-001] correction
**Logged**: 2026-09-06T18:30:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
VMX TRUE CTLS 低半字是 allowed-0(bit=0 必须为 1),不是 must-be-1 位图;旧 OR-low 公式漏 Virtual NMIs 等 must-be-1 位。
### Details
交接与初版计划把 Intel/KVM 极性说反,并把 cpu bit26、VPID 非零+enable=0 当成 error 7。复核后:SDM A.3 低 32=allowed-0、高 32=allowed-1;`(desired & high) | low` 与 KVM `(desired | low) & high` 都不会补 low=0 的必须-1 位。自测 pin=0x1f 缺 bit 5,同 MSR 下 Intel 公式应得 0x29。VPID 禁用时字段非零不是 SDM 列出的检查。`.build_tmp/kvm_smoke_serial.log` 从未 vmlaunch。
### Suggested Action
嵌套 KVM 实测:`CTLS2 allowed0=0`,`~allowed0` 会打开全部 secondary 位并继续 error 7。正确公式是 KVM 极性 `(desired | allowed0) & allowed1`。继续查 EPTP/MSR bitmap 等一致性。
### Resolution
- **Resolved**: 2026-09-06T18:45:00+08:00
- **Notes**: `~low` 会把 CTLS2 全打开。自测真正过线的是 pin 加上 Virtual NMIs(0x3f)。EPTP 改 UC 会回归 error 7,保持 WB。
### Metadata
- Source: conversation
- Related Files: CODE/UTSM/vmm/vmx.c, CODE/UTSM/vmm/vmm.c, CODE/UTSM/vmm/linux_boot.c
- Tags: vmx, error7, true-ctls
- Pattern-Key: harden.input_validation
---
## [LRN-20260906-002] correction
**Logged**: 2026-09-06T21:35:00+08:00
**Priority**: high
**Status**: promoted
**Promoted**: CLAUDE.md
**Area**: infra
### Summary
发行物必须全部落入 SYSTEM/;打包只扫描 SYSTEM/,不从 /home 或其它路径取镜像。
### Details
Phase 0 曾把 KDE/Python rootfs 留在 WSL `/home/deshab/`,避免塞进 2GiB FAT32 ESP。用户明确要求:所有软件装在 SYSTEM/,打包时不要从其它地方找,直接把整个 SYSTEM 打成一个镜像。FAT32 单文件不能超过 4GiB-1,因此 extra-rootfs 默认改为 3900MB。
### Suggested Action
构建脚本最终输出一律写 SYSTEM/boot;build.ps1 按 SYSTEM 体积自动扩 ESP;Limine 模块路径只引用 SYSTEM/boot 下的文件。
### Metadata
- Source: user_feedback
- Related Files: build.ps1, SYSTEM/limine/limine.conf, CODE/linux/build_extra_rootfs.sh
- Tags: system, pack, fat32
- Pattern-Key: harden.input_validation
---
## [LRN-20260906-003] correction
**Logged**: 2026-09-06T22:20:00+08:00
**Priority**: high
**Status**: promoted
**Promoted**: CLAUDE.md
**Area**: infra
### Summary
磁盘镜像改为 GPT 双分区:FAT32 ESP 只放引导(EFI/limine/boot),其余 SYSTEM/ 进 ext4。
### Details
用户要求把文件系统换成 ext4 + Fat32 引导分区 boot 文件夹。原先 New-GptFat32Image 把整个 SYSTEM/ 打进单一 FAT32 ESP。现由 WSL `pack_system_image.sh` 写 p1 FAT32 + p2 ext4;Limine 用固定 ext4 UUID 加载 /driver 与 /system,boot(): 仅指向 ESP 上的 /boot。FAT32 4GiB 单文件上限仍约束 SYSTEM/boot 里的 extra-rootfs。
### Suggested Action
打包只走 pack_system_image.sh;改 limine 数据路径时同步 EXT4_UUID。DSK 运行时 FAT32 读取仍走独立 SATA 测试盘(整盘 LBA0),不要假设主 virtio 盘 LBA0 是 BPB。
### Metadata
- Source: user_feedback
- Related Files: CODE/linux/pack_system_image.sh, build.ps1, SYSTEM/limine/limine.conf
- Tags: gpt, fat32, ext4, pack
- Pattern-Key: harden.input_validation
---
## [LRN-20260906-004] knowledge_gap
**Logged**: 2026-09-06T22:45:00+08:00
**Priority**: high
**Status**: promoted
**Promoted**: CLAUDE.md
**Area**: boot
### Summary
Limine 10 不能从 ext4 加载模块;`uuid()` 只能指向它能读的 FAT 卷。
### Details
GPT 双分区后把 driver/system 放到 ext4 并用 `uuid(...):/driver/...`,Limine 10.8.5 在加载 utsm.elf 后 PANIC:Failed to open module。boot(): /boot/utsm.elf 成功是因为文件在 FAT32 ESP。
### Suggested Action
ESP 必须包含 Limine 要加载的全部路径:/EFI /limine /boot /driver /system。ext4 只作数据分区。
### Metadata
- Source: qemu_serial_loop1.log
- Related Files: SYSTEM/limine/limine.conf, CODE/linux/pack_system_image.sh
- Tags: limine, ext4, fat32
- Pattern-Key: harden.input_validation
---
## [LRN-20260907-001] insight
**Logged**: 2026-09-07T06:41:00+08:00
**Priority**: high
**Status**: resolved
**Area**: backend
### Summary
`/dev/utsm` ioctl 的 payload 已在 `copy_from_user` 整结构之后进入内核栈,必须 `memcpy`,不能再 `copy_*_user` 结构体内部数组。
### Details
`UTSM_IOCTL_RECV_MSG` 对 `umsg.data`(内核地址)做 `copy_to_user` 得到 `-EFAULT`。daemon 当成空 ring,立刻 park,host `lxc_exec` 返回 `-4`。`EXEC_READY` 能过是因为 `data_len==0`。同一 bug 也让 desktop `guest ping` 误报 “Linux guest not running”。
### Suggested Action
ioctl 先整包 `copy_from_user`,内核内 `memcpy` payload,再整包 `copy_to_user`。改 `CODE/linux/patches/utsm_hcall.c` 后必须拷到 WSL 原生树 `/home/deshab/linux-6.6` 重编 bzImage,不要经 `/mnt/d` 改内核源。
### Metadata
- Source: error
- Related Files: CODE/linux/patches/utsm_hcall.c, CODE/linux/initramfs/bin/utsm_exec_daemon.c, CODE/UTSM/vmm/linux_compat.c
- Tags: ioctl, ipc, linux-guest, kde
- See Also: ERR-20260906-001
---
## [LRN-20260907-002] correction
**Logged**: 2026-09-07T21:20:00+08:00
**Priority**: high
**Status**: pending
**Area**: tooling
### Summary
KDE/DSK 验证必须用 `ISO/deshab-dev.img`。`.build_tmp/sata_fat32_dsk.img` 只是 QEMU 额外挂的 AHCI 测试盘;UTSM 优先从 block0 FAT32 读 `deshab.elf`,挂上它就会把 ISO ESP 上的 DSK 盖掉。
### Details
用户明确纠正:测的是 ISO 下的 dev 镜像,不是 SATA。`run_qemu_kvm.sh` 默认挂 SATA,导致热补 ESP 的 `deshab.elf` 不生效。另外 QEMU 开着时 `pack_system_image.sh` 最后 `cp` 到 `ISO/*.img` 会因文件占用失败,三个镜像时间戳都不动。`build/configs/` 目前不存在,`both` 只会 WARN 后用同一份 SYSTEM 打三份。打包日志里的 FAT32 是 GPT p1 ESP(Limine 不能读 ext4),p2 才是 ext4;`linux-extra-rootfs.img` 必须放在 ESP,所以镜像看起来像“大 FAT32”。SATA sidecar 重建也会打出 FAT32 字样,但那不是 ISO 系统盘。
### Suggested Action
验证脚本设 `SATA_IMG_OVERRIDE` 为不存在的路径。打包前先停 QEMU。不要把 SATA 测试盘当成系统镜像。
### Metadata
- Source: conversation
- Related Files: ISO/run_qemu_kvm.sh, CODE/linux/qemu_verify_loop.sh, CODE/UTSM/kernel/dsk_loader.c, build.ps1
- Tags: qemu, sata, iso, dsk-loader
- Pattern-Key: harden.input_validation
---
## [LRN-20260907-003] insight
**Logged**: 2026-09-07T22:30:00+08:00
**Priority**: high
**Status**: resolved
**Area**: backend
### Summary
ESP ≥ 4GiB 时 pack 使用 32KiB FAT 簇。DSK 仍按 8 扇区读 4KiB 缓冲,desktop.elf 入口是垃圾代码,一跳就复位。
### Details
修好后串口有 `fat32 spc=0x40`、`desktop ready`、`user/desktop` 图标。firstInit 走 LFN `system/user/use/firstInit.txt`。
### Suggested Action
簇缓冲 ≥ 32KiB,读长度用 BPB `spc`。
### Metadata
- Source: error
- Related Files: CODE/dsk/main.c, CODE/tools/fat32_io.h, CODE/linux/pack_system_image.sh
- Tags: fat32, cluster, desktop
- See Also: LRN-20260907-002
---
## [LRN-20260907-004] insight
**Logged**: 2026-09-07T22:45:00+08:00
**Priority**: high
**Status**: resolved
**Area**: backend
### Summary
virtio-mmio 未实现的 SHM 长度必须是 `(u64)-1`,否则 Linux virtio-gpu probe `-EBUSY`。virtio-blk config 必须与 Linux uapi 同布局。
### Details
修好后 guest `Initialized virtio_gpu` + `SET_SCANOUT 1024x768`。`vdb`/`vdc` 仍 sector 0 I/O error(见 ERR-20260907-001)。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/virtio_mmio.c, CODE/UTSM/vmm/virtio_blk.c
- Tags: virtio, gpu, shm
---
## [LRN-20260908-001] insight
**Logged**: 2026-09-08T23:30:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
initramfs 必须在 `mount --move /dev` 之前挂 `/dev/vdc`;KDE 在 extra-rootfs 的 `/mnt/persist`。glibc `#GP` 在 `endbr64`,不是 drm fbdev 的 SET_SCANOUT。
### Details
`[ -b /dev/vdc ]` 在 `/dev` 被 move 走之后恒为假。kmsg 日志才能从串口里捞到 init 行。验证脚本不能把启动时 virtio-gpu fbdev `KDE scanout w=` 当成 Plasma。 musl busybox 无 SHSTK 会在 push 上对地址 0 写崩溃;Arch bash/glibc 有 IBT,崩在 `endbr64`。Host 侧不要 `wrmsr` CET MSR(#GP 掉 UTSM)。
### Suggested Action
给 Linux guest 做正确的 VMX CET 虚拟化,或换一套无 IBT 的用户态。在此之前 startplasma 起不来。
### Metadata
- Source: error
- Related Files: CODE/linux/initramfs/init, CODE/UTSM/vmm/vmexit.c, CODE/linux/guest/deshab-kde-session
- Tags: overlay, vdc, cet, ibt, glibc
---
## [LRN-20260910-001] insight
**Logged**: 2026-09-10T12:30:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
Nested KVM 不虚拟化 CET VMCS;用户态 #GP 能进 L1 但 `guest_fetch` 读到 0,不能按字节 skip ENDBR。Host SHSTK 让 musl busybox `push` 写地址 0。
### Details
`insn0=0` 但 RIP 仍是 libc `...140`。盲 skip 4 字节会消掉 dmesg 里的 `#GP`,但 busybox.static 仍 `segfault at 0 ip=0x4bede0 error 6`。`IA32_VMX_TRUE_EXIT/ENTRY_CTLS` 的 CET save/load 位 nested 不允许。不要在 host 上 `wrmsr` CET。`exec_async` 的 stderr 是 `/dev/null`,session 日志要写 `/dev/kmsg`。
### Suggested Action
修好用户态 GVA walk(别读到 zeropage),再按真实 ENDBR/NOP 跳过 4 字节。在 CET 漏进 L2 的情况下 Plasma/glibc 起不来。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/vmexit.c, CODE/UTSM/vmm/linux_boot.c, CODE/linux/guest/deshab-kde-session
- Tags: cet, nested-kvm, guest-fetch, busybox
- See Also: LRN-20260908-001
---
## [LRN-20260910-002] insight
**Logged**: 2026-09-10T13:40:00+08:00
**Priority**: critical
**Status**: pending
**Area**: backend
### Summary
Nested KVM 让 L0 CET 作用于 L2 用户态,但 L1/L2 都无法读写 CET MSR。
### Details
L1 CR4.CET=0 且 CPUID 无 CET,WRMSR #GP。VMCS CET save/load 位 nested 不允许。VM-entry MSR-load U_CET=0 得到 exit 34。L2 `wrmsrl_safe(0x6a0)` 返回 -EIO。WSL `arch_prctl(ARCH_SHSTK_DISABLE)` EINVAL。busybox 在 `0x4bede0` 的 `push r13` 写地址 0(SHSTK);glibc 在 `…140` #GP(IBT)。盲 skip 4 字节会把 libc RIP 跑飞,不要再用。不要 host 全局 `wrmsr IA32_U_CET`。
### Suggested Action
需要 L0 真正虚拟化 CET(或换一套会给 vCPU 加载 CET=0 的 KVM),否则无法启动 glibc Plasma。不要再盲 skip ENDBR。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/linux_boot.c, CODE/UTSM/vmm/vmexit.c, CODE/linux/patches/utsm_hcall.c, ISO/run_qemu_kvm.sh
- Tags: cet, nested-kvm, shstk, ibt, busybox
- See Also: LRN-20260910-001
---
## [LRN-20260910-003] insight
**Logged**: 2026-09-10T14:40:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
park 之后拦截 #PF 会让 `utsm_exec_daemon` 在 `0x402661` 写 `0x4a36a0` 崩溃;nested 下 #PF 不能当普通异常 reinject。
### Details
16 字节对齐盲 skip 同样会弄坏 daemon。CR2 不是 VMCS 字段,resume 前 `mov cr2` 也不能修好。SHSTK 模拟因此没机会跑。不要在 Linux guest 的 exception bitmap 里开 #PF。
### Suggested Action
CET 仍只能靠 L0 虚拟化。保持 #GP/#CP 仅在真实 ENDBR/NOP 字节上 skip。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/linux_resume.c, CODE/UTSM/vmm/vmexit.c
- Tags: cet, page-fault, nested-kvm, daemon
- See Also: LRN-20260910-002
---
## [LRN-20260910-004] insight
**Logged**: 2026-09-10T16:30:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
L1 用 VMCS/tracked CR3 走 libc GVA 会落到 hole 零页;CPU 仍在执行真实 libc。盲 skip 4 会把 RIP 拆到下一条再 #GP。
### Details
`KVM_GET/SET_MSRS` 对 CET 返回 nmsrs=0。`arch_prctl` EINVAL。不要把 `/bin/sh` 指到 musl busybox.static。用户态 #GP 的 `VMCS_INSTRUCTION_LENGTH` 是 2/3,不是 ENDBR 的 4。`ept_gpa_to_hpa` 对 walk GPA 有 HPA,但内容全 0。CR3 拦截有效,故障时 CR3=`0x2fa2000`。不要再对 fetch-fail 的 0x7f 16 字节对齐 RIP 盲 skip 4。
### Suggested Action
对第一次用户态 #GP 打完整页表 walk(PML4[255] 等)。用能看见真实 libc 字节的翻译再 skip ENDBR。不要 skip `ilen`(nested 报短了)。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/vmexit.c, CODE/linux/patch_boot_on_img.sh, CODE/linux/qemu_disable_cet.c
- Tags: cet, page-walk, cr3, endbr, busybox
- See Also: LRN-20260910-003
---
## [LRN-20260910-005] insight
**Logged**: 2026-09-10T16:45:00+08:00
**Priority**: critical
**Status**: pending
**Area**: backend
### Summary
Nested KVM 下 EPT leaf 未设 Ignore PAT 时,guest PAT=WC(virtio-gpu shmem)与 EPT WB 组合成保留类型,触发 EPT misconfig;VMM 若因此 `resume=0` 会直接杀掉 Linux vCPU。
### Details
串口:`desktop ready` 后 `EPT misconfig gpa=0x3e20000`,随后 `sh` 在 `clear_page_erms` 对非规范地址 `RDI=0x0005088000005000` #GP。KVM 对 RAM leaf 一律打 `VMX_EPT_IPAT_BIT`。不要把 2MB PD leaf 当成页表指针去写 4K PTE。不要从 `/mnt/d` loop-mount 4GiB extra-rootfs。
### Suggested Action
EPT leaf(含 2MB)写 WB+Ignore PAT;misconfig 时 repair leaf 再 resume。extra-rootfs 只在 WSL 本机盘上 mkfs/mount。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/ept.c, CODE/UTSM/vmm/vmexit.c, CODE/linux/patch_boot_on_img.sh
- Tags: ept, nested-kvm, pat, virtio-gpu, extra-rootfs
- Pattern-Key: harden.input_validation
---
## [LRN-20260910-006] insight
**Logged**: 2026-09-10T19:45:00+08:00
**Priority**: critical
**Status**: pending
**Area**: backend
### Summary
单 vCPU 上 daemon `sched_yield` 后立刻 PARK/HLT,会在 virtio 完成中断注入前冻住整机;`linux_resume()` 必须能在非 HLT 的 preemption timeslice 上回到 DSK。
### Details
async 子进程(KDE session)对 persist/ext4 的 `[ -x ]` 会进入 D-state。PID1 yield 立刻返回,64×1024 次 yield 远短于 10ms 抢占周期,随后 HLT,virtio IRQ 来不及注入。`idle=poll` 不能救:runnable 的 PID1 不会把时间让给 idle。HLT park 路径上调用 `vmexit_before_resume()`(virtio poll)会在 host 栈上 #PF(CR2=3)。timeslice 只允许在 `linux_resume()` 期间 armed,`linux_launch()` 不可提前归还。
### Suggested Action
async 时 daemon 禁止 park;`linux_compat` 循环 `linux_resume` 直到 EXEC_EXIT;HLT/timeslice 出口不要跑 guest virtio poll,改在下次 `linux_resume` 入口 `vmx_linux_prepare_entry()`。
### Metadata
- Source: error
- Related Files: CODE/linux/initramfs/bin/utsm_exec_daemon.c, CODE/UTSM/vmm/vmexit.c, CODE/UTSM/vmm/linux_compat.c, CODE/UTSM/vmm/linux_resume.c
- Tags: hlt, virtio, timeslice, kde, persist
- Pattern-Key: harden.input_validation
- See Also: LRN-20260910-007
---
## [LRN-20260910-007] correction
**Logged**: 2026-09-10T20:15:00+08:00
**Priority**: critical
**Status**: pending
**Area**: backend
### Summary
`linux_resume()` 从 park 跳回 host 时 CPU 上仍是 guest GPR。用栈上 `failed`(`%rbp` 寻址)会在 CR2=3 处 host #PF。必须像 `linux_launch` 一样写 RIP 相对全局变量。
### Details
`vmexit_asm` 先恢复 guest GPR 再 jmp `g_saved_return_rip`。vscode `test -e` 第一次 resume 后 RIP=`0xffffffff80006535`,RAX=3,Linux 直映寄存器仍在 R9。DSK 从未启动。改成 `g_linux_resume_failed(%rip)` 并 push/pop rbx/rbp/r12-r15 后:vscode skip、desktop ready、KDE session 打出 `checking persist mount`。
### Suggested Action
所有 vmlaunch/vmresume 着陆垫禁止依赖 guest 破坏后的 rbp 栈槽。
---
## [LRN-20260910-008] correction
**Logged**: 2026-09-10T20:22:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
KDE 宿主循环里每帧 `exec("sleep 0.05")` / `test -f` 会占满唯一 vCPU:daemon `waitpid` 同步 exec,async session 停在 `checking persist mount`。应追加 ABI 兼容的 `run_slice`(只 `linux_resume`,无 IPC)。
### Details
verify: `interpreter alive` + `checking persist mount`,从未 `persist check done` / `entering KDE`。`kde_ok=2`(spawned)。guest TSC 不稳时 `sleep` 可能空转。`linux_compat_service.run_slice` 放在表尾。
### Suggested Action
desktop/DSK KDE 循环用 `run_slice`;原生切换探测降到每 32 次 slice 一次。不要再 `exec sleep`。
### Metadata
- Source: error
- Related Files: CODE/UTSM/include/utsm/linux_compat.h, CODE/desktop/lxc.c, CODE/dsk/main.c, CODE/linux/guest/deshab-kde-session
- Tags: kde, run_slice, persist, sleep
- Pattern-Key: harden.input_validation
- See Also: LRN-20260910-006
---
## [LRN-20260912-001] best_practice
**Logged**: 2026-09-12T12:45:00+08:00
**Priority**: critical
**Status**: pending
**Area**: infra
### Summary
不要把 linux-extra-rootfs.img 当 Limine boot module。4GiB 打进 RAM 会在 `log=21013` 把 6G WSL QEMU 掐死。
### Details
Windows Terminal 也会在 t=26s 回到提示符,不只是 Cursor 的 45s 管道问题。virtio-blk /dev/vdc 应映射 ESP 上的 FAT32 文件,经 AHCI 读写。
### Suggested Action
limine.conf 注释 extra-rootfs module;`virtio_extra_rootfs_blk_init` 走 ESP 簇映射。
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/virtio_blk.c, SYSTEM/limine/limine.conf
- Tags: limine, extra-rootfs, wsl, qemu
- See Also: ERR-20260911-002
---
### Metadata
- Source: error
- Related Files: CODE/UTSM/vmm/linux_resume.c, CODE/UTSM/vmm/vmexit_asm.S
- Tags: linux_resume, gpr, rbp, idt, vscode
---
## [LRN-20260912-002] insight
**Logged**: 2026-09-12T14:45:00+08:00
**Priority**: high
**Status**: pending
**Area**: backend
### Summary
KDE extra-rootfs glibc 带 XSAVE/YMM/ZMM GNU property;L2 CPUID 隐藏 XSAVE 后 persist bash/Xorg 在 chroot 里立刻 127。不要 `objcopy --remove-section=.note.gnu.property`。
### Details
vdb e2fsck 干净;运行时 checksum 打在 grep/mkdir/mount。静态 `utsm_bindmount` 已能 bind /dev。persist ld.so 直接跑会 SHSTK 写地址 0;chroot persist bash=127;叠 vdb ld.so+libc=139。
### Suggested Action
给 L2 做 XSAVE 保存/恢复并放开 CPUID,或 extra 换无 AVX glibc / Xfbdev。不要再拆 GNU property section。
### Metadata
- Source: conversation
- Related Files: CODE/linux/guest/deshab-kde-session, CODE/UTSM/vmm/vmexit.c, CODE/linux/initramfs/bin/utsm_bindmount.c
- Tags: kde, xsave, persist, chroot
---
---
## [LRN-20260912-003] best_practice
**Logged**: 2026-09-12T21:55:00+08:00
**Priority**: high
**Status**: pending
**Area**: tooling / desktop
### Summary
`mcopy -o` 热补 ESP 内已存在的长名文件会删除 LFN 链(只剩 8.3 短名),fat32_io.h 的 LFN 查找随之 -3;热补长名文件必须 `mdel` + `mcopy` 重建条目。
### Details
M1 zhfont 集成:宿主 zhfont_test 全 PASS 但 QEMU 内 `f32_read_path_lfn_to` 对 simhei_16.dbf 返回 -3。Python 直读 FONT 目录簇发现 dbf 条目无 0x0F LFN 项(simhei.ttf 的完好)。根因是先前的 `mcopy -o` 热补剥离了 LFN。修复后 `[zhfont] dbf loaded (16px ok)`。另:Windows QEMU 验证 desktop 时 q35 内置 AHCI 用 `-device ide-hd,bus=ide.0` 挂盘会被 ahci.drv 识别为端口 2 ATAPI 签名(identified ports=0),须按 run_qemu_kvm.sh 的 `-device ahci,id=ahci0 ... bus=ahci0.0` 显式建控制器;2G 内存验证需临时注释 limine.conf 的 4.9GB Linux 模块行(sed 注释 → 验证 → 恢复)。
### Suggested Action
热补长名文件:`mdel` + `mcopy`,之后 `mdir` 确认行尾长名仍在。QEMU desktop 冒烟:显式 ahci0 + 2G + lite limine.conf。详细记录见 .learnings/hotpatch-mcopy-lfn-pitfall.md。
### Metadata
- Source: error
- Related Files: CODE/desktop/zhfont.c, CODE/desktop/f32io.c, ISO/run_qemu_kvm.sh, SYSTEM/limine/limine.conf
- Tags: mtools, lfn, hotpatch, qemu, ahci, zhfont
-26
View File
@@ -1,26 +0,0 @@
# Learning: mcopy -o 热补镜像会剥离 LFN 长名链
**日期**: 2026-09-12
**分类**: best_practice
**优先级**: high
## Summary
用 `mcopy -o` 覆盖 GPT 镜像 ESP 内**已存在的带 LFN 文件**时,mtools 会删除旧条目并写入新条目,但**新条目不带 LFN 长名链**(只有 8.3 短名,如 `SIMHEI~1.DBF`)。裸机 fat32_io.h 的 `f32_find_in_dir_lfn` 先做 LFN 匹配再回退 8.3 短名匹配——对 `simhei_16.dbf` 这类长名文件,短名 `SIMHEI~1.DBF` 无法匹配 → 查找返回 -3。
## Details
- 现象:zhfont 加载 `system/font/simhei_16.dbf` 返回 -3(文件未找到),但同目录 simhei.ttf(LFN 完好)能被 mdir 正常列出
- 诊断:Python 直读镜像目录簇(offset = (2048 + data_lba + (clus-2)*spc)*512)对比发现 dbf 条目前无 0x0F LFN 项
- 修复:`mdel ::SYSTEM/FONT/SIMHEI~1.DBF` 后重新 `mcopy`(新建条目会带 LFN)
- 同日另一坑:q35 下启动盘挂 `-device ide-hd,bus=ide.0` 会落在 AHCI 端口 2 且签名异常(identified ports=0);应使用显式 `-device ahci,id=ahci0 ... bus=ahci0.0`(run_qemu_kvm.sh 的挂法)
## Suggested Action
热补 ESP 内长名文件的正确流程:`mdel` + `mcopy`,**不要** `mcopy -o`。验证:补丁后 `mdir -i img@@1048576 ::路径` 确认 LFN 列还在(行尾显示长名)。boot 日志 `[zhfont] dbf loaded (16px ok)` 为加载成功标记。
## Metadata
- Source: M1 zhfont 集成调试(zhfont_test PASS 但 QEMU 内 -3)
- Related Files: CODE/desktop/zhfont.c, CODE/desktop/f32io.c, CODE/tools/fat32_io.h
- Tags: mtools, fat32, lfn, hotpatch, qemu
-347
View File
@@ -1,347 +0,0 @@
# Deshab 项目上下文
## 项目概览
Deshab 当前包含一个内核/系统实验方向,其中启动内核与主内核目标为:
```text
CODE/UTSM -> SYSTEM/boot/utsm.elf
CODE/dsk -> SYSTEM/system/deshab64/deshab.elf
```
`utsm.elf` 是当前由 bootloader 直接加载的首阶段内核,负责 UTSM、DKM 驱动加载、平台发现和早期系统服务。当前已实现两阶段 DSK 加载:优先通过 `kernel_api.block` + 内嵌 FAT32 解析从真实块设备读取 `/system/deshab64/deshab.elf`(在 SATA FAT32 盘场景已验证);无块设备时自动回退 Limine boot module 预加载。
DSK 主内核(`deshab.elf`)当前已实现:
- 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找)
- PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位)
- 静态启动 Logo(嵌入 Logo.png RGBA 数据,居中 alpha 混合绘制于渐变背景上,取代原旋转加载动画)
- 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程
- 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化)
- 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲)
FirstInit(`CODE/firstInit`)只负责用户级设置向导:
- 淡蓝背景 → "欢迎使用 Deshab"(中英双语,英文小字副标题)→ 设置提示 → 账户设置卡片
- PS/2 键盘轮询输入:计算机名、用户名、密码
- SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API)
- 18px 黑体中文位图(预渲染嵌入 ELF)
mouseInit(`CODE/mouse`)是 DSK 直接调度的系统输入初始化器:
- 当前为安全 stub(只读 PS/2 状态,不写控制器),避免阻塞首次启动
- 后续将实现完整 IRQ12 + 鼠标包解码
## 当前核心架构
```text
SAS-R0-PCQ
单地址空间 Ring0 任务模型
Per-CPU O(1) 位图调度器
DRR
Dedicated Recovery Root
看门狗 / checkpoint / recovery log / A-B 回滚
UTSM
UUID-Tuned Sealed Memory
UUID 调谐封缄内存
UTRW
UTSM Tagged Reader/Writer
UTSM 带标记读写器
DMP
Descriptor Meta Page
段描述符元页
PCKC
Per-CPU Key Cache
每 CPU 密钥缓存
FUCK
内核启动配置文件
INI 分区格式,控制启动行为/驱动开关/算法参数/桌面行为
```
## 关键设计约束
1. 优先考虑极高权限和极高效率。
2. 主系统采用单地址空间 Ring0 模型。
3. 不追求传统多用户强安全隔离。
4. DRR 作为恢复根,应尽量独立于普通调度器。
5. UTSM 数据区连续存密文,UUID 不嵌入数据区。
6. 调度器热路径必须保持 O(1)。
7. checkpoint 只处理 dirty shard / dirty page,不扫描全内存。
8. DRR 必须拥有独立 Emergency Pool,普通 OOM 不应阻断 recovery。
9. checkpoint metadata 使用 A/B 双槽、CRC 校验和原子 active slot 切换。
10. 驱动模块采用 DKM ELF64 .drv ABI,通过 driver_desc / driver_init / driver_exit 暴露入口。
11. 驱动默认通过 dkm_kernel_api 调用内核服务,不直接依赖任意内核符号。
12. stage0/stage1 驱动必须支持 bootloader 预加载 boot module 方案。
## 重要文件
```text
SYSTEM/
打包为 IMG 后的系统根目录。
SYSTEM/driver/manifest.json
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
SYSTEM/STARTMENU.CNF
M3 开始菜单注册表(FAT32 根目录,desktop 启动时解析)。
格式 NAME|显示名|path|args;path 支持 builtin:<name> / program/xxx.exe(GUI PE,
g++ 编译,run_windowed 嵌入桌面)/ bin/xxx.elf(ELF 工具,全屏加载)/
/guest/path(Linux guest 应用)。显示名可 UTF-8 中文;文件缺失时回退全部已注册应用。
纯 CLI exe(bin/pciscan.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行,
输出走串口。源码在 CODE/tools/pe-tools/(build.ps1 用 mingw-w64 g++ 编译),
ELF 工具源码在 CODE/tools/diskinfo/(→ SYSTEM/bin/diskinfo.elf)。
SYSTEM/program/ + SYSTEM/bin/ + SYSTEM/lib/ + PEAPPS.CNF + LINUXAPP.CNF + STARTMENU.CNF
桌面 PE/工具链全部经 DSK FAT32 只读 ESP——pack_system_image.sh 会把这几项
额外拷到 FAT32 ESP(p1)。漏拷则开始菜单/PE 应用在 GPT 镜像里静默失效。
SYSTEM/system/deshab64/FUCK
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为、panic 策略([panic] mode=strict|fallback)。
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
CODE/UTSM/kernel/ini_parser.h / ini_parser.c
UTSM INI 配置解析器,支持分区格式解析、key/value 查询、整型和布尔值便捷获取。
CODE/DKM/README.md
DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。
CODE/UTSM/README.md
UTSM 模块设计说明。
docs/RE/UTSM_算法记录.md
UTSM 核心算法记录。
docs/RE/UTSM_设计架构.md
UTSM 架构记录。
docs/RE/驱动模块ABI设计.md
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机、boot module 方案和服务总线 ABI(§8.5)。
docs/RE/万物互联与服务总线设计.md
万物互联架构定稿:dkm_bus 按名服务总线(kern.*/srv.*)、dsk_boot_context 逐槽迁移表、
DSK 溶解地图、P0-P6 落地路线与错误码登记(BUS-E/DKM-E44..47/SESSION-E)。
ISO/deshab.img
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
ISO/run_qemu.bat
使用 QEMU 启动 ISO/deshab.img 的脚本。
```
## 构建相关
已知说明:
```text
SYSTEM/ 是唯一打包输入:build.ps1 只扫描 SYSTEM/,不从 /home 或其它路径取发行物。
所有要进镜像的软件(utsm.elf、deshab.elf、.drv、bzImage/initrd、
linux-rootfs.img、linux-extra-rootfs.img、Python/KDE 内容)必须先落到 SYSTEM/。
打包成 GPT 双分区:p1 FAT32 ESP(SYSTEM/EFI + SYSTEM/limine + SYSTEM/boot
+ SYSTEM/driver + SYSTEM/system — Limine 10 不能读 ext4,boot(): 模块必须在 ESP),
p2 ext4(其余 SYSTEM/,并保留 driver/system 副本)。由 WSL
CODE/linux/pack_system_image.sh 执行 mkfs.vfat + mkfs.ext4。
ext4 UUID 固定为 64657368-6162-4000-8000-000000000002。
内核/驱动/DSK 一律走 boot(): (FAT32 ESP)。
FAT32 单文件上限 4GiB-1 仍适用于 ESP 上的 /boot 文件,linux-extra-rootfs.img 必须小于 4GiB。
SYSTEM/boot/ 存放启动模块,例如 utsm.elf。
SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
build.bat: 调用 build.ps1,透传参数(如 -Variant dev)。
build.ps1: 编译 UTSM/DSK/工具/驱动,把整个 SYSTEM/ 打成 GPT(FAT32 ESP + ext4) 镜像:
- ISO/deshab-dev.img 开发者模式(dev_mode=1,自动跑 shell.elf 测试,调试全开,QEMU 验证选项,panic=strict)
- ISO/deshab-release.img 发布版(dev_mode=0,进桌面+登录,调试零开销,真机安全选项,panic=fallback 即关闭 panic)
- ISO/deshab-realtest.img 真机测试版(release 同款真机选项 + 登录路径,panic=strict 即启用 panic)
参数 -Variant dev|release|realtest|both|all(默认 all = 三者;both = dev+release)。both/all 构建后恢复 SYSTEM 为 dev 配置。
配置模板在 build/configs/{dev,release,realtest}/(FUCK + firstInit.txt),打包前注入到 SYSTEM。
realtest/FUCK 由 release/FUCK 复制而来,仅头注释与 [panic] mode 不同;改 release 真机选项须同步 realtest。
ISO/run_qemu.bat [dev|release|realtest]: 用 QEMU + UEFI 固件启动对应镜像(默认 dev),串口输出到 stdio。
ISO/run_qemu_kvm.sh: 默认 12G 内存(Limine 会把 SYSTEM/boot 里的 rootfs 模块装进内存)。
build.ps1 自动把 Git usr/bin 加入 PATH(sh.exe + mkdir),解决 make `mkdir -p` 在 cmd.exe 下失败的问题。
```
当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT(FAT32 引导分区 + ext4 数据分区)镜像。早期启动要求:进入 C 前 `cli/cld`,early serial 不允许无限等待硬件 ready 位,未启用 FPU/SSE 前编译必须使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。DATA/BSS 段使用 PF_R|PF_W|PF_X(SAS-R0 下驱动模块内存需可执行)。
DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32/PLT32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
> **relocation 集合注意**:`R_X86_64_PLT32(4)` 语义等同 PC32(静态可重定位下的 `S+A-P`)。多编译单元驱动(如 `ath9k.drv = ath9k.o + wpa2_crypto.o`)跨 TU 的全局函数调用会被 lld 生成为 PLT32;loader 不支持即启动 `DKM-E37`。新增多源 `.drv` 前用 `llvm-readobj --relocations` 核对类型是否在上述集合内。
> **ath9k 硬件要求(实机 WiFi 试验)**:原生驱动仅支持 **Atheros AR928x 系列 PCIe 无线网卡(vendor `168c`,如 AR9285/9280/9287)**。FUCK `[drivers] ath9k=1` 时才加载。若机器无匹配 Atheros 卡、BAR0 位于 4G 以上、或复位/初始化失败,`driver_init` 返回非零 → `DKM-E39 EXTERNAL DRIVER INIT FAILED` **开机即 panic**(零降级,不豁免);串口会打印本机所有无线控制器 (class 02:80) 的 vendor:device 供确认卡型号。Intel/Realtek/MediaTek 等其他厂商网卡**不支持**。`wlan.drv` 为框架骨架,在有无线控制器的机器上只注册设备,其 scan/connect 调用即 `NETWL-E10/E11` panic。
当前已实现 14 个真实 DKM 驱动:
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC enable + IOAPIC 重定向表编程(ISO 表感知); B7: FUCK `apic_route=1` 时 IOAPIC→LAPIC 接管(注册 UTSM LAPIC EOI 钩子 + PIC 全屏蔽 + TPR=0 + tick vec 0x20 链路自验,失败自动完整降级回 PIC); `apic_route_legacy=1` 额外放开 IRQ1/12/11 持续投递(e1000 强制 level/active-low); `apic_diag=1` HPET 绝对时基对拍 + 键鼠流观察窗
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
- **pci** (stage0): PCI config space 扫描, 设备枚举
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管)
- **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令
- **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力
- **bootfs** (stage1): Limine boot module 内存文件系统
- **vfs** (stage2): 挂载 bootfs, 文件查找/读取
- **devfs** (stage2): 暴露 `/dev/version`, `/dev/platform`, `/dev/fb0`, `/dev/modules`, `/dev/boot/`
- **fat32** (stage2): 只读 FAT32 BPB/FAT/目录项解析, 通过 `driver/test.fat32` 验证读取 `README.TXT`
- **ps2kbd** (stage3): PS/2 IRQ1 注册, 端口 `0x60` 扫描码读取, scan code set 1 → ASCII demo
- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup
- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包
当前内核平台能力:IDT 0–255 stub 全覆盖、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、UTSM 侧 LAPIC EOI 钩子(`idt_register_apic_eoi`,IRQ 分发末尾调用,spurious 0xFF 特判跳过)、IOAPIC→LAPIC 路由接管(B7,legacy IRQ1/12/11 可持续投递)、Limine RSDP/framebuffer/module/HHDM request。
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages, block provider registry, mmio/mm_map_mmio/mm_unmap_mmio, register_apic_eoi(尾部追加,ABI 兼容)。
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
下一阶段优先路线(活跃路线 = **万物互联 P0-P6**(D6/D7,设计定稿 `docs/RE/万物互联与服务总线设计.md`)+ `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`):
0. **万物互联(D6/D7,当前最高优先)**: P0 manifest 依赖图强制 → P1 dkm_bus 双向服务总线(kern.*/srv.*)→ P2 fat32/ext4.drv 归位 → P3 加载器 FS 化 + stage4 + ps2mouse.drv → P4 六 app 模块 DKM 化(netman/shell/firstInit/login/desktop/cmd)→ P5 DSK 退役 + session.drv → P6 常驻调度 + 真 IRQ 输入。每 phase QEMU 验证后提交,进度见 STATUS.md 万物互联表。
1. **一期 F 系列收尾**: F2 统一控制台后端(fbcon.h 抽象已落,panic 已用)。(F1 莲花 panic / F3 跨平台 Makefile / F4 boot 分级自检 / F5 skip_firstinit 已完成)
2. **二期 U 系列**: ✅ 已完成(U1 UUID 表 / U2 Slow Path 真分支 / U3 @sealed 可选标记+linker .sealed section / U4 密文 dump / U5 RFC 8439 KAT)。chacha20 KAT 三方对拍通过(pycryptodome+官方向量+C),"间歇 FAIL" 确认为从未合并的幽灵缺陷。
3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表升级为服务总线(并入万物互联 P1)。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本)
4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核(多核在 P6 常驻调度之后排队)。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS)
5. **五期 E 系列**: 启动莲花动画、假想时光键彩蛋、提交信息规范、风味 ISO。
6. **工程债(Phase 存档遗留)**: vector allocator/MSI-X、NVMe 高位 BAR、virtio-net virtqueue、DKM 零 warning。
## M1 里程碑完成状态(2026-07-29)
### 任务 A: B6 FirstInit 按键崩溃修复
**状态**: [RESOLVED-NOT-REPRODUCIBLE]
**验证结果**:
- QEMU 复现测试完成,系统完整运行 FirstInit 流程无崩溃
- 按键扫描码处理正常,字段输入正确(测试序列: a, b, Enter, shift+comma, Enter, 1, 2, Enter)
- user.conf 成功写入磁盘,firstInit.txt 成功更新为 "1"
- 系统继续加载 desktop
**证据**: `.build_tmp/qemu_serial_b6.log` 串口日志,调试记录 `debug-firstinit-key-crash.md`
**结论**: B6 崩溃在当前构建中未复现,可能已在之前的修改中修复,或崩溃条件较特殊未触发。
### 任务 B: PS/2 鼠标完整实现
**状态**: [COMPLETED]
**实现内容**:
1. **完整初始化序列** (`CODE/mouse/main.c`):
- ps2_drain() 冲刷控制器残留数据
- 读配置字节 → 启用 AUX 时钟/IRQ/翻译 → 写回
- 启用 AUX 端口
- 复位鼠标 → 验证 ACK/自检/设备 ID
- Set Defaults → 采样率 100 → 分辨率 2 → 启用数据报告
- 全程超时保护(100000 次),单步失败只记日志继续
2. **IRQ12 中断处理**:
- 经 kernel_api.irq_register 注册 IRQ12 handler
- 读数据前检查 AUX 位(键盘数据留给键盘路径)
- 3 字节包解码:同步位验证、溢出检测、9 位有符号位移合成
- 坐标累计并钳位到 framebuffer 边界
- 安全不变式:注册但不解屏蔽 PIC,DSK 阶段 IF=0
3. **共享状态接口**:
- `mouse_state_t` 结构体(magic/version/x/y/buttons/计数器/边界)
- `g_mouse_state` 导出全局符号
- `mouse_poll_state()` 只读快照查询接口
**验证结果**:
- mouseInit 初始化成功:`cfg byte=0x47`, `data reporting enabled`, `IRQ12 handler registered`
- FirstInit 中鼠标轮询正常:AUX 端口启用、复位、自检、流模式启用成功
- 鼠标数据包正确接收和解码(串口日志显示 "[mouse] pkt")
**证据**: `.build_tmp/qemu_serial_mouse.log` 串口日志
**技术要点**:
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
- 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志
## M4 里程碑完成状态(2026-09-15):SAS-R0-PCQ 调度器 + DRR 恢复根
### 任务 A: SAS-R0-PCQ 调度器(ROADMAP Phase 7)
**状态**: [COMPLETED]
**实现内容**:
1. **O(1) 位图调度器** (`CODE/UTSM/sched/sched.c`):
- 64 槽 TCB 表 + 8 CPU runqueue(当前 BSP-only),ready_bitmap ctz 选最高优先级(0 最高)
- 侵入式 O(1) FIFO runqueue(slot 索引非指针),冻结 ABI 9 API 真实现
- 任务栈从 arena 分配,entry/arg/stack_base 存 task_aux
2. **xv6 式栈驱动切换** (`CODE/UTSM/sched/switch.S`):
- 抢占 = IRQ 帧落在被抢占任务自己的栈上,切换只换 rsp
- trampoline 跳 entry;schedule/yield/sleep/exit 全路径
- 切换路径只触碰 crypto_context 指针(禁止扫描 capability/MAC/checkpoint)
3. **LAPIC timer tick** (`CODE/UTSM/arch/x86_64/lapic_timer.c`):
- MMIO 映射 + TSC 延时校准(10ms 逆计数法)+ 周期模式
- tick_hz/timeslice_ms 由 FUCK [sched] 配置(默认 100Hz/10ms)
- demo 窗口含 tick 失速兜底:心跳无进展自动中止窗口,不卡死 bootstrap
4. **FUCK 配置**: `[sched] enable/tick_hz/timeslice_ms/demo_ms/demo_fault`
**验证结果**:
- selftest: runqueue O(1) PASS、block/wake PASS
- demo 窗口: 3 任务(prio 1/20/40)按优先级交错运行并正常退出
- 窗口结束 quiesce → timer stopped → 正常交接 DSK
### 任务 B: DRR 恢复根(ROADMAP Phase 8)
**状态**: [COMPLETED-MAC占位]
**实现内容** (`CODE/UTSM/drr/drr_stub.c`):
1. **快照区**: 静态区域(128 页容量,编译期),页条目含 seg_slot/page_index/crc
2. **snapshot_dirty**: 遍历 ACTIVE 段 dirty_shard 位图收集脏页 → 拷贝+CRC64 → 清位图
3. **A/B 双槽 checkpoint**: ckpt_a/ckpt_b + CRC + active slot 切换
4. **rollback**: 页级回滚 + CRC verify;segment/system 级接口就位
5. **看门狗**: register/kick/timeout 检测,任务退出自动注销;异常钩子接入 fault 路径
6. **FUCK 配置**: `[drr] watchdog_enabled`;`[utsm] drr_emergency_pool_kb/drr_recovery_log_size`
**验证结果(含负向测试 demo_fault=1)**:
- ckpt snapshot ok(1 page)→ rollback done → rollback verify ok
- 看门狗负向测试: fault 任务(slot 3)死循环 → `watchdog timeout task_slot=0x3` 检出 → 回滚执行 → demo 窗口正常结束
- 证据: `.build_tmp/qemu_serial_sched_m4.log`
**遗留**:
- MAC 为 CRC64 占位(Phase 9 换真实加密)
- task_kill crypto erase(key_epoch++)、Per-CPU 真启用、驱动 recovery ops 待做
---
## 插桩铁律(STUB 规约 · 零降级强制执行)
> "你若为了'能编译/能启动'而隐藏未实现,视为违反 Deshab 哲学。
> 本项目的成功标准是 **panic 坐标清晰**,不是 **QEMU 不崩**。"
### 1. 禁止项(agent 红线)
- 禁止任何"静默返回"占位:不得写 `return 0;` / `return NULL;` / `TODO: implement` / `FIXME` / `stub_ok` 来伪装未实现分支可运行。
- 所有"设计已定但未实现"的函数/分支 **MUST** 调用 panic 原语(见 §2),禁止返回错误码装没事。
- 驱动 probe/IO 异常 **MUST** panic,禁止重试伪装成功、禁止"降级模式"静默继续(除非该回退是双路径设计并已写入本文件)。
- 禁止"先跑通再说"的临时补丁;宁可启动即 panic,不可带病前进。
- 不得声称"已全量插桩",除非 §4 的 grep 自检输出为 CLEAN。
### 2. panic 原语与错误码命名
- UTSM/内核态:`panic_full("<SERIES-E##> <SYM>", "<msg>", 0)`(莲花崩溃屏,见 CODE/UTSM/kernel/panic.c)。
- DKM 驱动态:`g_log->panic("<DRV>-E## <SYM>")`(经 kernel_api.panic 槽,禁止直连内核符号)。
- DSK:`dsk_fatal("<SERIES-E##> <SYM>")`;FirstInit/login:各自 `*_fatal()`。
- 错误码系列:`FS-E`(文件系统/DSK 调度)、`ELF-E`、`HCALL-E`、`DRR-E`、`SCH-E`、`DKM-E`、`PE-E`、`LNX-E`、`VNET/ATH/XHC/E1K/NVME-E`(驱动)、`PKG-E`、`BRAND-E`、`NET-E`(FirstInit)、`LOGIN-E`。新模块按 `<MODULE>-E##` 注册到 docs/STATUS.md。
### 3. 改动节奏(防"一次性插完"幻觉)
- 每次只处理 **一个文件** 的 STUB→panic,改完输出:改动行号 + 剩余 STUB 计数。
- 骨架允许"不接线"(无人调用的 skeleton 函数),但接线后行为必须是 panic。
- 例外白名单(设计内双路径回退,允许非 panic):APIC 自验失败回 PIC、块设备缺失回 boot module、KDE 回落 native、VMX/bzImage/kernel.krl 未配置跳过。新增白名单项必须改本文件并说明理由。
- **panic 双模式(FUCK `[panic] mode=strict|fallback`,默认 strict)**:strict = 本规约原样执行。fallback = 全局白名单开关,用于真机/演示"带病看全貌":
- UTSM `panic_full(sym,msg,NULL)` 只打 `[PANIC:FALLBACK]` + 错误码 + 命中计数后返回,调用点走紧随其后的备用路径(跳过该能力/驱动/段/页面);DKM `kernel_api.panic` / `kern.log.panic` 同样返回驱动,驱动 init 返回非零。
- 模式经 `DSK_BOOT_FLAG_PANIC_FALLBACK (1<<4)` 下传,DSK/FirstInit/login/desktop 的 `*_fatal` 同样只留串口后返回;DSK ext4 内部 fatal 经 `__builtin_setjmp/longjmp` 中止本次操作 → 只用 FAT32 ESP。
- **永远停机(`panic_halt`,两种模式都不返回)**:CPU 异常(带 regs)、LA57 已启用(VMM-E11,4 级页表遍历会写坏内存)、DSK 跳转失败(VMM-E15)、panic 测试钩子(UTSM-E42)。
- 新增 panic 点必须保证 panic 调用后存在安全的继续路径(return 错误码 / continue / 置能力未就绪),否则改用 `panic_halt` 并登记到上一条。
- 启动期能力证明(attest,CODE/UTSM/kernel/attest.c):FUCK `[attest]` `require_<name>=1`(默认)而能力未就绪 → 交接 DSK 前 panic(莲花屏,错误码=该能力登记码)。`require_<name>=0` 豁免行是设计内白名单,仅限两类:① 该构建环境不可能具备(QEMU 无 wlan/嵌套 VMX);② STATUS.md ⬜/🔶 未落地项(实现后逐项删除豁免行)。当前豁免清单见 build/configs/*/FUCK [attest]。
### 4. 自检命令(验收只认这个输出,不认自然语言保证)
```
powershell -Command "Get-ChildItem CODE\UTSM,CODE\DKM,CODE\dsk -Recurse -Include *.c,*.h |
Select-String -Pattern 'TODO|FIXME|stub_ok|\bstub\b|未实现|暂不支持|待实现|not implemented' |
Group-Object Path | Sort-Object Count -Descending"
```
- 命中行 **只允许** 两类:① panic 调用/其注释(含错误码字面量);② 本文件白名单内的设计内回退日志。
- 其余命中 = 未清干净,禁止提交。基线快照(2026-10-03):~118 命中,绝大多数已是 panic 坐标注释;新增代码不得抬高裸命中数。
### 5. 验收格式(每次插桩提交必须附)
```
[STUB] <阶段>/<模块>/<文件>::<函数> | 改动行=<L1-L2> | 剩余STUB=<N> | “<一句疯洁标语>” | 已提交=<hash>
```
- 例:`[STUB] P1/UTSM/vmm/hypercall.c::hcall_utrw_read | 改动行=176-181 | 剩余STUB=112 | “封缄之下,未成之物当自爆”`
-691
View File
@@ -1,691 +0,0 @@
# DKM 驱动模块系统
```text
DKM = Deshab Kernel Module
DSM = Driver Startup Manager
```
DKM 是 Deshab 内核的驱动模块系统,负责在内核启动时加载 `SYSTEM/driver` 下的 ELF64 `.drv` 驱动模块,并把驱动注册到内核服务体系中。
当前实现状态:CODE/UTSM 内核已接入完整 DKM/DSM 驱动加载系统。内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载。全部 14 个外部 `.drv` 已完成 ET_REL 装载全链路:ELF64 校验、符号扫描、section 拷贝到 arena、SHT_NOBITS 清零、R_X86_64_64/32/32S/PC32 重定位、driver_desc magic 校验、外部 driver_init 调用进入 ACTIVE。当前镜像还预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
Linux/Windows 已编译驱动不能直接作为 DKM 驱动使用。真实驱动必须按本文件定义的 DKM ABI 编译,导出 `driver_desc`、`driver_init`、`driver_exit`,并只通过 `dkm_kernel_api` 调用内核服务。
当前真实驱动进度:14/14。
```text
stage0: timer, apic, acpi, pci
stage1: console_fb, ahci, nvme, bootfs
stage2: vfs, devfs, fat32
stage3: ps2kbd, e1000, virtio_net
```
manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。当前 IDT/PIC/IRQ 基础设施已在内核侧实现,`apic` 已完成 APIC discovery 并保留 PIC IRQ 路由,`ahci` 已完成 PCI/ABAR/HBA/port discovery,并通过 `kernel_api.dma.alloc_pages` 接入早期低位 DMA buffer,支持 SATA disk IDENTIFY 与 LBA0 READ 最小路径;识别 SATA disk 后可向 `kernel_api.block` 注册 `ahci0` block provider。`fat32` 已优先通过 block provider 读取 FAT32 镜像,无设备或 BPB 无效时回退 boot module。`nvme` 已完成 PCI discovery 并能识别 QEMU NVMe 控制器;由于当前 QEMU BAR0 位于 4G 以上,NVMe MMIO register 读取等待高位 PCI MMIO 映射能力。`ps2kbd` 和 `e1000` 已通过 `kernel_api.irq_register` 验证 IRQ handler 注册路径。`virtio_net` 已完成 PCI discovery 和 modern virtio capability 枚举,尚未进行 feature negotiation / virtqueue / RX-TX。
下一阶段 DKM/驱动路线:
```text
P0: 物理页分配器 + 页表/MMIO 映射接口
P1: DMA buffer/屏障约定 + AHCI/NVMe block 数据路径
P2: IDT 256 vectors + vector allocator + APIC/IOAPIC/MSI IRQ backend
P3: e1000 与 virtio_net RX/TX 数据路径
P4: FAT32 接入真实 block provider,替代 test.fat32 启动镜像
P5: 驱动 ABI 公共头、warning 清理、QEMU 场景自动化
```
---
## 1. 模块目标
DKM 的目标:
```text
1. 从 SYSTEM/driver/manifest.json 读取驱动清单。
2. 按 stage 分阶段加载驱动。
3. 支持 ELF64 .drv 模块加载和重定位。
4. 提供稳定的 driver_desc ABI。
5. 通过 dkm_kernel_api 向驱动暴露内核服务。
6. 支持 stage0/stage1 boot module 早期加载。
7. 将驱动内存纳入 UTSM。
8. 将驱动状态和恢复操作纳入 DRR。
```
---
## 2. 系统目录约定
打包为 IMG 后,`SYSTEM` 是系统根目录。
```text
SYSTEM/
boot/
utsm.elf
limine.conf
driver/
manifest.json
platform/
timer.drv
apic.drv
acpi.drv
bus/
pci.drv
block/
ahci.drv
nvme.drv
fs/
bootfs.drv
vfs.drv
fat32.drv
devfs.drv
net/
e1000.drv
virtio_net.drv
input/
ps2kbd.drv
console/
console_fb.drv
```
当前驱动清单:
```text
SYSTEM/driver/manifest.json
```
---
## 3. 启动阶段
DKM 使用分阶段加载。
```text
stage0: platform
timer / apic / acpi / pci
建立平台发现、中断、总线和基础计时能力。
stage1: boot
console / block / bootfs
建立启动日志、块设备和启动文件访问能力。
stage2: filesystem
vfs / fat32 / devfs
建立完整 VFS 和可挂载文件系统。
stage3: optional
net / input / gpu
加载网络、输入、图形等非启动关键驱动。
```
规则:
```text
1. stage0/stage1 可由 bootloader 预加载为 boot module。
2. stage2/stage3 默认从 SYSTEM/driver 加载。
3. required=true 的驱动失败时进入 DRR recovery 或 panic。
4. required=false 的驱动失败时标记 FAILED 并继续启动。
```
---
## 4. driver_desc ABI
每个 `.drv` 必须导出三个符号:
```text
driver_desc
driver_init
driver_exit
```
### 4.1 常量
```c
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1
```
`0x444B4D31` 表示:
```text
DKM1
```
### 4.2 driver_desc 结构
```c
struct dkm_driver_desc {
uint32_t magic;
uint16_t abi_version;
uint16_t desc_size;
const char *name;
const char *version;
const char *vendor;
uint32_t driver_class;
uint32_t stage;
uint32_t flags;
uint32_t priority;
const char *const *depends;
uint32_t depends_count;
const char *const *provides;
uint32_t provides_count;
uint64_t min_kernel_abi;
uint64_t feature_bits;
uint64_t reserved0;
uint64_t reserved1;
};
```
### 4.3 driver_class
```text
DKM_CLASS_PLATFORM
DKM_CLASS_BUS
DKM_CLASS_INTERRUPT
DKM_CLASS_TIMER
DKM_CLASS_CONSOLE
DKM_CLASS_STORAGE
DKM_CLASS_FS
DKM_CLASS_NET
DKM_CLASS_INPUT
DKM_CLASS_GPU
DKM_CLASS_MISC
```
### 4.4 flags
```text
DKM_F_REQUIRED
当前 stage 中失败则进入 DRR recovery 或 panic。
DKM_F_BOOT_MODULE
可由 bootloader 预加载。
DKM_F_NO_UNLOAD
不允许卸载。
DKM_F_STRONG_RECOVERY
驱动 data/bss 段使用 UTSM 强恢复策略。
DKM_F_DMA_REQUIRED
驱动需要 DMA API。
DKM_F_EARLY_LOG
驱动 init 期间允许使用 early log。
```
---
## 5. 驱动入口
```c
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle);
int driver_exit(struct dkm_driver_handle *handle);
```
约定:
```text
1. driver_init 返回 0 表示成功。
2. driver_init 返回负数表示失败。
3. driver_exit 仅用于非 DKM_F_NO_UNLOAD 驱动。
4. required 驱动失败会触发 DRR recovery 或 panic。
5. optional 驱动失败会被标记为 FAILED。
```
---
## 6. kernel_api ABI
驱动默认不直接依赖任意内核符号,而是通过 `dkm_kernel_api` 调用内核服务。
```c
#define DKM_KERNEL_API_VERSION 1
struct dkm_kernel_api {
uint32_t version;
uint32_t size;
uint64_t feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
uint64_t fb_width;
uint64_t fb_height;
uint64_t fb_pitch;
uint16_t fb_bpp;
const void *boot_modules_response;
int (*irq_register)(uint8_t irq, void *handler);
uint64_t hhdm_offset;
};
```
### 6.1 log API
```c
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
```
### 6.2 memory API
```c
struct dkm_mem_api {
void *(*alloc)(uint64_t size, uint32_t flags);
void (*free)(void *ptr);
void *(*alloc_pages)(uint32_t order, uint32_t flags);
void (*free_pages)(void *ptr, uint32_t order);
void *(*mmio_map)(uint64_t phys, uint64_t size, uint32_t flags);
void (*mmio_unmap)(void *virt, uint64_t size);
};
```
### 6.3 UTSM API
```c
struct dkm_utsm_api {
int (*create_segment)(uint64_t size, uint32_t flags, struct utsm_capability *out);
int (*read)(struct utsm_capability cap, uint64_t off, void *dst, uint64_t len);
int (*write)(struct utsm_capability cap, uint64_t off, const void *src, uint64_t len);
int (*pin)(struct utsm_capability cap, uint64_t off, uint64_t len, void **window);
int (*unpin)(void *window, uint32_t flags);
};
```
### 6.4 当前平台直通能力
当前内核已通过 `dkm_kernel_api` 直接暴露早期平台信息和 IRQ/HHDM 能力:
```text
rsdp_address Limine RSDP 地址,用于 ACPI 驱动
fb_address/width/height Limine framebuffer 信息,用于 console_fb
fb_pitch/fb_bpp framebuffer 行跨度和像素格式
boot_modules_response Limine module response,用于 bootfs/fat32 测试镜像
irq_register 注册 PIC IRQ0–15 handler,用于 ps2kbd/e1000
hhdm_offset physical → virtual direct map,用于 PCI BAR MMIO
```
### 6.5 IRQ / PCI / DMA API
```c
struct dkm_irq_api {
int (*register_irq)(uint32_t vector, int (*handler)(void *ctx), void *ctx, uint32_t flags);
int (*unregister_irq)(uint32_t vector);
void (*ack)(uint32_t vector);
};
struct dkm_pci_api {
int (*scan)(int (*cb)(const struct pci_device *dev, void *ctx), void *ctx);
int (*enable_device)(const struct pci_device *dev);
uint64_t (*bar_phys)(const struct pci_device *dev, uint32_t bar);
uint64_t (*bar_size)(const struct pci_device *dev, uint32_t bar);
};
struct dkm_dma_api {
int (*alloc)(uint64_t size, uint32_t flags, struct dkm_dma_buffer *out);
void (*free)(struct dkm_dma_buffer *buf);
int (*sync_for_device)(struct dkm_dma_buffer *buf);
int (*sync_for_cpu)(struct dkm_dma_buffer *buf);
};
```
---
## 7. ELF64 .drv Loader
第一版仅支持 x86_64 ELF64 小型内核模型。
### 7.1 必须支持的 relocation
```text
R_X86_64_64
绝对 64-bit 地址重定位。
R_X86_64_RELATIVE
base + addend。
R_X86_64_GLOB_DAT
GOT 符号地址。
R_X86_64_JUMP_SLOT
PLT 函数地址。
```
### 7.2 建议支持的 relocation
```text
R_X86_64_PC32
32-bit PC-relative 调用。
R_X86_64_PLT32
PLT-relative 调用。
R_X86_64_32 / R_X86_64_32S
仅允许确认地址范围安全时使用。
```
### 7.3 第一版禁止
```text
TLS relocation
IFUNC
lazy binding
外部动态库依赖
用户态 libc 依赖
异常展开表强依赖
```
### 7.4 加载流程
```text
DKM_ELF_LOAD(path_or_boot_module)
1. 读取 ELF header。
2. 校验 ELFCLASS64 / EM_X86_64 / endian / ABI。
3. 读取 program headers 或 section headers。
4. 计算模块内存大小和对齐。
5. 通过 UTSM 创建 code/data 段。
6. 加载 PT_LOAD 或 SHF_ALLOC section。
7. 清零 .bss。
8. 解析 symbol table。
9. 解析 relocation section。
10. 解析内核导出符号和 dkm_kernel_api。
11. 执行 relocation。
12. 查找 driver_desc / driver_init / driver_exit。
13. 设置 code 段 READ|EXEC|SEALED。
14. 设置 data 段 READ|WRITE|STRONG_RECOVERY。
15. 返回 module handle。
```
---
## 8. manifest.json 格式
路径:
```text
SYSTEM/driver/manifest.json
```
顶层结构:
```json
{
"schema": "deshab.driver.manifest.v1",
"root": "SYSTEM",
"driverRoot": "SYSTEM/driver",
"format": "elf64-drv",
"abi": {},
"policy": {},
"stages": []
}
```
驱动项示例:
```json
{
"name": "pci",
"path": "driver/bus/pci.drv",
"class": "bus",
"required": true,
"depends": ["irq"],
"provides": ["pci"]
}
```
路径规则:
```text
manifest 中的 path 相对 SYSTEM 根目录。
SYSTEM/driver/manifest.json 内的 driver/bus/pci.drv 指向 SYSTEM/driver/bus/pci.drv。
stage0/stage1 驱动允许来自 boot module。
stage2/stage3 驱动默认从 SYSTEM/driver 加载。
```
---
## 9. 驱动加载状态机
正常加载:
```text
DISCOVERED
-> QUEUED
-> DEP_WAIT
-> LOADING
-> ELF_CHECKED
-> MEMORY_ALLOCATED
-> RELOCATED
-> ABI_CHECKED
-> INITING
-> ACTIVE
```
失败状态:
```text
FAILED_ELF
FAILED_RELOCATION
FAILED_DEPENDENCY
FAILED_ABI
FAILED_INIT
FAILED_REQUIRED
```
卸载状态:
```text
ACTIVE
-> QUIESCING
-> EXITING
-> UNLOADING
-> UNLOADED
```
DRR 恢复状态:
```text
ACTIVE
-> SUSPENDED_BY_DRR
-> RECOVERING
-> REINITING
-> ACTIVE
```
---
## 10. DSM 加载策略
```text
DSM_LOAD_ALL()
1. 读取 SYSTEM/driver/manifest.json。
2. 校验 schema / abi / policy。
3. 按 stage 顺序加载。
4. 每个 stage 内按 priority 排序。
5. 检查 depends 是否已由 provides 满足。
6. 未满足依赖的驱动进入 DEP_WAIT。
7. 每轮加载成功后更新 provides set。
8. 一轮后仍 DEP_WAIT 的 required 驱动进入 FAILED_REQUIRED。
9. optional 驱动失败只记录状态。
```
required 失败策略:
```text
stage0/stage1 required 失败:
DRR report_fault -> boot fallback -> panic / system rollback
stage2 required 失败:
DRR recovery -> fallback 到 bootfs 或只读模式
stage3 optional 失败:
mark FAILED -> continue boot
```
---
## 11. stage0/stage1 boot module 方案
stage0/stage1 不应依赖普通 VFS。
bootloader 应预加载:
```text
SYSTEM/driver/manifest.json
SYSTEM/driver/platform/timer.drv
SYSTEM/driver/platform/acpi.drv
SYSTEM/driver/platform/apic.drv
SYSTEM/driver/bus/pci.drv
SYSTEM/driver/console/console_fb.drv
SYSTEM/driver/fs/bootfs.drv
SYSTEM/driver/test.fat32
```
可选预加载:
```text
SYSTEM/driver/block/ahci.drv
SYSTEM/driver/block/nvme.drv
SYSTEM/driver/fs/vfs.drv
SYSTEM/driver/fs/devfs.drv
SYSTEM/driver/fs/fat32.drv
SYSTEM/driver/net/e1000.drv
SYSTEM/driver/net/virtio_net.drv
SYSTEM/driver/input/ps2kbd.drv
```
boot module 表:
```c
struct dkm_boot_module {
const char *path;
void *base;
uint64_t size;
uint32_t type;
uint32_t flags;
uint64_t checksum;
};
struct dkm_boot_module_table {
uint32_t count;
struct dkm_boot_module modules[];
};
```
查找规则:
```text
DSM_LOAD_DRIVER(path)
1. 如果 path 在 boot module table 中,直接从内存加载。
2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。
3. 否则返回 BOOT_MODULE_NOT_FOUND。
```
---
## 12. 与 UTSM / DRR 的关系
### 12.1 UTSM 内存策略
```text
驱动 text:READ | EXEC | SEALED
驱动 rodata:READ | SEALED
驱动 data:READ | WRITE | STRONG_RECOVERY
驱动 bss:READ | WRITE | STRONG_RECOVERY
驱动 DMA:DMA | PINNED,必要时 CIPHERTEXT_DMA 或 NO_ENCRYPT
```
### 12.2 DRR 记录项
```text
driver name
stage
state
module memory range
provided services
recovery ops
init result
last fault
```
### 12.3 recovery ops
```c
struct dkm_driver_recovery_ops {
int (*quiesce)(void *ctx);
int (*reset)(void *ctx);
int (*reinit)(void *ctx);
int (*dump_state)(void *ctx, void *buf, uint64_t len);
};
```
DRR recovery 顺序:
```text
quiesce -> UTSM rollback -> device reset -> driver reinit -> resume
```
---
## 13. 第一版实现清单
```text
1. manifest.json parser
2. boot module table lookup
3. ELF64 header 校验
4. ELF64 section/program loading
5. relocation 处理
6. driver_desc 校验
7. kernel_api table 初始化
8. depends/provides resolver
9. staged loader
10. driver state table
11. DRR driver fault reporting
12. UTSM module memory allocation
```
---
## 14. 相关文件
```text
SYSTEM/driver/manifest.json
驱动启动清单。
RE/驱动模块ABI设计.md
DKM ABI 详细设计。
CODE/UTSM/README.md
UTSM 与驱动加载的整合说明。
```
-478
View File
@@ -1,478 +0,0 @@
/* DKM ACPI Driver - RSDP scan / RSDT parse / table enumeration
* Stage 0, optional, provides "acpi".
* Scans BIOS memory for RSDP, then walks RSDT/XSDT to list all ACPI tables.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
/* ---------------------------------------------------------------
* ACPI structures
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
char signature[8];
u8 checksum;
char oem_id[6];
u8 revision;
u32 rsdt_addr;
} acpi_rsdp_v1;
typedef struct __attribute__((packed)) {
/* RSDP v1 fields */
char signature[8];
u8 checksum;
char oem_id[6];
u8 revision;
u32 rsdt_addr;
/* RSDP v2+ fields */
u32 length;
u64 xsdt_addr;
u8 ext_checksum;
u8 reserved[3];
} acpi_rsdp_v2;
typedef struct __attribute__((packed)) {
char signature[4];
u32 length;
u8 revision;
u8 checksum;
char oem_id[6];
char oem_table_id[8];
u32 oem_revision;
u32 creator_id;
u32 creator_revision;
} acpi_sdt_header;
/* ACPI Generic Address Structure (GAS) — HPET 等表使用 */
typedef struct __attribute__((packed)) {
u8 space_id; /* 0=SystemMemory, 1=SystemIO, 2=PCIConfig, ... */
u8 bit_width;
u8 bit_offset;
u8 access_size; /* 0=undefined, 1=byte, 2=word, 3=dword, 4=qword */
u64 address;
} acpi_generic_address;
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_provides[] = { "acpi" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "acpi",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 1, /* DKM_CLASS_PLATFORM */
.stage = 0,
.flags = 0, /* optional */
.priority = 0,
.depends = NULL,
.depends_count = 0,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* Helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static u32 min_u32(u32 a, u32 b) { return a < b ? a : b; }
static int memneq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
if (a[i] != b[i]) return 1;
}
return 0;
}
static u8 checksum8(const u8 *data, u32 len) {
u8 sum = 0;
for (u32 i = 0; i < len; i++) {
sum += data[i];
}
return sum;
}
static void log_hex(const char *prefix, u64 val) {
static const char hex[] = "0123456789abcdef";
char buf[32];
u32 pos = 0;
while (pos < 30) { buf[pos] = 0; pos++; }
pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
}
buf[pos] = 0;
/* manual concat via serial_write-like approach: pass prefix then buf */
g_log->info(prefix);
g_log->info(buf);
}
/* ---------------------------------------------------------------
* RSDP scan
* --------------------------------------------------------------- */
static const acpi_rsdp_v2 *acpi_find_rsdp(const void *limine_rsdp) {
/* If Limine gave us the RSDP, use it directly */
if (limine_rsdp) {
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)limine_rsdp;
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
if (checksum8((const u8 *)rsdp, 20) == 0) {
return rsdp;
}
}
}
/* EBDA: extended BIOS data area, base at 0x40E */
{
u16 ebda_seg = *(const volatile u16 *)(uintptr_t)0x40E;
u64 ebda_base = (u64)ebda_seg << 4;
if (ebda_base >= 0x80000ULL && ebda_base < 0x100000ULL) {
for (u64 off = 0; off < 1024; off += 16) {
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)(ebda_base + off);
if (off + sizeof(acpi_rsdp_v1) > 1024) break;
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
if (checksum8((const u8 *)rsdp, 20) == 0) {
return rsdp;
}
}
}
}
}
/* BIOS read-only area: 0x000E0000 - 0x000FFFFF */
for (u64 addr = 0x000E0000ULL; addr < 0x00100000ULL; addr += 16) {
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)addr;
if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) {
if (checksum8((const u8 *)rsdp, 20) == 0) {
return rsdp;
}
}
}
return NULL;
}
/* ---------------------------------------------------------------
* MCFG (PCIe ECAM Memory-mapped Configuration) 解析
*
* **QEMU 无法测试但真机必需**: PCIe 配置空间通过 ECAM 访问,
* MCFG 表提供 ECAM 基址和总线范围。没有 MCFG 解析,
* 真机无法访问 Bus 0 以外的 PCIe 设备配置空间。
* QEMU 的 PCI 设备都在 Bus 0, 使用传统 CF8/CFC 端口即可。
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
u64 base_address; /* ECAM 基址 (4K 对齐, 256MB per bus) */
u16 pci_segment_group; /* PCI 段组号 (通常为 0) */
u8 start_bus_number; /* 起始总线号 */
u8 end_bus_number; /* 结束总线号 */
u32 reserved;
} mcfg_entry;
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u64 reserved; /* 保留, 通常为 0 */
/* mcfg_entry entries[] 紧跟 */
} mcfg_table;
static void acpi_parse_mcfg(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(mcfg_table)) return;
const mcfg_table *mcfg = (const mcfg_table *)hdr;
u32 entry_size = sizeof(mcfg_entry);
u32 entries_len = mcfg->hdr.length - sizeof(mcfg_table);
u32 count = entries_len / entry_size;
g_log->info("[acpi] MCFG entries:");
log_hex("[acpi] count=", count);
for (u32 i = 0; i < count; i++) {
const mcfg_entry *e = (const mcfg_entry *)((const u8 *)mcfg + sizeof(mcfg_table) + i * entry_size);
log_hex("[acpi] MCFG base=", e->base_address);
log_hex("[acpi] MCFG seg=", e->pci_segment_group);
log_hex("[acpi] MCFG bus_start=", e->start_bus_number);
log_hex("[acpi] MCFG bus_end=", e->end_bus_number);
/* 验证 ECAM 基址合理性 */
if (e->base_address == 0 || e->base_address == 0xFFFFFFFFFFFFFFFFULL) {
g_log->warn("[acpi] MCFG entry has invalid base address");
continue;
}
/* 真机: ECAM 基址通常在 0xB0000000 - 0xBFFFFFFF (3G-4G 范围),
* 但也可能在 4G 以上 (特别是大内存系统) */
if (e->base_address >= 0x100000000ULL) {
g_log->info("[acpi] MCFG base above 4G — requires MMIO mapping");
}
}
}
/* ---------------------------------------------------------------
* HPET (High Precision Event Timer) 解析
*
* 真机需要 HPET 替代 PIT 作为高精度定时器源。
* QEMU 默认也提供 HPET, 但当前内核使用 PIT 即可。
* HPET 信息留作后续高精度定时器实现的参考。
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
u8 hardware_rev_id;
u8 comparator_count : 5;
u8 counter_size : 1; /* 0=32-bit, 1=64-bit */
u8 reserved0 : 1;
u8 legacy_replacement : 1; /* 1=LegacyReplacement IRQ routing available */
u16 pci_vendor_id;
acpi_generic_address address; /* HPET 寄存器基址 */
u8 hpet_number;
u16 minimum_tick;
u8 page_protection;
} hpet_entry_data;
/* HPET 表不含子条目, 只有 1 个 data block */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 event_timer_block_id; /* 与 hpet_entry_data 的前 4 字节相同 */
acpi_generic_address base_address;
u8 hpet_number;
u16 minimum_tick;
u8 page_protection;
} hpet_table;
static void acpi_parse_hpet(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(hpet_table)) return;
const hpet_table *hpet = (const hpet_table *)hdr;
u32 block_id = hpet->event_timer_block_id;
u8 comp_count = (block_id >> 8) & 0x1f;
u8 count_size = (block_id >> 13) & 1;
u8 legacy = (block_id >> 15) & 1;
u8 rev_id = block_id & 0xff;
u16 vendor_id = (block_id >> 16) & 0xffff;
g_log->info("[acpi] HPET info:");
log_hex("[acpi] rev_id=", rev_id);
log_hex("[acpi] comparators=", comp_count);
log_hex("[acpi] counter_size=", count_size ? 64 : 32);
log_hex("[acpi] legacy_irq=", legacy);
log_hex("[acpi] vendor_id=", vendor_id);
log_hex("[acpi] min_tick=", hpet->minimum_tick);
/* HPET 地址空间类型 */
if (hpet->base_address.space_id == 0) {
/* System Memory — MMIO 访问 */
u64 addr = hpet->base_address.address;
log_hex("[acpi] HPET MMIO addr=", addr);
if (addr >= 0x100000000ULL) {
g_log->info("[acpi] HPET base above 4G — requires MMIO mapping");
}
} else if (hpet->base_address.space_id == 1) {
/* System I/O — 端口 I/O */
log_hex("[acpi] HPET I/O port=", hpet->base_address.address);
}
}
/* ---------------------------------------------------------------
* MADT (APIC) 解析 — 为 apic 驱动提供数据
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 lapic_addr;
u32 flags;
} acpi_madt;
static void acpi_parse_madt(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(acpi_madt)) return;
const acpi_madt *madt = (const acpi_madt *)hdr;
g_log->info("[acpi] MADT detail:");
log_hex("[acpi] LAPIC addr=", madt->lapic_addr);
log_hex("[acpi] flags=", madt->flags);
if (madt->flags & 1) {
g_log->info("[acpi] PCAT_COMPAT=1 (8259 PIC present)");
}
/* 枚举 MADT 子条目做汇总统计 */
const u8 *base = (const u8 *)madt;
u32 off = sizeof(acpi_madt);
u32 lapic_count = 0, ioapic_count = 0, iso_count = 0, nmi_count = 0;
while (off + 2 <= madt->hdr.length) {
const u8 *entry = base + off;
u8 type = entry[0];
u8 len = entry[1];
if (len < 2 || off + len > madt->hdr.length) break;
if (type == 0) lapic_count++;
else if (type == 1) ioapic_count++;
else if (type == 2) iso_count++;
else if (type == 3 || type == 4) nmi_count++;
off += len;
}
log_hex("[acpi] LAPIC entries=", lapic_count);
log_hex("[acpi] IOAPIC entries=", ioapic_count);
log_hex("[acpi] ISO entries=", iso_count);
log_hex("[acpi] NMI entries=", nmi_count);
}
/* ---------------------------------------------------------------
* Table enumeration
* --------------------------------------------------------------- */
static void acpi_enumerate_rsdt(const acpi_sdt_header *rsdt, u32 entry_size) {
u32 entry_count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size;
const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header);
g_log->info("[acpi] tables found");
for (u32 i = 0; i < entry_count; i++) {
u64 addr;
if (entry_size == 4) {
addr = (u64)((const u32 *)entries)[i];
} else {
addr = ((const u64 *)entries)[i];
}
if (addr == 0) continue;
const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr;
char sig[5];
sig[0] = hdr->signature[0];
sig[1] = hdr->signature[1];
sig[2] = hdr->signature[2];
sig[3] = hdr->signature[3];
sig[4] = 0;
g_log->info("[acpi] ");
g_log->info(sig);
log_hex("[acpi] addr=", addr);
log_hex("[acpi] len =", (u64)hdr->length);
/* 对关键表做详细解析 */
if (sig[0]=='A' && sig[1]=='P' && sig[2]=='I' && sig[3]=='C') {
acpi_parse_madt(hdr);
} else if (sig[0]=='M' && sig[1]=='C' && sig[2]=='F' && sig[3]=='G') {
acpi_parse_mcfg(hdr);
} else if (sig[0]=='H' && sig[1]=='P' && sig[2]=='E' && sig[3]=='T') {
acpi_parse_hpet(hdr);
}
}
}
/* ---------------------------------------------------------------
* Driver entry points
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) {
return -1;
}
g_log = api->log;
g_log->info("[acpi] scanning for RSDP");
const acpi_rsdp_v2 *rsdp = acpi_find_rsdp(api->rsdp_address);
if (!rsdp) {
g_log->error("[acpi] RSDP not found");
return 0; /* optional driver, don't fail boot */
}
g_log->info("[acpi] RSDP found");
log_hex("[acpi] revision =", (u64)rsdp->revision);
g_log->info("[acpi] oem_id");
{
char oem[7];
for (int i = 0; i < 6; i++) oem[i] = rsdp->oem_id[i];
oem[6] = 0;
g_log->info(oem);
}
if (rsdp->revision >= 2 && rsdp->xsdt_addr) {
g_log->info("[acpi] using XSDT");
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
acpi_enumerate_rsdt(xsdt, 8);
} else if (rsdp->rsdt_addr) {
g_log->info("[acpi] using RSDT");
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->rsdt_addr;
acpi_enumerate_rsdt(rsdt, 4);
} else {
g_log->warn("[acpi] no RSDT/XSDT address");
}
g_log->info("[acpi] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-712
View File
@@ -1,712 +0,0 @@
/* DKM AHCI Driver — PCI AHCI discovery and ABAR read-only enumeration
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
* This first version does not issue DMA commands; it only discovers the HBA
* and ports so later block I/O can be added safely.
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(ahci_port_found);
DKM_STAT_DECL(ahci_identify_ok);
DKM_STAT_DECL(ahci_block_reads);
DKM_STAT_DECL(ahci_block_writes);
DKM_TS_DECL(ts_abar);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "ahci",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
.flags = 0, /* optional */
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
#define AHCI_CLASS_STORAGE 0x01
#define AHCI_SUBCLASS_SATA 0x06
#define AHCI_PROGIF_AHCI 0x01
#define HBA_CAP 0x00
#define HBA_GHC 0x04
#define HBA_IS 0x08
#define HBA_PI 0x0C
#define HBA_VS 0x10
#define HBA_CAP2 0x24
#define HBA_BOHC 0x28
#define HBA_PORT_BASE 0x100
#define HBA_PORT_SIZE 0x80
#define PxCLB 0x00
#define PxFB 0x08
#define PxIS 0x10
#define PxIE 0x14
#define PxCMD 0x18
#define PxTFD 0x20
#define PxSIG 0x24
#define PxSSTS 0x28
#define PxSCTL 0x2C
#define PxSERR 0x30
#define PxSACT 0x34
#define PxCI 0x38
#define PxSNTF 0x3C
#define PxFBS 0x40
#define AHCI_CMD_ST (1u << 0)
#define AHCI_CMD_FRE (1u << 4)
#define AHCI_CMD_W (1u << 6)
#define AHCI_CMD_FR (1u << 14)
#define AHCI_CMD_CR (1u << 15)
#define AHCI_CMD_ICC_ACTIVE (1u << 28)
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_DMA_EXT 0x35
typedef struct __attribute__((packed)) ahci_cmd_header {
u16 flags;
u16 prdtl;
u32 prdbc;
u32 ctba;
u32 ctbau;
u32 reserved[4];
} ahci_cmd_header;
typedef struct __attribute__((packed)) ahci_prdt_entry {
u32 dba;
u32 dbau;
u32 reserved;
u32 dbc_i;
} ahci_prdt_entry;
typedef struct __attribute__((packed)) ahci_cmd_table {
u8 cfis[64];
u8 acmd[16];
u8 reserved[48];
ahci_prdt_entry prdt[1];
} ahci_cmd_table;
static const struct dkm_log_api *g_log;
static volatile u32 *g_abar;
static u32 g_ready_port = 0xffffffffu;
static ahci_cmd_header *g_cmd_header;
static struct dkm_dma_buffer g_cmd_table;
static struct dkm_dma_buffer g_data;
/* IDENTIFY 读出的用户可寻址扇区数;LBA48 优先,回退 LBA28 */
static u64 g_sector_count;
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
dkm_pci_write(bus, dev, func, reg, value);
}
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void ahci_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
/* PCI 规范允许 256 条 bus (0-255),实机 AHCI 可能位于 bus > 7 */
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = pci_read(bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == AHCI_CLASS_STORAGE &&
subclass == AHCI_SUBCLASS_SATA &&
prog_if == AHCI_PROGIF_AHCI) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
return 0;
}
}
}
}
return -1;
}
static const char *sata_sig_name(u32 sig) {
if (sig == 0x00000101u) return "SATA drive";
if (sig == 0xEB140101u) return "ATAPI device";
if (sig == 0xC33C0101u) return "Enclosure mgmt";
if (sig == 0x96690101u) return "Port multiplier";
if (sig == 0x00000000u) return "empty/unknown";
return "unknown signature";
}
static void ahci_log_port(u32 port) {
if (!g_abar || port >= 32) {
return;
}
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
u32 ssts = g_abar[(base + PxSSTS) / 4];
u32 sig = g_abar[(base + PxSIG) / 4];
u32 cmd = g_abar[(base + PxCMD) / 4];
u32 tfd = g_abar[(base + PxTFD) / 4];
u32 serr = g_abar[(base + PxSERR) / 4];
u32 ci = g_abar[(base + PxCI) / 4];
u32 sact = g_abar[(base + PxSACT) / 4];
u32 det = ssts & 0xf;
u32 spd = (ssts >> 4) & 0xf;
u32 ipm = (ssts >> 8) & 0xf;
log_hex("[ahci] port=", port);
log_hex("[ahci] SSTS=", ssts);
log_hex("[ahci] DET=", det);
log_hex("[ahci] SPD=", spd);
log_hex("[ahci] IPM=", ipm);
log_hex("[ahci] SIG=", sig);
g_log->info("[ahci] type");
g_log->info(sata_sig_name(sig));
log_hex("[ahci] CMD=", cmd);
log_hex("[ahci] TFD=", tfd);
log_hex("[ahci] SERR=", serr);
log_hex("[ahci] CI=", ci);
log_hex("[ahci] SACT=", sact);
}
static int ahci_stop_port(u32 port) {
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
u32 cmd = g_abar[(base + PxCMD) / 4];
cmd &= ~AHCI_CMD_ST;
g_abar[(base + PxCMD) / 4] = cmd;
/* 实机: 500ms TSC 超时等待 CR 清除 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
while (dkm_rdtsc() < deadline) {
cmd = g_abar[(base + PxCMD) / 4];
if (!(cmd & AHCI_CMD_CR)) break;
__asm__ volatile("pause");
}
cmd &= ~AHCI_CMD_FRE;
g_abar[(base + PxCMD) / 4] = cmd;
/* 实机: 500ms TSC 超时等待 FR 清除 */
deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
while (dkm_rdtsc() < deadline) {
cmd = g_abar[(base + PxCMD) / 4];
if (!(cmd & AHCI_CMD_FR)) return 0;
__asm__ volatile("pause");
}
return -1;
}
static int ahci_start_port(u32 port) {
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
u32 cmd = g_abar[(base + PxCMD) / 4];
cmd |= AHCI_CMD_FRE;
g_abar[(base + PxCMD) / 4] = cmd;
cmd |= AHCI_CMD_ST;
g_abar[(base + PxCMD) / 4] = cmd;
return 0;
}
static void ahci_fill_identify_fis(u8 *fis, u8 command) {
for (u32 i = 0; i < 64; i++) fis[i] = 0;
fis[0] = 0x27; /* Register H2D FIS */
fis[1] = 1u << 7; /* command */
fis[2] = command;
fis[7] = 1u << 6; /* LBA mode */
}
static void ahci_fill_read_fis(u8 *fis, u64 lba, u32 count) {
ahci_zero(fis, 64);
fis[0] = 0x27;
fis[1] = 1u << 7;
fis[2] = ATA_CMD_READ_DMA_EXT;
fis[4] = (u8)(lba & 0xff);
fis[5] = (u8)((lba >> 8) & 0xff);
fis[6] = (u8)((lba >> 16) & 0xff);
fis[7] = (1u << 6) | (u8)((lba >> 24) & 0x0f);
fis[8] = (u8)((lba >> 24) & 0xff);
fis[9] = (u8)((lba >> 32) & 0xff);
fis[10] = (u8)((lba >> 40) & 0xff);
fis[12] = (u8)(count & 0xff);
fis[13] = (u8)((count >> 8) & 0xff);
}
static void ahci_fill_write_fis(u8 *fis, u64 lba, u32 count) {
ahci_zero(fis, 64);
fis[0] = 0x27;
fis[1] = 1u << 7;
fis[2] = ATA_CMD_WRITE_DMA_EXT;
fis[4] = (u8)(lba & 0xff);
fis[5] = (u8)((lba >> 8) & 0xff);
fis[6] = (u8)((lba >> 16) & 0xff);
fis[7] = (1u << 6) | (u8)((lba >> 24) & 0x0f);
fis[8] = (u8)((lba >> 24) & 0xff);
fis[9] = (u8)((lba >> 32) & 0xff);
fis[10] = (u8)((lba >> 40) & 0xff);
fis[12] = (u8)(count & 0xff);
fis[13] = (u8)((count >> 8) & 0xff);
}
static int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data, u64 lba, u32 count, void *buffer) {
if (!buffer || count == 0) return -1;
if (count > 8) return -2;
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
ahci_cmd_table *tbl = (ahci_cmd_table *)table->virt;
u32 bytes = count * 512;
ahci_zero(tbl, sizeof(ahci_cmd_table));
ahci_zero(data->virt, bytes);
hdr[0].flags = 5;
hdr[0].prdtl = 1;
hdr[0].prdbc = 0;
hdr[0].ctba = (u32)(table->phys & 0xffffffffu);
hdr[0].ctbau = (u32)(table->phys >> 32);
tbl->prdt[0].dba = (u32)(data->phys & 0xffffffffu);
tbl->prdt[0].dbau = (u32)(data->phys >> 32);
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
ahci_fill_read_fis(tbl->cfis, lba, count);
g_abar[(base + PxIS) / 4] = 0xffffffffu;
g_abar[HBA_IS / 4] = (1u << port);
g_abar[(base + PxCI) / 4] = 1;
/* TSC deadline 超时: 5 秒,实机机械 HDD 首次寻道需数百 ms */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
u8 *dst = (u8 *)buffer;
u8 *src = (u8 *)data->virt;
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
return 0;
}
__asm__ volatile("pause");
}
g_log->warn("[ahci] READ timeout");
log_hex("[ahci] READ lba=", lba);
log_hex("[ahci] READ count=", count);
log_hex("[ahci] READ CI=", g_abar[(base + PxCI) / 4]);
log_hex("[ahci] READ IS=", g_abar[(base + PxIS) / 4]);
log_hex("[ahci] READ TFD=", g_abar[(base + PxTFD) / 4]);
return -3;
}
static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data, u64 lba, u32 count, const void *buffer) {
if (!buffer || count == 0) return -1;
if (count > 8) return -2;
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
ahci_cmd_table *tbl = (ahci_cmd_table *)table->virt;
u32 bytes = count * 512;
ahci_zero(tbl, sizeof(ahci_cmd_table));
/* copy data from user buffer into DMA buffer */
{
u8 *dst = (u8 *)data->virt;
const u8 *src = (const u8 *)buffer;
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
}
/* BUG-P0-2: 写入前必须 flush CPU cache,否则 DMA 控制器从物理内存
* 读到陈旧数据(CPU write-back cache 尚未写回)。实机必现。 */
__asm__ volatile("wbinvd" ::: "memory");
hdr[0].flags = 5 | (1u << 6); /* CFL=5, W=1 (write) */
hdr[0].prdtl = 1;
hdr[0].prdbc = 0;
hdr[0].ctba = (u32)(table->phys & 0xffffffffu);
hdr[0].ctbau = (u32)(table->phys >> 32);
tbl->prdt[0].dba = (u32)(data->phys & 0xffffffffu);
tbl->prdt[0].dbau = (u32)(data->phys >> 32);
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
ahci_fill_write_fis(tbl->cfis, lba, count);
/* P0 修复: 移除 PxCMD.W 写入。AHCI 规范中 PxCMD.W 是只读位,
* 由硬件根据命令头 flags.W 自动设置。软件写入此位违反规范,
* 可能在某些实机 AHCI 控制器上导致未定义行为。 */
/* (原代码: cmd |= AHCI_CMD_W; g_abar[...] = cmd; — 已删除) */
g_abar[(base + PxIS) / 4] = 0xffffffffu;
g_abar[HBA_IS / 4] = (1u << port);
g_abar[(base + PxCI) / 4] = 1;
/* P0 修复: 写入超时改为 TSC deadline,与读取路径一致。
* 固定循环在实机 HDD 上会因 CPU 频率差异导致提前超时或死循环。 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
/* BUG-P1-1 fix: CI 清除后检查 TFD 错误位。
* TFD 低字节是 STS,正常值 0x50 (DRDY|DRQ) 非零但不是错误。
* 只有 STS bit0 (ERR) 或 ERR 寄存器 (bits15:8) 非零才是真正错误。
* BUG-20260730-P14: 原 `tfd & 0xFF` 误判 STS=0x50 为错误,导致写入
* 实际成功却被报告为失败,FAT32 写路径提前返回错误码。 */
u32 tfd = g_abar[(base + PxTFD) / 4];
u32 is = g_abar[(base + PxIS) / 4];
u8 sts = (u8)(tfd & 0xFF);
u8 err_reg = (u8)((tfd >> 8) & 0xFF);
if ((sts & 0x01) || err_reg) {
g_log->warn("[ahci] write error: TFD ERR");
log_hex("[ahci] TFD=", tfd);
log_hex("[ahci] PxIS=", is);
return -4;
}
return 0;
}
__asm__ volatile("pause");
}
g_log->warn("[ahci] WRITE timeout");
log_hex("[ahci] WRITE lba=", lba);
log_hex("[ahci] WRITE count=", count);
log_hex("[ahci] WRITE CI=", g_abar[(base + PxCI) / 4]);
log_hex("[ahci] WRITE IS=", g_abar[(base + PxIS) / 4]);
log_hex("[ahci] WRITE TFD=", g_abar[(base + PxTFD) / 4]);
return -3;
}
static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
DKM_STAT_INC(ahci_block_reads);
log_hex("[ahci] block_read lba=", lba);
log_hex("[ahci] block_read count=", count);
while (count > 0) {
u32 chunk = count > 8 ? 8 : count;
int status = ahci_read_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer);
if (status != 0) return status;
lba += chunk;
count -= chunk;
buffer = (void *)((u8 *)buffer + (chunk * 512));
}
return 0;
}
static int ahci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
DKM_STAT_INC(ahci_block_writes);
/* ensure port is in ST+FRE state */
u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE;
u32 cmd = g_abar[(base + PxCMD) / 4];
if (!(cmd & AHCI_CMD_ST) || !(cmd & AHCI_CMD_FRE)) {
g_log->warn("[ahci] port not ready; restarting");
ahci_stop_port(g_ready_port);
ahci_start_port(g_ready_port);
}
while (count > 0) {
u32 chunk = count > 8 ? 8 : count;
int status = ahci_write_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer);
if (status != 0) return status;
lba += chunk;
count -= chunk;
buffer = (const void *)((const u8 *)buffer + (chunk * 512));
}
return 0;
}
static int ahci_read_lba0(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data) {
int status = ahci_read_blocks_internal(port, hdr, table, data, 0, 1, data->virt);
if (status == 0) {
log_hex("[ahci] READ LBA0 port=", port);
log_hex("[ahci] READ LBA0 bytes=", hdr[0].prdbc);
log_hex("[ahci] READ LBA0 word0=", ((u16 *)data->virt)[0]);
log_hex("[ahci] READ LBA0 word255=", ((u16 *)data->virt)[255]);
return 1;
}
return status;
}
static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages) return -1;
u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE;
u32 ssts = g_abar[(base + PxSSTS) / 4];
u32 sig = g_abar[(base + PxSIG) / 4];
u32 det = ssts & 0xf;
u32 ipm = (ssts >> 8) & 0xf;
if (det != 3 || ipm != 1) return 0;
if (sig != 0x00000101u) {
g_log->info("[ahci] IDENTIFY skipped non-SATA port");
log_hex("[ahci] IDENTIFY skip port=", port);
log_hex("[ahci] IDENTIFY skip sig=", sig);
return 0;
}
struct dkm_dma_buffer clb;
struct dkm_dma_buffer fis;
struct dkm_dma_buffer table;
struct dkm_dma_buffer data;
if (dma->alloc_pages(1, 1024, 0x100000000ULL, &clb) != 0) return -2;
if (dma->alloc_pages(1, 256, 0x100000000ULL, &fis) != 0) return -3;
/* BUG-P0-1: sizeof(ahci_cmd_table)=144, 128B 缓冲区越界 16 字节。
* 改为 256B 满足对齐和大小要求,防止 ahci_zero 越界写。 */
if (dma->alloc_pages(1, 256, 0x100000000ULL, &table) != 0) return -4;
/* BUG-20260730-022: DMA data buffer must hold at least 8 sectors (4096B)
* for ahci_block_write chunked DMA. 512B caused 3584B out-of-bounds
* overwrite of adjacent kernel data on real hardware. */
if (dma->alloc_pages(1, 8192, 0x100000000ULL, &data) != 0) return -5;
/* P0 验证: DMA 缓冲区物理地址必须在 4GB 以下。
* AHCI 控制器 8086:2922 只支持 32 位 DMA,
* PRDT 地址超过 4GB 会导致 DMA 写入截断到错误物理地址。
* dma->alloc_pages 的上限参数 0x100000000ULL 已保证此约束,
* 但添加显式校验以防御 alloc_pages 实现变更。 */
if (clb.phys >= 0x100000000ULL || fis.phys >= 0x100000000ULL ||
table.phys >= 0x100000000ULL || data.phys >= 0x100000000ULL) {
g_log->warn("[ahci] DMA buffer above 4GB — 32-bit DMA will fail on this controller!");
log_hex("[ahci] clb.phys=", clb.phys);
log_hex("[ahci] fis.phys=", fis.phys);
log_hex("[ahci] table.phys=", table.phys);
log_hex("[ahci] data.phys=", data.phys);
}
if (ahci_stop_port(port) != 0) {
g_log->warn("[ahci] port stop timeout");
return -6;
}
g_abar[(base + PxCLB) / 4] = (u32)(clb.phys & 0xffffffffu);
g_abar[(base + PxCLB + 4) / 4] = (u32)(clb.phys >> 32);
g_abar[(base + PxFB) / 4] = (u32)(fis.phys & 0xffffffffu);
g_abar[(base + PxFB + 4) / 4] = (u32)(fis.phys >> 32);
ahci_cmd_header *hdr = (ahci_cmd_header *)clb.virt;
ahci_cmd_table *tbl = (ahci_cmd_table *)table.virt;
hdr[0].flags = 5; /* FIS length in DWORDS */
hdr[0].prdtl = 1;
hdr[0].ctba = (u32)(table.phys & 0xffffffffu);
hdr[0].ctbau = (u32)(table.phys >> 32);
tbl->prdt[0].dba = (u32)(data.phys & 0xffffffffu);
tbl->prdt[0].dbau = (u32)(data.phys >> 32);
tbl->prdt[0].dbc_i = (512 - 1) | (1u << 31);
u8 cmd = ATA_CMD_IDENTIFY;
ahci_fill_identify_fis(tbl->cfis, cmd);
g_abar[(base + PxIS) / 4] = 0xffffffffu;
g_abar[HBA_IS / 4] = (1u << port);
ahci_start_port(port);
g_abar[(base + PxCI) / 4] = 1;
/* BUG-P0-3: 固定 500 万次循环在 3GHz CPU 上仅 ~1-2ms,
* 不够 HDD 寻道。改为 TSC deadline 5s 超时,与 read/write 路径一致。 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
log_hex("[ahci] IDENTIFY port=", port);
log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]);
log_hex("[ahci] IDENTIFY bytes=", hdr[0].prdbc);
u16 *id = (u16 *)data.virt;
log_hex("[ahci] IDENTIFY word0=", id[0]);
log_hex("[ahci] IDENTIFY word49=", id[49]);
log_hex("[ahci] IDENTIFY word83=", id[83]);
/* 容量必须在 ahci_read_lba0 复用 data 缓冲区前提取,
* 否则 IDENTIFY 数据被 LBA0 内容覆盖后容量丢失。 */
if ((id[83] & 0x0400u) && (id[103] | id[102] | id[101] | id[100])) {
/* LBA48: word100-103 小端 u16 序列拼接 */
g_sector_count = (u64)id[100] | ((u64)id[101] << 16)
| ((u64)id[102] << 32) | ((u64)id[103] << 48);
} else {
/* LBA28: word60-61 */
g_sector_count = (u64)id[60] | ((u64)id[61] << 16);
}
log_hex("[ahci] IDENTIFY sector_count=", g_sector_count);
ahci_read_lba0(port, hdr, &table, &data);
g_ready_port = port;
g_cmd_header = hdr;
g_cmd_table = table;
g_data = data;
return 1;
}
__asm__ volatile("pause");
}
g_log->warn("[ahci] IDENTIFY timeout");
log_hex("[ahci] IDENTIFY CI=", g_abar[(base + PxCI) / 4]);
log_hex("[ahci] IDENTIFY IS=", g_abar[(base + PxIS) / 4]);
log_hex("[ahci] IDENTIFY TFD=", g_abar[(base + PxTFD) / 4]);
return -7;
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[ahci] init begin");
u8 bus = 0;
u8 dev = 0;
u8 func = 0;
if (ahci_find(&bus, &dev, &func) != 0) {
g_log->warn("[ahci] AHCI controller not found");
g_log->info("[ahci] driver ready");
return 0;
}
g_log->info("[ahci] AHCI controller found");
log_hex("[ahci] bus=", bus);
log_hex("[ahci] dev=", dev);
log_hex("[ahci] func=", func);
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[ahci] vendor=", vd & 0xffff);
log_hex("[ahci] device=", vd >> 16);
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[ahci] PCI command=", command);
/* 读取 ABAR: BAR5 可能是 64-bit MMIO BAR,高位在 BAR4 */
u32 bar5_lo = pci_read(bus, dev, func, PCI_BAR5);
u64 abar_phys;
if ((bar5_lo & 0x7) == 0x4) {
/* 64-bit MMIO BAR: 高 32 位在 BAR4 */
u32 bar5_hi = pci_read(bus, dev, func, PCI_BAR4);
abar_phys = ((u64)bar5_hi << 32) | (bar5_lo & 0xFFFFFFF0u);
g_log->info("[ahci] ABAR is 64-bit MMIO BAR");
} else {
abar_phys = bar5_lo & 0xFFFFFFF0u;
}
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[ahci] ABAR phys=", abar_phys);
log_hex("[ahci] PCI IRQ line=", irq_line);
if (!api->hhdm_offset || !abar_phys) {
g_log->warn("[ahci] missing HHDM or ABAR; skip MMIO enumeration");
g_log->info("[ahci] driver ready");
return 0;
}
g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys);
DKM_TS_BEGIN(ts_abar);
log_hex("[ahci] HHDM offset=", api->hhdm_offset);
u32 cap = g_abar[HBA_CAP / 4];
u32 ghc = g_abar[HBA_GHC / 4];
u32 is = g_abar[HBA_IS / 4];
u32 pi = g_abar[HBA_PI / 4];
u32 vs = g_abar[HBA_VS / 4];
u32 cap2 = g_abar[HBA_CAP2 / 4];
u32 bohc = g_abar[HBA_BOHC / 4];
log_hex("[ahci] CAP=", cap);
log_hex("[ahci] GHC=", ghc);
log_hex("[ahci] IS=", is);
log_hex("[ahci] PI=", pi);
log_hex("[ahci] VS=", vs);
log_hex("[ahci] CAP2=", cap2);
log_hex("[ahci] BOHC=", bohc);
/* AHCI 1.3+ BOHC handoff: 如果 BIOS 仍拥有控制器, 请求所有权 */
if (bohc & 0x01) {
g_log->info("[ahci] BIOS owns HBA, requesting handoff");
g_abar[HBA_BOHC / 4] = bohc | 0x02; /* set OOS */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
if (!(g_abar[HBA_BOHC / 4] & 0x01)) break;
__asm__ volatile("pause");
}
if (g_abar[HBA_BOHC / 4] & 0x01) {
g_log->warn("[ahci] BIOS handoff timeout, proceeding anyway");
} else {
g_log->info("[ahci] BIOS handoff complete");
}
}
log_hex("[ahci] ports count=", (cap & 0x1f) + 1);
log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1);
DKM_TS_END(ts_abar, g_log->info, "ahci_abar_mmio");
u32 implemented = 0;
for (u32 port = 0; port < 32; port++) {
if (pi & (1u << port)) {
implemented++;
DKM_STAT_INC(ahci_port_found);
ahci_log_port(port);
}
}
log_hex("[ahci] implemented ports=", implemented);
u32 identified = 0;
if (api->dma) {
for (u32 port = 0; port < 32; port++) {
if (pi & (1u << port)) {
int ident = ahci_identify_port(port, api->dma);
if (ident > 0) {
identified++;
DKM_STAT_INC(ahci_identify_ok);
break;
}
}
}
} else {
g_log->warn("[ahci] DMA API unavailable; skip IDENTIFY");
}
log_hex("[ahci] identified ports=", identified);
if (identified && api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "ahci0";
desc.sector_size = 512;
desc.sector_count = g_sector_count;
desc.ctx = 0;
desc.read = ahci_block_read;
desc.write = ahci_block_write;
int index = api->block->register_device(&desc);
log_hex("[ahci] block provider index=", (u64)(i64)index);
} else if (identified) {
g_log->warn("[ahci] block API unavailable; provider not registered");
}
g_log->info("[ahci] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-1562
View File
@@ -1,1562 +0,0 @@
/* DKM APIC Driver — APIC discovery + IOAPIC init + LAPIC enable + 路由接管
* Stage 0, required, depends on "timer", provides "irq" and "apic".
*
* 真机路径:
* 1. LAPIC 使能 + Spurious Interrupt Vector 注册
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
* (兼容 PIC 布局), 逐条目 mask
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
* 4. SIPI 多核启动框架 (预留: 调用 dkm_sipi_wake_ap 即 DKM-E03 panic,
* 见 CODE/UTSM/dkm/kernel_api.c 骨架)
*
* B7 路由接管 (apic_route):
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
* 从 8259 PIC 切换到 IOAPIC→LAPIC 路径:
* a. 经 kernel_api.register_apic_eoi 向 UTSM 注册 LAPIC EOI 钩子
* (B7 阶段2 接口; idt_handler 两个 IRQ 分支在分发末尾调用之)
* b. 注册 PIT tick 计数 handler 于 vector 0x20 — 即 idt_handler 的
* PIC 分支 (0x20-0x2F), 与键盘 0x21/鼠标 0x2C/网卡 0x2B 同一代码
* 路径; handler 返回 0 由钩子完成 LAPIC EOI, 重复投递 >=2 即证明
* "legacy vector 分支 + 钩子 EOI" 全链路可持续 (阶段1 用 0xE0 自
* EOI 只证明了 APIC 分支)
* c. 按 ISO 表解析 PIT GSI (QEMU q35: IRQ0→GSI2), 解屏蔽该条目
* d. LAPIC TPR=0 全放行, PIC IMR 全 1 屏蔽 (QEMU 无功能 IMCR 寄存器,
* PIC 全屏蔽即等效切换; 8259 与 IOAPIC 同时监听 IRQ 线, 全屏蔽避免双投递)
* e. PIT ch0 mode3 100Hz 产生真实硬件中断, 500ms 窗口内计数 >=2 次
* 即证明 IOAPIC→LAPIC→IDT→handler→钩子EOI 全链路
* f. 验证失败完整降级: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 handler
* 与 EOI 钩子, 系统行为与切换前完全一致
*
* B7 阶段2 (apic_route_legacy):
* EOI 钩子落地后, legacy IRQ1(键盘 0x21)/IRQ12(鼠标 0x2C)/IRQ11(e1000
* 0x2B) 重定向项可持续投递, 不再是 one-shot。ISA 线按 MADT ISO 极性/触发
* (缺省 edge/active-high); PCI 线 (IRQ11) 无 ISO 覆盖时强制 level/
* active-low (PCI INTx 规范; QEMU 内部 pci irq 电平语义与之匹配,
* Linux/Windows 同配置)。e1000 handler 读 ICR 清源后电平 deassert,
* 由钩子 EOI 完成 LAPIC 侧清扫。
*
* apic_diag=1 诊断:
* - HPET 绝对时基对拍: 解析 ACPI HPET 表 (QEMU q35 有, 100MHz 主计数器),
* 用主计数器实测 tsc_calibrate 的 10ms 窗口与 route 验证的 500ms 窗口,
* 定论 "tick 2x" 根因 (TSC 校准测半 vs PIT mode3 双触发)
* - PS/2 鼠标流: 初始化 AUX 端口 + 注册 IRQ12 诊断 handler, 配合
* QEMU monitor mouse_move 验证 IRQ12 持续投递 (计数 + 观察窗)
* diag 只增观测不改变量路由语义, 默认 0 关闭。
*
* QEMU 兼容:
* - QEMU 默认只提供 1 个 IOAPIC, 24 条 GSI
* - 实机可能有多个 IOAPIC, GSI 范围更大
* - 实机必须处理 MADT ISO (IRQ0→GSI2 等)
*/
#include <stdint.h>
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
const void *block;
const void *mmio;
void *(*mm_map_mmio)(u64 phys, u64 size);
void (*mm_unmap_mmio)(void *virt, u64 size);
/* B7 阶段2 尾部追加 (旧内核无此字段 -> 判空使用) */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3 尾部追加 (本驱动不使用, 保持布局与 UTSM 一致) */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
static const char *const g_depends[] = { "timer" };
static const char *const g_provides[] = { "irq", "apic" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "apic",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 3, /* DKM_CLASS_INTERRUPT */
.stage = 0,
.flags = 1, /* DKM_F_REQUIRED */
.priority = 0,
.depends = g_depends,
.depends_count = 1,
.provides = g_provides,
.provides_count = 2,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
typedef struct __attribute__((packed)) {
char signature[8];
u8 checksum;
char oem_id[6];
u8 revision;
u32 rsdt_addr;
} acpi_rsdp_v1;
typedef struct __attribute__((packed)) {
char signature[8];
u8 checksum;
char oem_id[6];
u8 revision;
u32 rsdt_addr;
u32 length;
u64 xsdt_addr;
u8 ext_checksum;
u8 reserved[3];
} acpi_rsdp_v2;
typedef struct __attribute__((packed)) {
char signature[4];
u32 length;
u8 revision;
u8 checksum;
char oem_id[6];
char oem_table_id[8];
u32 oem_revision;
u32 creator_id;
u32 creator_revision;
} acpi_sdt_header;
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 lapic_addr;
u32 flags;
} acpi_madt;
/* ACPI HPET 表 (signature "HPET") — apic_diag 绝对时基对拍用 */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 event_timer_block_id;
/* ACPI Generic Address Structure (GAS) */
u8 addr_space_id; /* 0 = system memory */
u8 register_bit_width;
u8 register_bit_offset;
u8 access_size;
u64 base_addr; /* HPET MMIO 物理基址 (QEMU: 0xFED00000) */
u8 hpet_number;
u16 min_clock_tick;
u8 page_protection;
} acpi_hpet;
/* Limine boot module 最小类型 (与 bootfs.drv / UTSM limine.h 布局一致) */
struct limine_file_min {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response_min {
u64 revision;
u64 module_count;
struct limine_file_min **modules;
};
/* ---- IOAPIC 信息存储(真机多 IOAPIC 支持) ---- */
#define MAX_IOAPIC 4
#define MAX_ISO 16
#define MAX_LAPIC 256
struct ioapic_info {
u8 id;
u32 gsi_base;
u32 gsi_count; /* 从 IOAPIC VER 寄存器读取: max_redir_entry + 1 */
u64 mmio_phys; /* IOAPIC MMIO 物理基址 */
volatile u32 *mmio; /* HHDM/MMIO 窗口映射后的虚拟地址, 0=未映射 */
};
struct iso_info {
u8 bus_source; /* ISA bus source IRQ (0-15) */
u32 gsi; /* 全局系统中断号 */
u16 flags; /* 极性/触发模式标志 */
};
struct lapic_entry {
u8 apic_id;
u32 flags; /* MADT LAPIC flags: bit0=enabled */
};
struct madt_info {
u64 lapic_phys;
u32 flags;
u32 lapic_count;
u32 ioapic_count;
u32 iso_count;
struct ioapic_info ioapics[MAX_IOAPIC];
struct iso_info isos[MAX_ISO];
struct lapic_entry lapics[MAX_LAPIC];
};
/* ---- B7 路由接管配置 (FUCK [drivers]) ---- */
struct apic_config {
int lapic_enable; /* lapic_enable, 默认 1 */
int ioapic_init; /* ioapic_init, 默认 1 */
int apic_route; /* apic_route, 默认 0 (PIC 路由, 现状) */
int apic_route_legacy; /* apic_route_legacy, 默认 0 */
int apic_diag; /* apic_diag, 默认 0 (HPET 校准/投递计数/鼠标流诊断) */
};
static const struct dkm_log_api *g_log;
static struct apic_config g_cfg = { 1, 1, 0, 0, 0 };
static volatile u32 *g_lapic; /* LAPIC MMIO 映射基址, 0=未映射 */
static u32 g_lapic_id; /* BSP Local APIC ID (IOAPIC 目的 ID) */
static volatile u32 g_tick_count; /* PIT tick 计数 (路由验证) */
static int g_route_active; /* 1=IOAPIC 路由已接管 */
static __inline__ void apic_mb(void) {
__asm__ volatile("" ::: "memory");
}
static int memeq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
if (a[i] != b[i]) return 0;
}
return 1;
}
static u32 str_len(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 在 NUL 结尾字符串中查找子串 (cmdline 匹配用) */
static int str_contains(const char *hay, const char *needle) {
u32 nlen = str_len(needle);
if (!nlen) return 1;
for (u32 i = 0; hay[i]; i++) {
u32 j = 0;
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
if (j == nlen) return 1;
}
return 0;
}
static u8 checksum8(const u8 *data, u32 len) {
u8 sum = 0;
for (u32 i = 0; i < len; i++) sum += data[i];
return sum;
}
static void log_hex(const char *prefix, u64 val) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void log_dec(const char *prefix, u64 val) {
char buf[24];
u32 pos = 0;
if (!val) {
buf[pos++] = '0';
} else {
char tmp[20];
u32 n = 0;
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
while (n) buf[pos++] = tmp[--n];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ---- I/O 端口 (PIC / PIT) ---- */
static __inline__ void outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u8 inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static __inline__ void io_wait(void) {
outb(0x80, 0);
}
static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
__asm__ volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(leaf), "c"(0));
}
static u64 rdmsr(u32 msr) {
u32 lo;
u32 hi;
__asm__ volatile ("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((u64)hi << 32) | lo;
}
static void wrmsr(u32 msr, u64 val) {
u32 lo = (u32)(val & 0xffffffffu);
u32 hi = (u32)(val >> 32);
__asm__ volatile("wrmsr" :: "a"(lo), "d"(hi), "c"(msr));
}
static int cpu_has_lapic(void) {
u32 a, b, c, d;
cpuid(1, &a, &b, &c, &d);
(void)a;
(void)b;
(void)c;
return (d & (1u << 9)) != 0;
}
/* ---- FUCK 配置自解析 (DKM 侧, 不依赖 UTSM ini_parser) ---- *
* UTSM 通过 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
* 此处实现最小分区感知 INI 扫描: 只找 [drivers] 分区下的目标键。
* 缺失文件/缺失键一律回退默认值, 行为与无配置时一致。
*/
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
u64 i = start;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
u64 tok_start = i;
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
u64 tok_len = i - tok_start;
const char *tok = line + tok_start;
if (!tok_len) return defval;
if (tok_len == 1 && tok[0] == '1') return 1;
if (tok_len == 1 && tok[0] == '0') return 0;
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
/* 十进制整数 (允许前导 '-') */
int neg = 0;
u64 j = 0;
if (tok[0] == '-') { neg = 1; j = 1; }
int val = 0;
int any = 0;
for (; j < tok_len; j++) {
if (tok[j] < '0' || tok[j] > '9') return defval;
val = val * 10 + (tok[j] - '0');
any = 1;
}
if (!any) return defval;
return neg ? -val : val;
}
static int fuck_get_int(const char *text, u64 size,
const char *section, const char *key, int defval) {
u64 pos = 0;
int in_section = 0;
u32 slen = str_len(section);
u32 klen = str_len(key);
while (pos < size) {
u64 end = pos;
while (end < size && text[end] != '\n') end++;
u64 line_len = end - pos;
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
const char *line = text + pos;
u64 i = 0;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
if (i < line_len) {
char c = line[i];
if (c == '#' || c == ';') {
/* 注释行, 跳过 */
} else if (c == '[') {
in_section = 0;
if (line_len >= (u64)(i + 1 + slen + 1) &&
memeq(line + i + 1, section, slen) &&
line[i + 1 + slen] == ']') {
in_section = 1;
}
} else if (in_section) {
if (line_len >= (u64)(i + klen) &&
memeq(line + i, key, klen)) {
u64 j = i + klen;
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
if (j < line_len && line[j] == '=') {
return parse_scalar(line, j + 1, line_len, defval);
}
}
}
}
pos = end + 1;
}
return defval;
}
static void load_fuck_config(const struct dkm_kernel_api *api) {
/* 编译期默认: 与 FUCK 注释承诺一致 (无配置时行为不变) */
g_cfg.lapic_enable = 1;
g_cfg.ioapic_init = 1;
g_cfg.apic_route = 0;
g_cfg.apic_route_legacy = 0;
g_cfg.apic_diag = 0;
const struct limine_module_response_min *resp =
(const struct limine_module_response_min *)api->boot_modules_response;
if (!resp || !resp->modules) {
g_log->warn("[apic] no boot modules; FUCK defaults in effect");
return;
}
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file_min *f = resp->modules[i];
if (!f || !f->cmdline || !f->address || !f->size) continue;
if (!str_contains(f->cmdline, "fuck:config")) continue;
const char *text = (const char *)f->address;
u64 size = f->size;
g_log->info("[apic] FUCK config module found");
log_hex("[apic] FUCK size=", size);
g_cfg.lapic_enable = fuck_get_int(text, size, "drivers", "lapic_enable", 1);
g_cfg.ioapic_init = fuck_get_int(text, size, "drivers", "ioapic_init", 1);
g_cfg.apic_route = fuck_get_int(text, size, "drivers", "apic_route", 0);
g_cfg.apic_route_legacy = fuck_get_int(text, size, "drivers", "apic_route_legacy", 0);
g_cfg.apic_diag = fuck_get_int(text, size, "drivers", "apic_diag", 0);
log_dec("[apic] cfg lapic_enable=", (u64)g_cfg.lapic_enable);
log_dec("[apic] cfg ioapic_init=", (u64)g_cfg.ioapic_init);
log_dec("[apic] cfg apic_route=", (u64)g_cfg.apic_route);
log_dec("[apic] cfg apic_route_legacy=", (u64)g_cfg.apic_route_legacy);
log_dec("[apic] cfg apic_diag=", (u64)g_cfg.apic_diag);
return;
}
g_log->warn("[apic] FUCK module (fuck:config) not found; defaults in effect");
}
static const acpi_sdt_header *find_table_from_rsdt(const acpi_sdt_header *rsdt, const char sig[4], u32 entry_size) {
if (!rsdt || rsdt->length < sizeof(acpi_sdt_header)) return NULL;
if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL;
u32 count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size;
const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header);
for (u32 i = 0; i < count; i++) {
u64 addr;
if (entry_size == 4) {
addr = (u64)((const u32 *)entries)[i];
} else {
addr = ((const u64 *)entries)[i];
}
if (!addr) continue;
const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr;
if (hdr->length >= sizeof(acpi_sdt_header) && memeq(hdr->signature, sig, 4)) {
if (checksum8((const u8 *)hdr, hdr->length) == 0) return hdr;
}
}
return NULL;
}
static const acpi_madt *find_madt(const void *rsdp_addr) {
if (!rsdp_addr) return NULL;
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)rsdp_addr;
if (!memeq(rsdp->signature, "RSD PTR ", 8)) return NULL;
if (checksum8((const u8 *)rsdp, 20) != 0) return NULL;
if (rsdp->revision >= 2 && rsdp->length >= sizeof(acpi_rsdp_v2) && rsdp->xsdt_addr) {
if (checksum8((const u8 *)rsdp, rsdp->length) == 0) {
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
const acpi_sdt_header *madt = find_table_from_rsdt(xsdt, "APIC", 8);
if (madt) return (const acpi_madt *)madt;
}
}
if (rsdp->rsdt_addr) {
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr;
const acpi_sdt_header *madt = find_table_from_rsdt(rsdt, "APIC", 4);
if (madt) return (const acpi_madt *)madt;
}
return NULL;
}
/* 查找 HPET 表 (apic_diag 用; 与 find_madt 同构, 仅签名不同) */
static const acpi_hpet *find_hpet(const void *rsdp_addr) {
if (!rsdp_addr) return NULL;
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)rsdp_addr;
if (!memeq(rsdp->signature, "RSD PTR ", 8)) return NULL;
if (checksum8((const u8 *)rsdp, 20) != 0) return NULL;
if (rsdp->revision >= 2 && rsdp->length >= sizeof(acpi_rsdp_v2) && rsdp->xsdt_addr) {
if (checksum8((const u8 *)rsdp, rsdp->length) == 0) {
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
const acpi_sdt_header *hpet = find_table_from_rsdt(xsdt, "HPET", 8);
if (hpet) return (const acpi_hpet *)hpet;
}
}
if (rsdp->rsdt_addr) {
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr;
const acpi_sdt_header *hpet = find_table_from_rsdt(rsdt, "HPET", 4);
if (hpet) return (const acpi_hpet *)hpet;
}
return NULL;
}
static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
const u8 *base = (const u8 *)madt;
u32 off = (u32)sizeof(acpi_madt);
while (off + 2 <= madt->hdr.length) {
const u8 *entry = base + off;
u8 type = entry[0];
u8 len = entry[1];
if (len < 2 || off + len > madt->hdr.length) {
g_log->warn("[apic] malformed MADT entry");
return;
}
if (type == 0 && len >= 8) {
/* LAPIC entry */
u8 apic_id = entry[3];
u32 flags = *(const u32 *)(const void *)(entry + 4);
log_hex("[apic] CPU LAPIC id=", apic_id);
if (flags & 1u) {
info->lapic_count++;
if (info->lapic_count <= MAX_LAPIC) {
info->lapics[info->lapic_count - 1].apic_id = apic_id;
info->lapics[info->lapic_count - 1].flags = flags;
}
}
} else if (type == 1 && len >= 12) {
/* IOAPIC entry */
u8 ioapic_id = entry[2];
u32 addr = *(const u32 *)(const void *)(entry + 4);
u32 gsi = *(const u32 *)(const void *)(entry + 8);
log_hex("[apic] IOAPIC id=", ioapic_id);
log_hex("[apic] IOAPIC addr=", addr);
log_hex("[apic] IOAPIC gsi_base=", gsi);
if (info->ioapic_count < MAX_IOAPIC) {
struct ioapic_info *io = &info->ioapics[info->ioapic_count];
io->id = ioapic_id;
io->gsi_base = gsi;
io->mmio_phys = (u64)addr;
io->mmio = (volatile u32 *)(uintptr_t)(u64)addr; /* HHDM 直映射, 后续映射修正 */
io->gsi_count = 0;
info->ioapic_count++;
}
} else if (type == 2 && len >= 10) {
/* ISO (Interrupt Source Override) entry */
u8 source = entry[3];
u32 gsi = *(const u32 *)(const void *)(entry + 4);
u16 flags = *(const u16 *)(const void *)(entry + 8);
info->iso_count++;
log_hex("[apic] ISO source=", source);
log_hex("[apic] ISO gsi=", gsi);
log_hex("[apic] ISO flags=", flags);
if (info->iso_count <= MAX_ISO) {
struct iso_info *iso = &info->isos[info->iso_count - 1];
iso->bus_source = source;
iso->gsi = gsi;
iso->flags = flags;
}
} else if (type == 5 && len >= 12) {
u64 override = *(const u64 *)(const void *)(entry + 4);
info->lapic_phys = override;
log_hex("[apic] LAPIC override=", override);
}
off += len;
}
}
/* ---- IOAPIC MMIO 访问 ---- *
* IOAPIC 寄存器通过 MMIO 访问, 地址空间为 IOREGSEL(0x00) + IOWIN(0x10).
* 真机 IOAPIC 地址通常在 0xFEC00000, HHDM 通常覆盖此低 4G 地址。
* 写入 IOREGSEL 选择寄存器号, 读/写 IOWIN 访问数据。
*/
#define IOAPIC_IOREGSEL 0x00
#define IOAPIC_IOWIN 0x10
static u32 ioapic_read(volatile u32 *base, u8 reg) {
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
apic_mb(); /* 寄存器选择必须先于数据读取 */
return base[IOAPIC_IOWIN / 4];
}
static void ioapic_write(volatile u32 *base, u8 reg, u32 val) {
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
apic_mb(); /* 寄存器选择必须先于数据写入 */
base[IOAPIC_IOWIN / 4] = val;
}
/* IOAPIC 重定向表项 (64-bit, 分两次 32-bit 读写, 82093AA 规范布局)
* bits 0-7: interrupt vector (0x20-0xFF)
* bits 8-10: delivery mode: 0=fixed, 1=lowest, 2=SMI, 4=NMI, 5=INIT, 7=ExtINT
* bit 11: destination mode: 0=physical, 1=logical
* bit 12: delivery status (RO)
* bit 13: polarity: 0=active high, 1=active low
* bit 14: remote IRR (RO, level-triggered only)
* bit 15: trigger mode: 0=edge, 1=level
* bit 16: mask: 0=enabled, 1=masked
* bits 17-55: reserved
* bits 56-63: destination APIC ID (physical mode) or MDA (logical mode)
* 注: 历史版本曾将 polarity/trigger/mask 错置于 bit 11/13/14,
* 导致 mask 写入只读 remote IRR 位而从未生效 —— 勿回退。
*/
static u64 ioapic_read_redir(volatile u32 *base, u8 entry) {
u32 lo = ioapic_read(base, (u8)(0x10 + entry * 2));
u32 hi = ioapic_read(base, (u8)(0x10 + entry * 2 + 1));
return ((u64)hi << 32) | (u64)lo;
}
static void ioapic_write_redir(volatile u32 *base, u8 entry, u64 val) {
ioapic_write(base, (u8)(0x10 + entry * 2), (u32)(val & 0xffffffffu));
ioapic_write(base, (u8)(0x10 + entry * 2 + 1), (u32)(val >> 32));
}
/* ---- B7: GSI 路由辅助 ---- */
/* 合成重定向表项: vector + ISO 极性/触发 + 物理目的 APIC ID + mask 位 */
static u64 redir_compose(u32 vector, u16 iso_flags, u32 dest_apic_id, int masked) {
u64 entry = (u64)(vector & 0xFFu);
/* delivery mode = fixed (0), destination mode = physical (0) */
/* 极性: bit 13. ISA 默认 active-high (0).
* ISO flags bit 0-1: 0=conforms, 1=active-high, 2=reserved, 3=active-low */
if ((iso_flags & 0x03u) == 0x03u) entry |= (1ULL << 13);
/* 触发: bit 15. ISA 默认 edge (0).
* ISO flags bit 2-3: 0=conforms, 1=edge, 2=reserved, 3=level */
if ((iso_flags & 0x0Cu) == 0x0Cu) entry |= (1ULL << 15);
entry |= ((u64)dest_apic_id << 56);
if (masked) entry |= (1ULL << 16);
return entry;
}
/* 找覆盖指定 GSI 的 IOAPIC (多 IOAPIC 按 GSI 范围划分) */
static struct ioapic_info *ioapic_for_gsi(struct madt_info *info, u32 gsi) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (gsi >= io->gsi_base && gsi < io->gsi_base + io->gsi_count) return io;
}
return NULL;
}
/* ISA IRQ → GSI 解析: 优先 MADT ISO override, 缺省恒等 (QEMU ISA 惯例) */
static u32 isa_irq_to_gsi(u8 isa_irq, u16 *flags_out,
const struct iso_info *isos, u32 iso_count) {
for (u32 i = 0; i < iso_count; i++) {
if (isos[i].bus_source == isa_irq) {
*flags_out = isos[i].flags;
return isos[i].gsi;
}
}
*flags_out = 0;
return (u32)isa_irq;
}
/* 编程单个 GSI 的重定向项 (unmask=1 放开投递, 0 保持屏蔽) */
static int ioapic_program_gsi(struct madt_info *info, u32 gsi,
u32 vector, u16 iso_flags, int unmask) {
struct ioapic_info *io = ioapic_for_gsi(info, gsi);
if (!io || !io->mmio) return -1;
u8 pin = (u8)(gsi - io->gsi_base);
ioapic_write_redir(io->mmio, pin,
redir_compose(vector, iso_flags, g_lapic_id, !unmask));
return 0;
}
/* 全 IOAPIC 全条目屏蔽 (降级路径) — 保留 vector 信息, 仅置 mask 位 */
static void ioapic_mask_all(struct madt_info *info) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (!io->mmio) continue;
for (u32 pin = 0; pin < io->gsi_count && pin < 64; pin++) {
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
entry |= (1ULL << 16);
ioapic_write_redir(io->mmio, (u8)pin, entry);
}
}
}
/* 重定向表回读 dump (验证证据) */
static void ioapic_dump_table(struct madt_info *info) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (!io->mmio) continue;
g_log->info("[apic] IOREDTBL readback:");
for (u32 pin = 0; pin < io->gsi_count && pin < 24; pin++) {
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
u32 vec = (u32)(entry & 0xFFu);
u32 masked = (u32)((entry >> 16) & 1u);
log_dec("[apic] pin=", pin);
log_hex("[apic] vec=", vec);
log_dec("[apic] mask=", masked);
}
}
}
/* ---- IOAPIC 初始化 ---- *
* 逐条目编程重定向表: ISA IRQ→vector 映射 + mask all。
* 真机关键: 处理 MADT ISO (IRQ0→GSI2 是最常见的 override)。
* PIC 兼容模式: vector = 0x20 + IRQ (与 PIC remap 布局一致)。
*
* **QEMU 无法测试但真机必需的代码路径**:
* 1. ISO 处理: 真机几乎总有 IRQ0→GSI2 override, QEMU 默认有 1 条
* 2. 极性标志: 真机 ISA IRQ0 (timer) 必须设 active-high,
* 但 ISO 可能指定 active-low (PCIe SCI 等)
* 3. 多 IOAPIC: 真机可能有 2+ IOAPIC, 每个 GSI 范围不同
*/
static void ioapic_init_one(struct ioapic_info *io, u64 hhdm_offset,
const struct iso_info *isos, u32 iso_count,
void *(*mm_map_mmio_fn)(u64, u64)) {
/* 映射 IOAPIC MMIO — 真机地址通常在 0xFECxxxxx, HHDM 通常覆盖,
* 但使用 mm_map_mmio 更安全 (PCD|PWT 属性) */
if (mm_map_mmio_fn) {
void *mapped = mm_map_mmio_fn(io->mmio_phys, 0x1000);
if (mapped) {
io->mmio = (volatile u32 *)mapped;
g_log->info("[apic] IOAPIC mapped via mm_map_mmio");
} else {
g_log->warn("[apic] mm_map_mmio failed for IOAPIC; trying HHDM");
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
}
} else {
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
}
/* 读取 IOAPIC 版本寄存器获取最大重定向条目数 */
u32 ver = ioapic_read(io->mmio, 0x01);
u32 max_redir = ((ver >> 16) & 0xff) + 1;
io->gsi_count = max_redir;
log_hex("[apic] IOAPIC VER=", ver);
log_hex("[apic] IOAPIC max_redir=", max_redir);
/* 读取 IOAPIC ID 验证 */
u32 id_reg = ioapic_read(io->mmio, 0x00);
log_hex("[apic] IOAPIC ID reg=", id_reg);
/* 构建默认 ISA IRQ→GSI→vector 映射表 (identity map + ISO override)
* 默认: GSI = ISA_IRQ (0-15), vector = 0x20 + ISA_IRQ
* ISO override: 修改特定 IRQ 的 GSI 和极性/触发模式 */
u32 gsi_to_vector[256]; /* GSI → vector 映射 */
u32 gsi_flags[256]; /* GSI → polar/trigger flags from ISO */
for (u32 i = 0; i < 256; i++) {
gsi_to_vector[i] = 0;
gsi_flags[i] = 0;
}
/* 先建立 identity 映射: GSI 0-15 → vector 0x20-0x2F */
for (u32 gsi = 0; gsi < 16; gsi++) {
gsi_to_vector[gsi] = 0x20 + gsi;
}
/* 应用 ISO override */
for (u32 i = 0; i < iso_count; i++) {
const struct iso_info *iso = &isos[i];
u8 src = iso->bus_source;
u32 gsi = iso->gsi;
if (gsi < 256) {
gsi_to_vector[gsi] = 0x20 + src; /* vector 仍基于 ISA IRQ 编号 */
gsi_flags[gsi] = iso->flags;
g_log->info("[apic] ISO: ISA IRQ -> GSI override");
log_hex("[apic] ISA IRQ=", src);
log_hex("[apic] GSI=", gsi);
log_hex("[apic] vector=", 0x20 + src);
}
}
/* 编程重定向表: 全部 mask, 编入正确的 vector/极性/触发模式/目标 APIC */
for (u32 pin = 0; pin < max_redir && pin < 256; pin++) {
u32 gsi = io->gsi_base + pin;
u64 entry = 0;
if (gsi < 256 && gsi_to_vector[gsi] != 0) {
u32 vec = gsi_to_vector[gsi];
u16 flags = gsi_flags[gsi];
/* vector + ISO 极性/触发 + 真实 BSP LAPIC ID, 保持 mask */
entry = redir_compose(vec, flags, g_lapic_id, 1);
} else {
entry = (1ULL << 16); /* 无映射条目: 纯 mask (bit16, vector=0 无害) */
}
ioapic_write_redir(io->mmio, (u8)pin, entry);
}
g_log->info("[apic] IOAPIC redirect table programmed (all masked)");
}
/* ---- LAPIC 使能 ---- *
* 真机必须:
* 1. 写 IA32_APIC_BASE MSR 启用 bit 11 (global enable)
* 2. 写 LAPIC SVR 寄存器设置 Spurious Interrupt Vector (建议 0xFF)
* 3. 设置 SVR bit 8 (APIC software enable)
*
* **QEMU 无法测试但真机必需**: LAPIC SVR 设置 — QEMU 默认已启用 LAPIC,
* 但实机 BIOS 可能留 LAPIC 为 software-disable 状态。
*/
static void lapic_enable(void) {
volatile u32 *lapic = g_lapic;
/* 确保 IA32_APIC_BASE 的 global enable 和 BSP 位已设置 */
u64 apic_base = rdmsr(0x1B);
apic_base |= (1ULL << 11); /* global enable */
/* 保持 BSP 位不变 (bit 8) */
wrmsr(0x1B, apic_base);
g_log->info("[apic] LAPIC global enable set in MSR");
/* 读当前 SVR */
u32 svr = lapic[0xF0 / 4];
log_hex("[apic] LAPIC SVR before=", svr);
/* 设置 Spurious Interrupt Vector = 0xFF + APIC software enable (bit 8) */
svr = 0xFF | (1u << 8);
lapic[0xF0 / 4] = svr;
apic_mb();
/* 回读验证 */
svr = lapic[0xF0 / 4];
log_hex("[apic] LAPIC SVR after=", svr);
if (svr & (1u << 8)) {
g_log->info("[apic] LAPIC software enabled");
} else {
g_log->warn("[apic] LAPIC SVR bit8 not set — hardware may not support");
}
/* 读 LAPIC ID 和 version 做诊断 dump */
log_hex("[apic] LAPIC ID=", lapic[0x20 / 4]);
log_hex("[apic] LAPIC LDR=", lapic[0xD0 / 4]);
log_hex("[apic] LAPIC version=", lapic[0x30 / 4]);
}
/* LAPIC EOI — 写 0 到 EOI 寄存器 (offset 0xB0)。
* B7 阶段2: 本函数作为 EOI 钩子经 kernel_api.register_apic_eoi 注册给 UTSM,
* idt_handler 在两个 IRQ 分支的分发末尾调用 (钩子签名 void(*)(void) 匹配)。
* 调用点: IRQ 上下文, 单核 BSP interrupt gate (IF=0), 无嵌套, 无需锁。 */
static __inline__ void lapic_eoi(void) {
if (g_lapic) {
g_lapic[0xB0 / 4] = 0;
apic_mb();
}
}
/* ---- B7 diag: HPET 绝对时基 (tick 2x 定论实验) ---- *
* 选型理由: TSC 校准 (PIT mode0 回绕法) 与被测对象 (PIT mode3 投递频率)
* 共享 PIT 硬件, 循环论证无法定论 2x 根因。HPET 主计数器是独立时基
* (QEMU q35: 100MHz, period=10000fs), 32 位回绕周期 ~42.9s, 对 ms 级
* 窗口充裕。只设 GEN_CONF bit0 (overall enable); 绝不置 bit1
* (legacy replacement route 会抢 PIT IRQ0/RTC IRQ8 路由, 破坏被测链路)。
*/
static volatile u32 *g_hpet; /* HPET MMIO 基址 (HHDM), 0=不可用 */
static u32 g_hpet_period_fs; /* 主计数器周期 (飞秒), GCAP_ID[63:32] */
#define HPET_GCAP_ID 0x00
#define HPET_GEN_CONF 0x10
#define HPET_MAIN_CNT 0xF0
static u64 hpet_read64(u32 off) {
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
u32 lo = p[0];
u32 hi = p[1];
return ((u64)hi << 32) | lo;
}
static void hpet_write64(u32 off, u64 v) {
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
p[0] = (u32)(v & 0xffffffffu);
p[1] = (u32)(v >> 32);
}
static __inline__ u32 hpet_main32(void) {
return *(volatile u32 *)((volatile u8 *)g_hpet + HPET_MAIN_CNT);
}
/* 初始化 HPET 时基; 成功返回 0 且 g_hpet/g_hpet_period_fs 有效 */
static int hpet_diag_init(const struct dkm_kernel_api *api) {
const acpi_hpet *hpet = find_hpet(api->rsdp_address);
if (!hpet) {
g_log->warn("[apic] diag: HPET table not found");
return -1;
}
if (hpet->addr_space_id != 0 || !hpet->base_addr) {
g_log->warn("[apic] diag: HPET not memory-mapped");
return -1;
}
g_hpet = (volatile u32 *)(uintptr_t)(api->hhdm_offset + hpet->base_addr);
u64 gcap = hpet_read64(HPET_GCAP_ID);
g_hpet_period_fs = (u32)(gcap >> 32);
if (!g_hpet_period_fs || g_hpet_period_fs > 100000000u) {
g_log->warn("[apic] diag: HPET period implausible");
g_hpet = 0;
return -1;
}
log_hex("[apic] diag: HPET base=", hpet->base_addr);
log_dec("[apic] diag: HPET period(fs)=", g_hpet_period_fs);
/* overall enable (bit0); legacy replacement route (bit1) 保持 0 */
u64 conf = hpet_read64(HPET_GEN_CONF);
if (!(conf & 1u)) {
hpet_write64(HPET_GEN_CONF, conf | 1u);
apic_mb();
}
g_log->info("[apic] diag: HPET main counter enabled");
return 0;
}
/* HPET 主计数器差值 → 微秒 (全程 u64 整数, -msoft-float 环境无浮点) */
static u64 hpet_delta_us(u32 h0, u32 h1) {
/* 无符号回绕安全: u32 减法自动回绕 (42.9s 周期, 窗口 << 此值) */
u64 ticks = (u64)(u32)(h1 - h0);
/* ticks * period_fs / 1e9 = us; period_fs 典型 10000, 500ms 窗口
* ticks≈5e7, 乘积 5e11 << 2^64, 溢出安全 */
return (ticks * (u64)g_hpet_period_fs) / 1000000000ull;
}
/* ---- B7 diag: PS/2 鼠标流 (IRQ12 持续投递验证) ---- *
* UTSM 阶段 IRQ12 无属主 (mouseInit 是 DSK 阶段模块), diag handler 独占;
* DSK mouseInit 之后经 irq_register(12) 覆盖本 handler 并完整复位鼠标,
* 无状态冲突。所有单步带超时, 失败只记日志不阻塞。 */
static volatile u32 g_mouse_pkt_count;
static int ps2_wait_ibf(void) {
u32 t = 100000;
while (t--) { if (!(inb(0x64) & 0x02)) return 0; }
return -1;
}
static int ps2_wait_obf(void) {
u32 t = 100000;
while (t--) { if (inb(0x64) & 0x01) return 0; }
return -1;
}
static void ps2_drain(void) {
while (inb(0x64) & 0x01) { (void)inb(0x60); }
}
/* 经 0xD4 通道写鼠标命令并等 ACK(0xFA); 失败返回 -1 */
static int ps2_aux_cmd(u8 cmd) {
if (ps2_wait_ibf() != 0) return -1;
outb(0x64, 0xD4);
if (ps2_wait_ibf() != 0) return -1;
outb(0x60, cmd);
if (ps2_wait_obf() != 0) return -1;
return inb(0x60) == 0xFA ? 0 : -1;
}
/* IRQ12 诊断 handler: 只读数据计数, EOI 交给 idt_handler 分发路径
* (PIC 模式: 8259 EOI; route 模式: 钩子 LAPIC EOI + 无害 8259 EOI)。
* 返回 0 — 不自负 EOI, 与 ps2kbd/e1000 handler 同一约定。 */
static int apic_mouse_diag_handler(u8 vector) {
(void)vector;
u8 st = inb(0x64);
if ((st & 0x01) && (st & 0x20)) { /* OBF 且 AUX 位置位 = 鼠标数据 */
u8 data = inb(0x60); /* 必须读, 否则 OBF 保持阻塞后续包 */
g_mouse_pkt_count++;
if (g_mouse_pkt_count <= 6) {
g_log->info("[apic] diag: mouse packet byte");
log_hex("[apic] diag: data=", data);
}
}
return 0;
}
/* IRQ1 诊断 handler (键盘流计数): ps2kbd demo 窗口只有 ~10ms 数量级,
* 自动化无法抓住; diag 在 stage0 自建 3s 观察窗验证 IRQ1 持续投递。
* stage3 ps2kbd 注册时覆盖本 handler, 无冲突。 */
static volatile u32 g_kbd_pkt_count;
static int apic_kbd_diag_handler(u8 vector) {
(void)vector;
u8 st = inb(0x64);
if ((st & 0x01) && !(st & 0x20)) { /* OBF 且 AUX 位清零 = 键盘数据 */
u8 data = inb(0x60); /* 读走扫描码 (窗内按键, 无消费者) */
g_kbd_pkt_count++;
if (g_kbd_pkt_count <= 6) {
g_log->info("[apic] diag: kbd scancode byte");
log_hex("[apic] diag: sc=", data);
}
}
return 0;
}
/* AUX 端口初始化: 启用 AUX 时钟 + 采样率 100 + 数据报告, 最后才开 AUX IRQ。
* 顺序要点: AUX IRQ (cfg bit1) 必须在全部命令/ACK 交互完成后才使能 —
* 否则 ACK 字节 (0xFA) 到达即触发 IRQ12 被 diag handler 读走, 轮询侧
* 永远等不到 ACK (实测 F3 nack 即此竞态: handler 日志 data=0xfa)。
* 单步失败只记日志继续 (与 mouseInit 同一安全准则)。 */
static void ps2_aux_enable_stream(void) {
ps2_drain();
if (ps2_wait_ibf() != 0) { g_log->warn("[apic] diag: PS2 ibf timeout"); return; }
outb(0x64, 0x20); /* 读配置字节 */
if (ps2_wait_obf() != 0) { g_log->warn("[apic] diag: PS2 cfg read timeout"); return; }
u8 cfg = inb(0x60);
cfg &= (u8)~0x02; /* bit1=0: AUX IRQ 先关闭 (轮询期) */
cfg &= (u8)~0x20; /* bit5=0: AUX clock enable */
cfg |= 0x01; /* bit0=1: KBD IRQ 保持使能 (键盘流用) */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x60); /* 写配置字节 */
if (ps2_wait_ibf() != 0) return;
outb(0x60, cfg);
log_hex("[apic] diag: PS2 cfg byte=", cfg);
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0xA8); /* 启用 AUX 端口 */
if (ps2_aux_cmd(0xF3) != 0) { g_log->warn("[apic] diag: mouse F3 nack"); }
else if (ps2_aux_cmd(100) != 0) { g_log->warn("[apic] diag: mouse rate nack"); }
if (ps2_aux_cmd(0xF4) != 0) { g_log->warn("[apic] diag: mouse F4 nack"); return; }
g_log->info("[apic] diag: PS/2 mouse data reporting enabled");
ps2_drain(); /* 清 F4 ACK 后可能挂起的首个流字节 */
/* 全部交互完成, 现在才开 AUX IRQ — 之后鼠标字节全走 IRQ12 handler */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x20);
if (ps2_wait_obf() != 0) return;
cfg = inb(0x60);
cfg |= 0x02; /* bit1=1: AUX IRQ enable */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x60);
if (ps2_wait_ibf() != 0) return;
outb(0x60, cfg);
log_hex("[apic] diag: PS2 cfg byte(final)=", cfg);
}
/* ---- B7 阶段2: PIT tick 路由验证 ---- *
* vector 0x20 落在 idt_handler 的 PIC 分支 (vector 32-47) — 与键盘 0x21/
* 鼠标 0x2C/网卡 0x2B 同一代码路径。分发顺序: handler(本函数) → 钩子
* LAPIC EOI → 8259 EOI (PIC 全屏蔽下为无害 no-op)。
* 本 handler 返回 0 不自负 EOI: 由 UTSM 钩子写 LAPIC EOI 寄存器清除
* ISR 位; 重复投递 (计数>=2) 即证明"legacy 向量分支 + 钩子 EOI"可持续,
* 这正是键盘/鼠标/网卡将要走的路径 (阶段1 的 0xE0+自EOI 无法证明此路径)。
* handler 是普通 C 函数 (SysV ABI), isr stub 已完成现场保存/iret。
*/
static int apic_tick_handler(u8 vector) {
(void)vector;
g_tick_count++;
if (g_tick_count <= 8) {
g_log->info("[apic] ioapic tick (PIT GSI -> LAPIC -> IDT vec 0x20, hook EOI)");
}
return 0;
}
static u64 rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
static u64 g_tsc_per_ms;
/* PIT ch0 mode 0 单发 (~10ms, 11932 counts) 校准 TSC。
* 必须在把 PIT 改为 mode 3 周期模式之前调用。 */
static void tsc_calibrate(void) {
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
outb(0x40, 0x9C); /* 11932 low = ~10ms */
outb(0x40, 0x2E); /* 11932 high */
u64 tsc_start = rdtsc();
u16 prev = 0;
u64 loops = 0;
for (;;) {
outb(0x43, 0x00);
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
if (cur > prev && loops > 10) break; /* 回绕 = 10ms 到 */
prev = cur;
loops++;
}
u64 tsc_end = rdtsc();
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
}
static void tsc_delay_ms(u32 ms) {
if (!g_tsc_per_ms) {
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
return;
}
u64 target = g_tsc_per_ms * ms;
u64 start = rdtsc();
while (rdtsc() - start < target) __asm__ volatile("pause");
}
/* ---- B7 阶段2: 路由切换主流程 ---- *
* 成功: PIC IMR 全 1, IOAPIC 拥有路由, EOI 钩子保持注册, tick 验证后
* tick 线重新 mask (保留路由表内容), legacy 线按 apic_route_legacy。
* 失败: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 tick handler + 注销 EOI
* 钩子, 与切换前状态完全一致 (现状不回归)。
*/
static void apic_route_switch(const struct dkm_kernel_api *api,
struct madt_info *info) {
g_log->info("[apic] apic_route=1: attempting IOAPIC routing takeover");
if (!g_lapic) {
g_log->warn("[apic] route abort: LAPIC MMIO unavailable; PIC retained");
return;
}
if (info->ioapic_count == 0) {
g_log->warn("[apic] route abort: no IOAPIC; PIC retained");
return;
}
if (!api->irq_register) {
g_log->warn("[apic] route abort: irq_register missing; PIC retained");
return;
}
/* 0) B7 阶段2 前提: EOI 钩子接口必须存在 (旧内核无此字段 → 判空中止)。
* 无钩子时 legacy 向量 (0x20-0x2F) 在 IOAPIC 路由下只能 one-shot,
* 路由接管不成立 — 宁可保留 PIC 也不进入半残状态。 */
if (!api->register_apic_eoi) {
g_log->warn("[apic] route abort: register_apic_eoi missing (old kernel); PIC retained");
return;
}
api->register_apic_eoi(lapic_eoi);
g_log->info("[apic] LAPIC EOI hook registered to UTSM");
/* 1) 保存 PIC IMR (降级恢复用) */
u8 imr1 = inb(0x21);
u8 imr2 = inb(0xA1);
log_hex("[apic] PIC IMR saved master=", imr1);
log_hex("[apic] PIC IMR saved slave=", imr2);
/* 2) TSC 校准 (PIT 尚在 firmware/driver 遗留状态, 先校准再改模式)。
* diag: HPET 绝对时基对拍本窗口 — 校准窗口标称 10ms, HPET 实测
* 若 ~20000us 则 PIT mode0 实际跑了 20ms (TSC 测半), ~10000us
* 则校准正确, tick 2x 根因另在他处。 */
int hpet_ok = 0;
if (g_cfg.apic_diag) {
hpet_ok = (hpet_diag_init(api) == 0);
}
u32 hc0 = 0;
if (hpet_ok) hc0 = hpet_main32();
tsc_calibrate();
if (hpet_ok) {
u32 hc1 = hpet_main32();
u64 calib_us = hpet_delta_us(hc0, hc1);
log_dec("[apic] diag: calib window(us)=", calib_us);
g_log->info("[apic] diag: (nominal 10000us; ~20000 => TSC half-measured)");
}
log_hex("[apic] tsc_per_ms=", g_tsc_per_ms);
/* 3) 注册 tick handler @ vector 0x20 — PIC 分支 (legacy 向量路径),
* handler 返回 0, LAPIC EOI 由 UTSM 钩子完成 */
if (api->irq_register(0x20, (void *)(uintptr_t)apic_tick_handler) != 0) {
g_log->warn("[apic] route abort: irq_register(0x20) failed");
api->register_apic_eoi(NULL);
return;
}
g_log->info("[apic] tick handler registered at vector 0x20 (PIC branch, hook EOI)");
/* 4) PIT tick GSI 解析: MADT ISO source 0 (QEMU q35: GSI2), 缺省恒等 */
u16 tick_flags = 0;
u32 tick_gsi = isa_irq_to_gsi(0, &tick_flags, info->isos, info->iso_count);
log_hex("[apic] tick GSI=", tick_gsi);
log_hex("[apic] tick GSI flags=", tick_flags);
/* 5) 编程 tick 重定向项 (vector 0x20, 放开) */
if (ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 1) != 0) {
g_log->warn("[apic] route abort: tick GSI not covered by any IOAPIC");
api->irq_register(0x20, (void *)0);
api->register_apic_eoi(NULL);
return;
}
g_log->info("[apic] tick redirection unmasked (vec 0x20)");
/* 6) legacy 线 (apic_route_legacy): EOI 钩子已就位, 持续投递成立。
* ISA 线 (IRQ1/12) 按 ISO, 缺省 edge/active-high;
* PCI 线 (IRQ11 e1000) 无 ISO 覆盖时强制 level/active-low
* (PCI INTx 规范; flags=0xF = pol bit0-1=3 active-low + trig bit2-3=3
* level)。e1000 handler 读 ICR 清源 → 电平 deassert → 钩子 EOI。 */
if (g_cfg.apic_route_legacy) {
u16 f1 = 0, f12 = 0, f11 = 0;
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
u32 gsi11 = isa_irq_to_gsi(11, &f11, info->isos, info->iso_count);
if (!f11) f11 = 0x0F;
ioapic_program_gsi(info, gsi1, 0x21, f1, 1); /* 键盘 */
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1); /* 鼠标 */
ioapic_program_gsi(info, gsi11, 0x2B, f11, 1); /* e1000 (QEMU 默认 IRQ11) */
g_log->info("[apic] legacy IRQ1/12/11 unmasked (sustained delivery via EOI hook)");
log_hex("[apic] kbd gsi=", gsi1);
log_hex("[apic] mouse gsi=", gsi12);
log_hex("[apic] e1000 gsi=", gsi11);
log_hex("[apic] e1000 flags=", f11);
}
/* 7) LAPIC TPR=0 全放行 */
g_lapic[0x80 / 4] = 0;
apic_mb();
log_hex("[apic] LAPIC TPR=", g_lapic[0x80 / 4]);
/* 8) PIC 全屏蔽 (先 slave 后 master, 避免 cascade 窗口; QEMU 无功能 IMCR) */
outb(0xA1, 0xFF);
io_wait();
outb(0x21, 0xFF);
io_wait();
g_log->info("[apic] PIC fully masked; IOAPIC owns IRQ routing");
/* 9) PIT ch0 mode 3 周期 ~100Hz (divisor 11932), 产生真实 tick 源 */
outb(0x43, 0x36);
outb(0x40, 0x9C);
outb(0x40, 0x2E);
g_log->info("[apic] PIT ch0 mode3 100Hz started");
/* 10) 500ms 验证窗口 (+ diag: HPET 实测窗口与 tick 频率定论) */
u32 hv0 = 0;
if (hpet_ok) hv0 = hpet_main32();
u32 before = g_tick_count;
tsc_delay_ms(500);
u32 ticks = g_tick_count - before;
if (hpet_ok) {
u32 hv1 = hpet_main32();
u64 win_us = hpet_delta_us(hv0, hv1);
log_dec("[apic] diag: verify window(us)=", win_us);
if (win_us) {
u64 hz_x10 = (u64)ticks * 10000000ull / win_us;
log_dec("[apic] diag: tick rate(Hz x10)=", hz_x10);
}
/* tick 2x 定论分支:
* win≈500ms 且 ticks≈100 → PIT mode3 在 IOAPIC edge 下双触发 (200Hz 真实)
* win≈1000ms 且 ticks≈100 → TSC 校准窗口翻倍 (tick 频率真实 100Hz)
* win≈500ms 且 ticks≈50 → 一切正常 */
if (win_us > 750000) {
g_log->info("[apic] diag: TICK2X VERDICT: TSC calib window doubled (PIT mode0 ran ~20ms); tick rate nominal");
} else if (ticks > 75) {
g_log->info("[apic] diag: TICK2X VERDICT: PIT mode3 double-clocked on IOAPIC edge (~200Hz real)");
} else {
g_log->info("[apic] diag: TICK2X VERDICT: nominal (~100Hz, no 2x)");
}
}
log_dec("[apic] route verify ticks=", ticks);
if (ticks >= 2) {
/* 成功: 重复投递证明钩子 EOI 正确清除 ISR 位, 路由链路完整 */
g_log->info("[apic] ROUTE VERIFY OK — IOAPIC->LAPIC->IDT(vec 0x20)->handler->hook EOI chain proven");
/* diag: 第二窗口持续投递证据 (排除窗口内偶发两次的弱证明) */
if (g_cfg.apic_diag) {
u32 before2 = g_tick_count;
tsc_delay_ms(500);
u32 ticks2 = g_tick_count - before2;
log_dec("[apic] diag: sustain window ticks=", ticks2);
if (ticks2 >= 2) {
g_log->info("[apic] diag: SUSTAINED delivery confirmed (2nd window)");
}
}
/* tick 线重新 mask (保留路由表, vector 保持 0x20), 避免后台周期中断;
* PIT 恢复 mode 3 @ 18.2Hz (BIOS 惯例值, 已被 mask 不会投递)。
* EOI 钩子保持注册 — legacy 线的持续投递依赖它。 */
ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 0);
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
g_route_active = 1;
g_log->info("[apic] APIC ROUTING ACTIVE (PIC masked; EOI hook live; tick line re-masked)");
} else {
/* 失败: 完整降级回 PIC (含注销 EOI 钩子) */
g_log->error("[apic] ROUTE VERIFY FAILED — degrading to PIC routing");
ioapic_mask_all(info);
api->irq_register(0x20, (void *)0);
api->register_apic_eoi(NULL);
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
outb(0x21, imr1);
io_wait();
outb(0xA1, imr2);
io_wait();
g_log->info("[apic] PIC routing restored (IMR restored, IOAPIC masked, hook cleared)");
}
}
/* ---- B7 diag: IRQ1/IRQ12 键鼠流观察窗 ---- *
* 在 route switch 之后调用 (route 成功走 IOAPIC; 否则走 PIC IMR)。
* 打锚点日志供测试脚本/人工在窗口内用 QEMU monitor sendkey/mouse_move
* 注入输入; 窗口结束分别报告收包计数。计数 >1 即证明该线在当前路由
* 模式下可持续投递 (一次按键=make+break 两包, 一次移动=多包)。 */
static void apic_diag_mouse_window(const struct dkm_kernel_api *api,
struct madt_info *info) {
g_log->info("[apic] diag: IRQ1/IRQ12 input stream check begin");
/* 确保 TSC 延时可用 (route=0 时未校准: 现场校准并恢复 PIT 惯例状态) */
if (!g_tsc_per_ms) {
tsc_calibrate();
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
}
if (!api->irq_register) return;
if (g_route_active) {
/* IOAPIC 路由: 确保 IRQ12/IRQ1 重定向项放开 (legacy=1 时幂等重编) */
u16 f12 = 0, f1 = 0;
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1);
ioapic_program_gsi(info, gsi1, 0x21, f1, 1);
g_log->info("[apic] diag: IRQ12+IRQ1 via IOAPIC redirection");
} else {
/* PIC 路由: 解 slave IMR bit4 (IRQ12); master IMR bit1 (IRQ1)
* UTSM pic_remap 已留 0xFD (IRQ1 enable), 幂等确认。
* diag 属实验开关, 显式改变 PIC 状态并打日志; UTSM PIC 分支
* 会做 8259 EOI, diag handler 返回 0 即可。 */
outb(0xA1, (u8)(inb(0xA1) & (u8)~0x10u));
outb(0x21, (u8)(inb(0x21) & (u8)~0x02u));
g_log->info("[apic] diag: IRQ12+IRQ1 via PIC (IMR bits cleared)");
}
/* 控制器交互必须全轮询完成: 期间到达的 IRQ (KBD bit0 一直开着)
* 因 handler 未注册只会被 EOI 丢弃, 不会偷走命令响应字节。
* (实测教训: 先注册 handler 时, 0x20 读 cfg 的响应被 kbd handler
* 当扫描码读走, 轮询侧 cfg read timeout) */
ps2_aux_enable_stream();
/* 交互全部完成, 现在才注册诊断 handler */
if (api->irq_register(0x2C, (void *)(uintptr_t)apic_mouse_diag_handler) != 0) {
g_log->warn("[apic] diag: irq_register(0x2C) failed");
return;
}
if (api->irq_register(0x21, (void *)(uintptr_t)apic_kbd_diag_handler) != 0) {
g_log->warn("[apic] diag: irq_register(0x21) failed");
return;
}
/* 6000ms 窗口: 注入方需经 "串口日志落盘(块缓冲) → 测试脚本轮询" 的
* 可见性延迟 (~1s 量级), 3s 窗口实测会被注入延迟吃光 */
g_log->info("[apic] diag: INPUT OBSERVE WINDOW 6000ms — sendkey/mouse_move now");
u32 m_before = g_mouse_pkt_count;
u32 k_before = g_kbd_pkt_count;
tsc_delay_ms(6000);
u32 mpkts = g_mouse_pkt_count - m_before;
u32 kpkts = g_kbd_pkt_count - k_before;
log_dec("[apic] diag: mouse packets observed=", mpkts);
log_dec("[apic] diag: kbd packets observed=", kpkts);
if (mpkts > 1) {
g_log->info("[apic] diag: IRQ12 SUSTAINED delivery confirmed");
} else if (mpkts == 1) {
g_log->warn("[apic] diag: IRQ12 single packet only (check EOI path)");
} else {
g_log->info("[apic] diag: no mouse packets (inject via monitor mouse_move)");
}
if (kpkts > 1) {
g_log->info("[apic] diag: IRQ1 SUSTAINED delivery confirmed");
} else if (kpkts == 1) {
g_log->warn("[apic] diag: IRQ1 single packet only (check EOI path)");
} else {
g_log->info("[apic] diag: no kbd packets (inject via monitor sendkey)");
}
/* handler 保持注册: stage3 ps2kbd 经 irq_register(1) 覆盖键盘 handler,
* DSK mouseInit 经 irq_register(12) 覆盖鼠标 handler, 均无冲突;
* 重定向项/IMR 保持现状 (ps2kbd/mouseInit 有完整初始化序列)。 */
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
g_log->info("[apic] init begin");
if (!api->irq_register) {
g_log->error("[apic] kernel irq_register missing");
return -1;
}
g_log->info("[apic] kernel IRQ backend present: PIC");
/* B7: FUCK 配置自解析 (lapic_enable/ioapic_init/apic_route/apic_route_legacy/apic_diag) */
load_fuck_config(api);
if (!cpu_has_lapic()) {
g_log->warn("[apic] CPUID local APIC not present; PIC fallback active");
g_log->info("[apic] driver ready");
return 0;
}
g_log->info("[apic] CPUID local APIC present");
u64 apic_base = rdmsr(0x1B);
u64 lapic_phys = apic_base & 0xFFFFF000ULL;
log_hex("[apic] IA32_APIC_BASE=", apic_base);
log_hex("[apic] LAPIC phys base=", lapic_phys);
if (apic_base & (1ULL << 8)) g_log->info("[apic] bootstrap processor");
if (apic_base & (1ULL << 11)) g_log->info("[apic] LAPIC enabled by firmware");
struct madt_info info;
info.lapic_phys = lapic_phys;
info.flags = 0;
info.lapic_count = 0;
info.ioapic_count = 0;
info.iso_count = 0;
const acpi_madt *madt = find_madt(api->rsdp_address);
if (madt) {
g_log->info("[apic] MADT found");
info.lapic_phys = (u64)madt->lapic_addr;
info.flags = madt->flags;
log_hex("[apic] MADT lapic_phys=", info.lapic_phys);
log_hex("[apic] MADT flags=", info.flags);
parse_madt_entries(madt, &info);
log_hex("[apic] CPU LAPIC count=", info.lapic_count);
log_hex("[apic] IOAPIC count=", info.ioapic_count);
log_hex("[apic] ISO count=", info.iso_count);
} else {
g_log->warn("[apic] MADT not found; PIC fallback active");
}
if (api->hhdm_offset && info.lapic_phys) {
g_lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
g_lapic_id = g_lapic[0x20 / 4] >> 24;
log_hex("[apic] LAPIC ID reg=", g_lapic[0x20 / 4]);
log_hex("[apic] LAPIC version reg=", g_lapic[0x30 / 4]);
log_hex("[apic] LAPIC SVR reg=", g_lapic[0xF0 / 4]);
log_hex("[apic] LAPIC TPR reg=", g_lapic[0x80 / 4]);
log_hex("[apic] BSP LAPIC ID=", g_lapic_id);
} else {
g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read");
g_lapic = (volatile u32 *)0;
}
/* ---- LAPIC 使能(真机必需,QEMU 也可安全执行) ---- */
if (g_lapic && g_cfg.lapic_enable) {
lapic_enable();
} else if (!g_cfg.lapic_enable) {
g_log->info("[apic] lapic_enable=0: skip LAPIC software enable");
}
/* ---- IOAPIC 初始化(真机必需,QEMU 也可安全执行) ---- */
if (g_cfg.ioapic_init) {
for (u32 i = 0; i < info.ioapic_count; i++) {
g_log->info("[apic] initializing IOAPIC");
log_hex("[apic] index=", i);
ioapic_init_one(&info.ioapics[i], api->hhdm_offset,
info.isos, info.iso_count,
api->mm_map_mmio);
}
} else {
g_log->info("[apic] ioapic_init=0: skip IOAPIC programming (PIC mode)");
}
/* ---- B7: 路由接管 (默认关, FUCK apic_route=1 启用) ---- */
if (g_cfg.apic_route && g_cfg.ioapic_init) {
apic_route_switch(api, &info);
} else {
g_log->info("[apic] apic_route=0: PIC routing retained (default)");
}
/* ---- B7 diag: IRQ12 鼠标流观察窗 (apic_diag=1, 两种路由模式皆可测) ---- */
if (g_cfg.apic_diag) {
apic_diag_mouse_window(api, &info);
}
/* 重定向表回读 (验证证据; 无 IOAPIC 时无输出) */
if (g_cfg.ioapic_init) {
ioapic_dump_table(&info);
}
if (g_route_active) {
g_log->info("[apic] discovery + init complete; APIC routing active");
} else {
g_log->info("[apic] discovery + init complete; PIC routing retained as fallback");
}
g_log->info("[apic] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-1395
View File
@@ -1,1395 +0,0 @@
/* DKM ath9k Driver — Atheros AR928x 802.11abgn WiFi
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
*
* 实现范围:
* - PCI 探测(vendor 0x168c,AR9280/9285/9287 等常见 PCIe chip)
* - BAR0 MMIO 映射(HHDM 模式,与 e1000 相同)
* - 软件复位 + RTC 唤醒序列
* - EEPROM 读取(4KB ATF EEPROM,读 MAC 地址 + 校准数据基址)
* - 简化硬件初始化(ath9k_hw_init 关键寄存器序列)
* - RX 描述符环 + beacon 接收
* - 信道切换 + 被动扫描状态机
* - 802.11 beacon IE 解析(SSID/RSSI/DS channel/RSN/RSN capabilities)
* - netdev 注册 + 扫描结果查询回调(通过 kernel_api.net.scan_*)
*
* 注意:QEMU 不支持 WiFi 网卡模拟,本驱动只能在真实硬件测试。
* 完整 ath9k HAL 寄存器初始化序列非常庞大,本驱动保留核心扫描路径,
* 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。
*/
#include "../dkm_shared.h"
#include "wpa2_crypto.h"
static const char *const g_depends[] = {"pci","irq"};
static const char *const g_provides[] = {"netdev"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="ath9k",.version="0.1.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=2,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
};
/* ---- ath9k 寄存器(AR9280/9285/9287 PCIe MAC,Linux ath9k reg.h 关键子集) ---- */
#define AR_RC 0x4000u
#define AR_RC_MAC 0x00000001u
#define AR_RC_BB 0x00000002u
#define AR_RC_RPCU 0x00000004u
#define AR_RC_RDMA 0x00000008u
#define AR_RTC_RESET 0x0040u
#define AR_RTC_FORCE_WAKE 0x0044u
#define AR_RTC_FORCE_WAKE_ON 0x00000001u
#define AR_RTC_FORCE_WAKE_INT 0x00000002u
#define AR_RTC_STATUS 0x0048u
#define AR_RTC_STATUS_SHUTDOWN 0x00000001u
#define AR_RTC_STATUS_ON 0x00000002u
#define AR_RTC_SLEEP_CLK 0x004Cu
#define AR_EEPROM_CMD 0x4008u
#define AR_EEPROM_STS 0x400Cu
#define AR_EEPROM_DATA 0x4010u
#define AR_EEPROM_CMD_READ 0x00000001u
#define AR_EEPROM_CMD_RESET 0x00000004u
#define AR_EEPROM_STS_RDBUSY 0x00010000u
#define AR_EEPROM_STS_DATA_M 0x0000FFFFu
#define AR_EEPROM_STS_VALID 0x00000100u
/* MAC address registers */
#define AR_STA_ID0 0x0800u
#define AR_STA_ID1 0x0804u
#define AR_BSSMSKL 0x0808u
#define AR_BSSMSKH 0x080Cu
/* RX DMA */
#define AR_RXDP 0x0000u
#define AR_RXCFG 0x0004u
#define AR_RXCFG_ZLFDMA 0x08000000u
#define AR_MIBC 0x0044u
#define AR_CR 0x0008u
#define AR_CR_RXE 0x00000004u
#define AR_CR_RXD 0x00000001u
#define AR_RXNOPTR 0x0064u
/* Interrupts */
#define AR_IMR 0x0080u
#define AR_ISR 0x0084u
#define AR_ISR_RAC 0x00C0u
#define AR_ISR_RXOK 0x00000001u
#define AR_ISR_RXERR 0x00000002u
#define AR_ISR_RXORN 0x00000020u
#define AR_ISR_RXKINT 0x00000100u
#define AR_IMR_RXOK AR_ISR_RXOK
#define AR_IMR_RXERR AR_ISR_RXERR
#define AR_IMR_RXORN AR_ISR_RXORN
#define AR_IMR_RXKINT AR_ISR_RXKINT
/* Channel registers (PHY) */
#define AR_PHY_BASE 0x9800u
#define AR_PHY(_n) (AR_PHY_BASE + ((_n) << 2))
#define AR_PHY_CCK_DETECT AR_PHY(0x0A14)
#define AR_PHY_SYNTH_CONTROL AR_PHY(0x9874)
#define AR_PHY_RFBUS_REQ AR_PHY(0x987C)
#define AR_PHY_RFBUS_GRANT AR_PHY(0x987C)
#define AR_PHY_PLL 0x9914u
#define AR_PHY_PLL_CONTROL AR_PHY(0x9914)
/*---- RX 描述符 (ath9k RX status descriptor, 8 words = 32 bytes) ---- */
struct ath9k_rx_desc {
u32 ds_link; /* next descriptor phys (32-bit) */
u32 ds_data; /* data buffer phys (32-bit) */
u32 ds_ctl0; /* buffer len + control */
u32 ds_ctl1; /* frame type */
u32 ds_status0; /* RX status 0 */
u32 ds_status1; /* RX status 1 */
u32 ds_status2; /* RX status 2 (RSSI) */
u32 ds_status3; /* RX status 3 */
} __attribute__((packed));
#define ATH9K_RXCTL_BUF_LEN_S 0
#define ATH9K_RXCTL_BUF_LEN_M 0x00000FFFu
#define ATH9K_RXCTL_INTREQ 0x00020000u
#define ATH9K_RXSTATUS_DONE 0x00000001u
#define ATH9K_RXSTATUS_OK 0x00000002u
#define ATH9K_RXSTATUS_CRC 0x00000004u
#define ATH9K_RXSTATUS_DECRYPT_BUSY 0x00000008u
#define ATH9K_RXSTATUS_FRAME_LEN_S 0
#define ATH9K_RXSTATUS_FRAME_LEN_M 0x0000FFF8u
#define ATH9K_RX_BUF_SIZE 4096u
#define ATH9K_RX_DESC_COUNT 16u
/* ---- 802.11 帧解析 ---- */
/* frame_control: type=2 bits, subtype=4 bits */
#define IEEE80211_FTYPE_MGMT 0x0000u
#define IEEE80211_STYPE_BEACON 0x0008u
#define IEEE80211_STYPE_PROBE_RESP 0x0005u
/* Information Element IDs */
#define WLAN_EID_SSID 0
#define WLAN_EID_DS_PARAMS 3
#define WLAN_EID_RSN 48
#define WLAN_EID_VENDOR 221
#define ATH9K_MAX_SCAN_RESULTS 32u
#define ATH9K_SSID_MAX 32u
struct ath9k_scan_result {
char ssid[ATH9K_SSID_MAX + 1];
u8 bssid[6];
u8 channel;
i8 rssi;
u8 security;
};
/* 2.4GHz 信道表 (channel -> freq MHz) */
__attribute__((used))
static const u16 g_chan2g[] = {
2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
2452, 2457, 2462, 2467, 2472
};
#define ATH9K_CHAN_2G_COUNT (sizeof(g_chan2g) / sizeof(g_chan2g[0]))
/* ---- 全局状态 ---- */
static const struct dkm_log_api *g_log;
static volatile u8 *g_mmio;
static u64 g_bar0_phys;
static u8 g_mac[6];
static u16 g_pci_device_id;
static u8 g_pci_irq;
static struct dkm_dma_buffer g_rx_desc_dma;
static struct ath9k_rx_desc *g_rx_desc;
static struct dkm_dma_buffer g_rx_buf_dma[ATH9K_RX_DESC_COUNT];
static u8 *g_rx_buf[ATH9K_RX_DESC_COUNT];
static u32 g_rx_head;
static int g_rx_ready;
/* 扫描状态 */
static struct ath9k_scan_result g_scan_results[ATH9K_MAX_SCAN_RESULTS];
static u32 g_scan_count;
static int g_scanning;
/* 前向声明:接收路径需要把管理/控制帧交给连接状态机 */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
static void ath9k_handle_eapol(const u8 *eap, u32 len);
static int eapol_find(const u8 *frame, u32 len, u32 *out_off);
static void copy_mac(u8 dst[6], const u8 src[6]);
/* ---- 串口日志(参考 e1000 raw_log) ---- */
#define COM1 0x3F8
/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */
static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); }
static void raw_log(const char *s) {
while (*s) {
char c = *s++;
if (c == '\n') {
serial_wait_tx();
dkm_outb(COM1, '\r');
}
serial_wait_tx();
dkm_outb(COM1, (u8)c);
}
}
static void raw_hex(const char *prefix, u64 v) {
static const char h[] = "0123456789abcdef";
raw_log(prefix);
raw_log("0x");
for (int i=15; i>=0; i--) {
serial_wait_tx();
dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]);
}
raw_log("\n");
}
/* ---- PCI 配置空间 ---- */
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
dkm_pci_write(bus, dev, func, reg, v);
}
/* ath9k PCI 设备 ID 表(vendor 0x168c Atheros) */
static int ath9k_find(u8 *bus, u8 *dev) {
for (u8 d=0; d<32; d++) {
u32 vd = pci_read(0,d,0,PCI_VENDOR_ID);
if ((vd&0xffff)==0xffff) continue;
if ((vd&0xffff)!=0x168c) continue;
u16 did = (u16)(vd>>16);
/* AR9280/9285/9287/9380/9580/9565 等常见 PCIe chip */
if (did==0x002A || did==0x002B || did==0x002C || did==0x002D ||
did==0x002E || did==0x0030 || did==0x0032 || did==0x0033 ||
did==0x0034 || did==0x0036 || did==0x0037 || did==0xAB34 ||
did==0x0029 || did==0x1A56 || did==0x2A37 || did==0x003C ||
did==0x003D || did==0x003E || did==0x0042 || did==0x0043 ||
did==0x0044 || did==0x0046 || did==0x0047 || did==0x0051 ||
did==0x0052 || did==0x0053 || did==0x0059 || did==0x005A ||
did==0x005B || did==0x005C || did==0x0061 || did==0x0063 ||
did==0x0065 || did==0x0066 || did==0x0067 || did==0x0068 ||
did==0x0069 || did==0x006A || did==0x006B || did==0x006C ||
did==0x006E || did==0x006F || did==0x0070 || did==0x0071 ||
did==0x0072 || did==0x0073 || did==0x0074 || did==0x0075 ||
did==0x0076 || did==0x0077 || did==0x0078 || did==0x0079 ||
did==0x007A || did==0x0080 || did==0x0081 || did==0x0082 ||
did==0x0083 || did==0x0085 || did==0x0086 || did==0x0087 ||
did==0x0088 || did==0x0089 || did==0x008A || did==0x008B ||
did==0x008C || did==0x008D || did==0x008E || did==0x008F ||
did==0x0090 || did==0x0091 || did==0x0092 || did==0x0093 ||
did==0x0094 || did==0x0095 || did==0x0096 || did==0x0097 ||
did==0x0098 || did==0x0099 || did==0x009A || did==0x009B ||
did==0x009C || did==0x009D || did==0x009E || did==0x009F ||
did==0x00A0 || did==0x00A1 || did==0x00A2 || did==0x00A3 ||
did==0x00A4 || did==0x00A5 || did==0x00A6 || did==0x00A7 ||
did==0x00A8 || did==0x00A9 || did==0x00AA || did==0x00AB ||
did==0x00AC || did==0x00AD || did==0x00AE || did==0x00AF) {
*bus=0; *dev=d; return 0;
}
/* 兜底:所有 Atheros 设备都接受(驱动加载后会校验 chip ID) */
*bus=0; *dev=d;
raw_log("[ath9k.raw] found Atheros device, did=0x");
static const char hh[] = "0123456789abcdef";
char hex[5];
hex[0]=hh[(did>>12)&0xf]; hex[1]=hh[(did>>8)&0xf];
hex[2]=hh[(did>>4)&0xf]; hex[3]=hh[did&0xf]; hex[4]=0;
raw_log(hex); raw_log("\n");
return 0;
}
/* 无匹配 Atheros 设备:打印本机所有无线控制器 (class 02:80) 的
* vendor:device,便于确认实机卡型号(ATH 硬件要求见 CLAUDE.md)。 */
{
u32 found = 0;
for (u8 d = 0; d < 32; d++) {
u32 vd = pci_read(0, d, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u32 cls = pci_read(0, d, 0, 0x08);
if (((cls >> 24) & 0xff) != 0x02 || (((cls >> 16) & 0xff) != 0x80)) continue;
raw_hex("[ath9k.raw] wireless ctrl (non-Atheros) dev=", (u64)d);
raw_hex("[ath9k.raw] vendor:device=", ((u64)(vd & 0xffff) << 16) | (u64)(vd >> 16));
found++;
}
raw_hex("[ath9k.raw] wireless controllers found=", (u64)found);
raw_log("[ath9k.raw] NO SUPPORTED ATHEROS DEVICE (need AR928x PCIe, vendor 168c)\n");
}
return -1;
}
/* ---- MMIO 寄存器读写 ---- */
static u32 ath9k_reg_read(u32 reg) {
if (!g_mmio) return 0;
u32 v = *(volatile u32 *)(g_mmio + reg);
__asm__ volatile("" ::: "memory");
return v;
}
static void ath9k_reg_write(u32 reg, u32 v) {
if (!g_mmio) return;
*(volatile u32 *)(g_mmio + reg) = v;
__asm__ volatile("" ::: "memory");
}
static void ath9k_reg_rmw(u32 reg, u32 set, u32 clr) {
u32 v = ath9k_reg_read(reg);
v &= ~clr;
v |= set;
ath9k_reg_write(reg, v);
}
/* ---- EEPROM 读取(AR5416/AR9280 I2C-style EEPROM 接口) ---- */
static u16 ath9k_eeprom_read(u16 addr) {
/* AR5416_EEPROM_CMD: bit0=READ, bit1=BUSY, bit2=RESET */
/* 启动 read:写 cmd=READ | addr<<3 */
ath9k_reg_write(AR_EEPROM_CMD, AR_EEPROM_CMD_RESET);
dkm_delay_us(5);
u32 cmd = AR_EEPROM_CMD_READ | ((u32)(addr & 0x3FF) << 3);
ath9k_reg_write(AR_EEPROM_CMD, cmd);
/* 等待 RDBUSY 清除 (实机: 100ms TSC 超时) */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 sts = ath9k_reg_read(AR_EEPROM_STS);
if (!(sts & AR_EEPROM_STS_RDBUSY)) {
if (sts & AR_EEPROM_STS_VALID) {
return (u16)(sts & AR_EEPROM_STS_DATA_M);
}
return 0xffff;
}
dkm_delay_us(1);
}
return 0xffff;
}
/* ---- 软件复位 + RTC 唤醒 ---- */
static int ath9k_reset(void) {
raw_log("[ath9k.raw] reset sequence\n");
/* 1. 强制唤醒(Power-On) */
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
dkm_delay_us(50);
/* 2. 等待 RTC 进入 ON 状态 (实机: 100ms TSC 超时) */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 s = ath9k_reg_read(AR_RTC_STATUS);
if ((s & 0x3) == AR_RTC_STATUS_ON) break;
dkm_delay_us(1);
}
/* 3. 全 chip 复位 */
ath9k_reg_write(AR_RC, AR_RC_MAC | AR_RC_BB | AR_RC_RPCU);
dkm_delay_us(100);
ath9k_reg_write(AR_RC, 0);
dkm_delay_us(100);
/* 4. 重新唤醒 */
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
dkm_delay_us(50);
/* 5. 等待 RTC ON (实机: 100ms TSC 超时) */
u32 ok = 0;
u64 deadline2 = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline2) {
u32 s = ath9k_reg_read(AR_RTC_STATUS);
if ((s & 0x3) == AR_RTC_STATUS_ON) { ok = 1; break; }
dkm_delay_us(1);
}
if (!ok) { raw_log("[ath9k.raw] reset: RTC not ON\n"); return -1; }
raw_log("[ath9k.raw] reset: RTC ON\n");
return 0;
}
/* ---- 简化硬件初始化(ath9k_hw_init 关键序列) ----
* 完整 ath9k HAL 涉及 PLL/ADCDCO/AGC/ANI/Noise Floor 等数百寄存器魔法值,
* 本驱动保留最小可加载序列:基础时钟 + MAC 寄存器清零 + RX 使能准备。
* 真实扫描路径在硬件验证后增量完善。
*/
static int ath9k_hw_init(void) {
raw_log("[ath9k.raw] hw_init minimal\n");
/* 关闭 RX/DMA,清 STA_ID1 */
ath9k_reg_write(AR_CR, AR_CR_RXD);
dkm_delay_us(10);
ath9k_reg_write(AR_STA_ID1, 0);
/* 清中断 */
ath9k_reg_write(AR_ISR, 0xFFFFFFFFu);
ath9k_reg_read(AR_ISR_RAC);
/* 写 MAC 地址到 STA_ID0/1(驱动本地缓存) */
u32 mac_lo = (u32)g_mac[0] | ((u32)g_mac[1] << 8) |
((u32)g_mac[2] << 16) | ((u32)g_mac[3] << 24);
u32 mac_hi = (u32)g_mac[4] | ((u32)g_mac[5] << 8);
ath9k_reg_write(AR_STA_ID0, mac_lo);
ath9k_reg_rmw(AR_STA_ID1, (mac_hi & 0xFFFF), 0xFFFFu);
raw_log("[ath9k.raw] hw_init: MAC written\n");
return 0;
}
/* ---- RX 环初始化 ---- */
static int ath9k_rx_init(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
/* 分配 RX 描述符环(1页 = 4096 字节,足够 16 个 32B 描述符) */
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_rx_desc_dma) != 0) {
raw_log("[ath9k.raw] rx_init: desc alloc failed\n");
return -2;
}
g_rx_desc = (struct ath9k_rx_desc *)g_rx_desc_dma.virt;
/* 清零描述符 */
for (u32 i = 0; i < sizeof(struct ath9k_rx_desc) * ATH9K_RX_DESC_COUNT; i++) {
((u8 *)g_rx_desc)[i] = 0;
}
/* 分配每个 RX 缓冲区 */
for (u32 i = 0; i < ATH9K_RX_DESC_COUNT; i++) {
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_rx_buf_dma[i]) != 0) {
raw_log("[ath9k.raw] rx_init: buf alloc failed\n");
return -3;
}
g_rx_buf[i] = (u8 *)g_rx_buf_dma[i].virt;
/* 填描述符:link 指向下一个,data 指向缓冲区 */
g_rx_desc[i].ds_link = (u32)(g_rx_desc_dma.phys +
(((i + 1) % ATH9K_RX_DESC_COUNT) * sizeof(struct ath9k_rx_desc)));
g_rx_desc[i].ds_data = (u32)g_rx_buf_dma[i].phys;
g_rx_desc[i].ds_ctl0 = ATH9K_RX_BUF_SIZE & ATH9K_RXCTL_BUF_LEN_M;
g_rx_desc[i].ds_ctl1 = ATH9K_RXCTL_INTREQ;
g_rx_desc[i].ds_status0 = 0;
g_rx_desc[i].ds_status1 = 0;
g_rx_desc[i].ds_status2 = 0;
g_rx_desc[i].ds_status3 = 0;
}
__asm__ volatile("" ::: "memory");
raw_log("[ath9k.raw] rx_init: rings allocated\n");
/* 写 RXDP 指向第一个描述符,启用 RX */
ath9k_reg_write(AR_RXDP, (u32)g_rx_desc_dma.phys);
ath9k_reg_write(AR_RXCFG, 0); /* 默认 RX 配置 */
ath9k_reg_write(AR_MIBC, 0); /* 关闭 MIB 计数器,避免中断风暴 */
/* 使能 RXOK/RXERR 中断 */
ath9k_reg_write(AR_IMR, AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXORN);
/* 启动 RX DMA */
ath9k_reg_write(AR_CR, AR_CR_RXE);
g_rx_head = 0;
g_rx_ready = 1;
raw_log("[ath9k.raw] rx_init: RX enabled\n");
return 0;
}
/* ---- 802.11 beacon IE 解析 ----
* 从 beacon/probe-response 帧中提取 SSID、信道、加密类型。
* 返回 0 = 解析成功,-1 = 非 beacon,-2 = 太短
*/
static int parse_beacon(const u8 *frame, u32 len, struct ath9k_scan_result *out) {
if (len < 24 + 12) return -2;
/* frame_control(2) + duration(2) + DA(6) + SA(6) + BSSID(6) + seq(2) = 24 */
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
u16 ftype = fctl & 0x000Cu;
u16 fsubtype = fctl & 0x00F0u;
if (ftype != IEEE80211_FTYPE_MGMT) return -1;
if (fsubtype != IEEE80211_STYPE_BEACON && fsubtype != IEEE80211_STYPE_PROBE_RESP) return -1;
/* BSSID 在 offset 16 */
for (int i = 0; i < 6; i++) out->bssid[i] = frame[16 + i];
/* fixed: timestamp(8) + beacon_interval(2) + capability(2) = 12 bytes */
u32 ie_off = 24 + 12;
/* 默认值 */
out->ssid[0] = 0;
out->channel = 0;
out->security = DKM_NET_SEC_OPEN;
/* 遍历 IE */
while (ie_off + 2 <= len) {
u8 eid = frame[ie_off];
u8 elen = frame[ie_off + 1];
if (ie_off + 2 + elen > len) break;
const u8 *edata = frame + ie_off + 2;
if (eid == WLAN_EID_SSID && elen > 0 && elen <= ATH9K_SSID_MAX) {
for (u8 i = 0; i < elen; i++) out->ssid[i] = (char)edata[i];
out->ssid[elen] = 0;
} else if (eid == WLAN_EID_SSID && elen == 0) {
out->ssid[0] = 0; /* hidden SSID */
} else if (eid == WLAN_EID_DS_PARAMS && elen >= 1) {
out->channel = edata[0];
} else if (eid == WLAN_EID_RSN && elen >= 2) {
/* RSN IE → WPA2 (或 WPA3 由 version 决定) */
u16 version = (u16)edata[0] | ((u16)edata[1] << 8);
if (version == 1) out->security = DKM_NET_SEC_WPA2;
else if (version == 2) out->security = DKM_NET_SEC_WPA3;
else out->security = DKM_NET_SEC_WPA2;
} else if (eid == WLAN_EID_VENDOR && elen >= 4) {
/* Microsoft WPA IE: OUI 00:50:F2 + type 1 */
if (edata[0]==0x00 && edata[1]==0x50 && edata[2]==0xF2 && edata[3]==0x01) {
if (out->security < DKM_NET_SEC_WPA) out->security = DKM_NET_SEC_WPA;
}
}
ie_off += 2 + elen;
}
return 0;
}
/* 查找已扫描 BSSID 是否已记录,返回索引或 -1 */
static int find_bssid(const u8 bssid[6]) {
for (u32 i = 0; i < g_scan_count; i++) {
int match = 1;
for (int j = 0; j < 6; j++) if (g_scan_results[i].bssid[j] != bssid[j]) { match = 0; break; }
if (match) return (int)i;
}
return -1;
}
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) {
if (len < 24) return;
u16 fctl = (u16)buf[0] | ((u16)buf[1] << 8);
u16 ftype = fctl & 0x000C;
/* 连接状态机:管理帧(认证/关联响应)与数据帧中的 EAPOL-Key */
if (ftype == 0x0000) {
if (ath9k_handle_mgmt(buf, len) == 0) return;
} else if (ftype == 0x0008) { /* data */
u32 eoff = 0;
if (eapol_find(buf, len, &eoff) == 0)
ath9k_handle_eapol(buf + eoff, len - eoff);
/* 数据帧继续走 beacon 解析(多数会被拒绝) */
}
struct ath9k_scan_result tmp;
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
if (parse_beacon(buf, len, &tmp) != 0) return;
/* 跳过隐藏 SSID(长度为 0) */
if (tmp.ssid[0] == 0) return;
tmp.rssi = rssi;
/* 去重:BSSID 已存在则更新 RSSI */
int existing = find_bssid(tmp.bssid);
if (existing >= 0) {
/* 更新 RSSI(取较强值) */
if (rssi > g_scan_results[existing].rssi) g_scan_results[existing].rssi = rssi;
return;
}
if (g_scan_count >= ATH9K_MAX_SCAN_RESULTS) return;
/* 追加 */
g_scan_results[g_scan_count] = tmp;
g_scan_count++;
}
/* 从 RX 环轮询所有已完成描述符 */
static void ath9k_rx_drain(void) {
if (!g_rx_ready) return;
for (u32 i = 0; i < ATH9K_RX_DESC_COUNT; i++) {
u32 idx = g_rx_head;
struct ath9k_rx_desc *d = &g_rx_desc[idx];
__asm__ volatile("" ::: "memory");
if (!(d->ds_status0 & ATH9K_RXSTATUS_DONE)) break;
if (d->ds_status0 & ATH9K_RXSTATUS_OK) {
u32 flen = (d->ds_status0 & ATH9K_RXSTATUS_FRAME_LEN_M) >> ATH9K_RXSTATUS_FRAME_LEN_S;
if (flen > 0 && flen <= ATH9K_RX_BUF_SIZE) {
/* RSSI 在 ds_status2 低 8 位(ath9k convention,负值表示) */
i8 rssi = (i8)(d->ds_status2 & 0xFF);
ath9k_handle_rx_frame(g_rx_buf[idx], flen, rssi);
}
}
/* 重置描述符,归还给硬件 */
d->ds_status0 = 0;
d->ds_status1 = 0;
d->ds_status2 = 0;
d->ds_status3 = 0;
d->ds_ctl0 = ATH9K_RX_BUF_SIZE & ATH9K_RXCTL_BUF_LEN_M;
d->ds_ctl1 = ATH9K_RXCTL_INTREQ;
__asm__ volatile("" ::: "memory");
g_rx_head = (g_rx_head + 1) % ATH9K_RX_DESC_COUNT;
}
}
/* ---- 信道切换(简化版) ----
* 完整 ath9k 信道切换涉及 PLL/RFBUS/ChannelFraction/RFBank 等数十寄存器,
* 本驱动仅写最小序列:PHY RFBUS 请求 + Channel 寄存器占位。
* 真实硬件验证后增量完善。
*/
static void ath9k_set_channel(u8 channel) {
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel)。
* 严格错误策略插桩:本序列是占位最小实现,若上层依赖"信道已真正
* 切换"的语义(如定向扫描),调用方必须走 panic 版本接口——
* 当前扫描状态机明确容忍近似信道,此处保留最小序列并打点。 */
raw_hex("[ath9k] set_channel (minimal sequence), ch=", channel);
/* 触发 RFBUS 请求,等待 grant */
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
dkm_delay_us(5);
for (u32 i = 0; i < 1000; i++) {
if (ath9k_reg_read(AR_PHY_RFBUS_REQ) & 0x2) break;
dkm_delay_us(1);
}
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0);
dkm_delay_us(50);
}
/* ---- 被动扫描状态机 ----
* 遍历 2.4GHz 信道 1-13,每个信道停留 ~400ms 监听 beacon。
* 完成后 g_scan_count 包含所有发现的 SSID。
*/
static int ath9k_scan_start_impl(void *ctx) {
(void)ctx;
raw_log("[ath9k.raw] scan_start\n");
if (!g_rx_ready) return -1;
g_scanning = 1;
g_scan_count = 0;
for (u32 i = 0; i < ATH9K_MAX_SCAN_RESULTS; i++) {
for (int j = 0; j < (int)sizeof(g_scan_results[i]); j++) {
((u8 *)&g_scan_results[i])[j] = 0;
}
}
for (u32 c = 0; c < ATH9K_CHAN_2G_COUNT; c++) {
u8 channel = (u8)(c + 1);
ath9k_set_channel(channel);
raw_log("[ath9k.raw] scan ch=");
static const char hh[] = "0123456789";
char buf[4]; buf[0]=hh[channel/10]; buf[1]=hh[channel%10]; buf[2]='\n'; buf[3]=0;
raw_log(buf);
/* 在该信道停留 ~400ms,期间轮询 RX 环 */
for (u32 tick = 0; tick < 400; tick++) {
ath9k_rx_drain();
dkm_delay_us(1000);
}
}
g_scanning = 0;
raw_log("[ath9k.raw] scan done, results=");
static const char hh2[] = "0123456789";
char cntbuf[4]; cntbuf[0]=hh2[g_scan_count/10]; cntbuf[1]=hh2[g_scan_count%10]; cntbuf[2]='\n'; cntbuf[3]=0;
raw_log(cntbuf);
return 0;
}
static int ath9k_scan_count_impl(void *ctx) {
(void)ctx;
return (int)g_scan_count;
}
static int ath9k_scan_result_impl(void *ctx, u32 n, struct dkm_net_scan_result *out) {
(void)ctx;
if (!out) return -1;
if (n >= g_scan_count) return -2;
/* 内部 ath9k_scan_result 与 dkm_net_scan_result 布局一致 */
const u8 *src = (const u8 *)&g_scan_results[n];
u8 *dst = (u8 *)out;
for (u32 i = 0; i < sizeof(struct dkm_net_scan_result); i++) dst[i] = src[i];
return 0;
}
static int ath9k_is_wireless_impl(void *ctx) {
(void)ctx;
return 1;
}
/* ============================================================
* TX 路径 + WPA2-PSK 连接状态机
*
* 说明:TX 描述符采用 AR9002(AR9280/9285)队列描述符布局的
* 最小可用子集;位域与 HAL initvals 需在实机 bring-up 时用
* 串口日志对拍(日志点见 ath9k_tx_frame)。协议层(认证/关联/
* 4-way/密钥安装)由 wpa2_crypto 提供并通过 wpa2_kat 自检。
* ============================================================ */
/* TX 队列(BE)寄存器 */
#define AR_TXDP(_q) (0x0000u + 0x0800u * (_q)) /* TXQ 描述符基址 */
#define AR_TXE 0x0800u
#define AR_TXCFG 0x0008u
#define AR_TXCFG_TXE 0x00000004u
#define ATH9K_TX_DESC_COUNT 8u
#define ATH9K_TX_BUF_SIZE 2048u
/* AR9002 TX 队列描述符(32 字节,最小字段集) */
struct ath9k_tx_desc {
u32 ds_link; /* 下一描述符物理地址 */
u32 ds_data; /* 数据缓冲物理地址 */
u32 ds_ctl0; /* FRAME_LEN(0:12) | TXQ(12:16) */
u32 ds_ctl1; /* BUF_LEN(0:12) | MORE(12) */
u32 ds_ctl2; /* RTS/CTS + 速率索引(0 = 默认 1M CCK) */
u32 ds_ctl3; /* 分片/重试(0) */
u32 ds_ctl4;
u32 ds_ctl5;
} __attribute__((packed));
#define ATH9K_TXCTL0_LEN_S 0
#define ATH9K_TXCTL0_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_LEN_S 0
#define ATH9K_TXCTL1_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_MORE 0x00001000u
/* ---- 连接状态 ---- */
#define WPA_STATE_IDLE 0
#define WPA_STATE_AUTH_SENT 1
#define WPA_STATE_ASSOC_SENT 2
#define WPA_STATE_M1_WAIT 3
#define WPA_STATE_M3_WAIT 4
#define WPA_STATE_CONNECTED 5
static struct dkm_dma_buffer g_tx_desc_dma;
static struct ath9k_tx_desc *g_tx_desc;
static struct dkm_dma_buffer g_tx_buf_dma;
static u8 *g_tx_buf;
static u32 g_tx_head;
static int g_tx_ready;
static const struct dkm_dma_api *g_dma;
/* WPA2 会话 */
static u8 g_sta_mac[6]; /* SPA */
static u8 g_ap_mac[6]; /* AA / BSSID */
static u8 g_channel;
static char g_target_ssid[ATH9K_SSID_MAX + 1];
static int g_wpa_state;
static u8 g_anonce[32];
static u8 g_snonce[32];
static u8 g_pmk[WPA2_PMK_LEN];
static u8 g_ptk[64]; /* KCK(16) KEK(16) TK(16) + 余量 */
static u64 g_ptk_replay;
static u64 g_tx_pn; /* CCMP 发包序号 */
static u16 g_seq;
static int g_key_installed;
#define ATH9K_STA_MAC_ID0 0x0800u
/* ---- 802.11 管理帧构造 ---- */
#define IEEE80211_STYPE_AUTH 0x00B0u
#define IEEE80211_STYPE_ASSOC_REQ 0x0000u
#define IEEE80211_STYPE_ASSOC_RESP 0x0010u
#define IEEE80211_STYPE_REASSOC_REQ 0x0020u
#define IEEE80211_STYPE_DEAUTH 0x00C0u
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_ERP 42
#define WLAN_EID_RSN 48
static u32 put_le16(u8 *p, u16 v) { p[0]=(u8)v; p[1]=(u8)(v>>8); return 2; }
/* 帧控制字(管理帧,非保护) */
static void mgmt_fc(u8 *f, u16 subtype) {
u16 fc = 0x0000u | subtype; /* proto 0, type=mgmt(0) */
f[0] = (u8)(fc & 0xFF);
f[1] = (u8)(fc >> 8);
f[2] = 0; f[3] = 0; /* duration */
}
/* 填 24 字节管理帧头:DA / SA / BSSID / seq */
static void mgmt_hdr(u8 *f, const u8 *da, const u8 *sa, const u8 *bssid) {
for (int i = 0; i < 6; i++) f[4+i] = da[i];
for (int i = 0; i < 6; i++) f[10+i] = sa[i];
for (int i = 0; i < 6; i++) f[16+i] = bssid[i];
u16 sc = (u16)((++g_seq & 0xFFF) << 4);
f[22] = (u8)(sc & 0xFF); f[23] = (u8)(sc >> 8);
}
/* WPA2-PSK RSN IE(AKM 00-0F-AC:2,CCMP) */
static u32 rsn_ie_psk(u8 *p) {
u8 *q = p;
q[0] = WLAN_EID_RSN;
q[1] = 20; /* 长度 */
q += 2;
q += put_le16(q, 1); /* version */
/* group cipher: OUI 00-0F-AC + type 4 (CCMP) */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* pairwise count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* AKM count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x02; q += 4; /* PSK */
q += put_le16(q, 0); /* RSN caps */
return (u32)(q - p);
}
/* 发送一帧(构造 802.11 头 → TX 环 → 触发) */
static int ath9k_tx_frame(const u8 *frame, u32 len);
/* ---- 认证请求(open system) ---- */
static int ath9k_send_auth(void) {
u8 f[30 + 4];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_AUTH);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
/* auth body: alg(2)=0, seq(2)=1, status(2)=0 */
put_le16(f + 24, 0);
put_le16(f + 26, 1);
put_le16(f + 28, 0);
raw_log("[ath9k.raw] tx auth request\n");
return ath9k_tx_frame(f, 30);
}
/* ---- 关联请求(含 RSN IE) ---- */
static int ath9k_send_assoc(void) {
u8 f[24 + 4 + 2 + 34 + 4 + 24 + 2];
u32 n = 0;
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_ASSOC_REQ);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
n = 24;
n += put_le16(f + n, 0x0431); /* capability: ESS + privacy + short preamble */
n += put_le16(f + n, 10); /* listen interval */
/* SSID IE */
{
u32 sl = 0;
while (g_target_ssid[sl]) sl++;
f[n++] = WLAN_EID_SSID;
f[n++] = (u8)sl;
for (u32 i = 0; i < sl; i++) f[n++] = (u8)g_target_ssid[i];
}
/* supported rates: 1,2,5.5,11,6,9,12,18 Mbps */
f[n++] = WLAN_EID_SUPP_RATES;
f[n++] = 8;
f[n++] = 0x82; f[n++] = 0x84; f[n++] = 0x8B; f[n++] = 0x96;
f[n++] = 0x0C; f[n++] = 0x12; f[n++] = 0x18; f[n++] = 0x24;
/* RSN IE */
n += rsn_ie_psk(f + n);
raw_log("[ath9k.raw] tx assoc request\n");
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key 构造(802.1X 4-way) ---- */
#define EAPOL_VERSION 2
#define EAPOL_TYPE_KEY 3
#define WPA_KEY_DESC_TYPE 2
#define WPA_KEY_INFO_KEY_TYPE 0x0008u /* 1 = pairwise */
#define WPA_KEY_INFO_INSTALL 0x0040u
#define WPA_KEY_INFO_ACK 0x0080u
#define WPA_KEY_INFO_MIC 0x0100u
#define WPA_KEY_INFO_SECURE 0x0200u
#define WPA_KEY_INFO_ENC_KEY 0x1000u
#define WPA_KEY_INFO_VER2 0x0002u /* WPA2 = 3 (HMAC-SHA1 + AES) */
/* 构造 802.11 数据帧(toDS)+ LLC/SNAP + EAPOL-Key,做 MIC 后发送。
* key_info 由调用方给,nonce 指针可为 0(M4 用)。 */
static int ath9k_send_eapol(u16 key_info, const u8 *nonce,
const u8 *key_data, u32 kd_len) {
static u8 f[256];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* 802.11 data 头:toDS(AP 为 RA=A1,SA=A2,DA=A3=BSSID) */
u16 fc = 0x0100u | 0x0008u; /* data + ToDS */
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
u32 n = 24;
/* LLC/SNAP:AA AA 03 00 00 00 88 8E (EAPOL) */
static const u8 snap[8] = {0xAA,0xAA,0x03,0x00,0x00,0x00,0x88,0x8E};
for (int i = 0; i < 8; i++) f[n++] = snap[i];
/* EAPOL 头 */
u32 eapol_off = n;
f[n++] = EAPOL_VERSION;
f[n++] = EAPOL_TYPE_KEY;
u32 len_off = n; n += 2; /* 长度稍后回填 */
/* EAPOL-Key 体 */
u32 mic_off;
f[n++] = WPA_KEY_DESC_TYPE;
n += put_le16(f + n, key_info);
n += put_le16(f + n, 16); /* key length (CCMP) */
for (int i = 0; i < 8; i++) f[n++] = (u8)(g_ptk_replay >> (i * 8));
for (int i = 0; i < 32; i++) f[n++] = nonce ? nonce[i] : 0; /* nonce */
n += 16; /* key IV(全 0) */
n += 8; /* key RSC */
n += 8; /* key ID(全 0) */
mic_off = n; n += 16; /* key MIC(待填) */
n += put_le16(f + n, (u16)kd_len);
for (u32 i = 0; i < kd_len; i++) f[n++] = key_data[i];
/* 回填 EAPOL 长度(含 4 字节 EAPOL 头) */
u16 elen = (u16)(n - eapol_off - 4);
f[len_off] = (u8)(elen & 0xFF); f[len_off + 1] = (u8)(elen >> 8);
/* MIC:若置位,对 EAPOL 帧(MIC 字段已清零)用 KCK 计算 HMAC-SHA1-128 */
if (key_info & WPA_KEY_INFO_MIC) {
u8 mic[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, f + eapol_off, n - eapol_off, mic);
for (int i = 0; i < 16; i++) f[mic_off + i] = mic[i];
}
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key M2(SNonce + MIC,4-way 第 2 条) ---- */
static int ath9k_send_m2(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
u8 kd[2 + 20]; /* RSN IE(WPA2 常见,可省) */
u32 kdn = rsn_ie_psk(kd);
raw_log("[ath9k.raw] tx EAPOL M2\n");
return ath9k_send_eapol(ki, g_snonce, kd, kdn);
}
/* ---- EAPOL-Key M4(MIC + secure,4-way 第 4 条) ---- */
static int ath9k_send_m4(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE |
WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
raw_log("[ath9k.raw] tx EAPOL M4\n");
return ath9k_send_eapol(ki, 0, 0, 0);
}
/* ---- PTK 派生(802.11i H.4) ---- */
static void ath9k_derive_ptk(void) {
u8 seed[64];
const u8 *aa = g_ap_mac, *spa = g_sta_mac;
int swap = 0;
for (int i = 0; i < 6; i++) {
if (aa[i] != spa[i]) { swap = aa[i] > spa[i]; break; }
}
const u8 *m1 = swap ? spa : aa; /* min(AA,SPA) */
const u8 *m2 = swap ? aa : spa; /* max(AA,SPA) */
for (int i = 0; i < 6; i++) { seed[i] = m1[i]; seed[6+i] = m2[i]; }
int nswap = 0;
for (int i = 0; i < 32; i++) {
if (g_anonce[i] != g_snonce[i]) { nswap = g_anonce[i] > g_snonce[i]; break; }
}
const u8 *n1 = nswap ? g_snonce : g_anonce;
const u8 *n2 = nswap ? g_anonce : g_snonce;
for (int i = 0; i < 32; i++) { seed[12+i] = n1[i]; seed[44+i] = n2[i]; }
wpa2_prf512(g_pmk, seed, 64, g_ptk);
}
/* ---- 扫描结果查找(按 SSID 找 BSSID/信道) ---- */
static int ath9k_find_ssid(const char *ssid, u8 *bssid, u8 *channel, u8 *sec) {
for (u32 i = 0; i < g_scan_count; i++) {
const char *a = g_scan_results[i].ssid;
const char *b = ssid;
while (*a && *b && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
for (int j = 0; j < 6; j++) bssid[j] = g_scan_results[i].bssid[j];
*channel = g_scan_results[i].channel;
*sec = g_scan_results[i].security;
return 0;
}
}
return -1;
}
/* ---- 随机 SNonce(TSC 混合,实机 TLS 级随机由上层补充) ---- */
static void ath9k_make_snonce(void) {
u64 t = dkm_rdtsc();
for (int i = 0; i < 32; i++) {
t = t * 6364136223846793005ULL + 1442695040888963407ULL;
g_snonce[i] = (u8)(t >> ((i % 8) * 8));
}
}
/* ---- 连接入口 ---- */
static int ath9k_connect_impl(void *ctx, const char *ssid, const char *password) {
(void)ctx;
if (!ssid) return -1;
raw_log("[ath9k.raw] connect request\n");
if (!g_tx_ready) { raw_log("[ath9k.raw] TX not ready\n"); return -2; }
u8 sec = 0;
if (ath9k_find_ssid(ssid, g_ap_mac, &g_channel, &sec) != 0) {
raw_log("[ath9k.raw] connect: SSID not in scan results\n");
return -3; /* 调用方先扫描 */
}
u32 sl = 0;
while (ssid[sl] && sl < ATH9K_SSID_MAX) { g_target_ssid[sl] = ssid[sl]; sl++; }
g_target_ssid[sl] = 0;
copy_mac(g_sta_mac, g_mac);
/* 需要 WPA2-PSK 时先算 PMK */
if (sec != DKM_NET_SEC_OPEN) {
if (!password || !password[0]) { raw_log("[ath9k.raw] connect: empty PSK\n"); return -4; }
wpa2_pbkdf2_sha1(password, (const u8 *)g_target_ssid, sl, g_pmk);
raw_log("[ath9k.raw] PMK derived\n");
}
ath9k_set_channel(g_channel);
g_key_installed = 0;
g_ptk_replay = 0;
g_tx_pn = 0;
g_wpa_state = WPA_STATE_AUTH_SENT;
if (ath9k_send_auth() != 0) { g_wpa_state = WPA_STATE_IDLE; return -5; }
return 0;
}
static int ath9k_disconnect_impl(void *ctx) {
(void)ctx;
raw_log("[ath9k.raw] disconnect\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
/* ---- 管理/控制帧处理(连接状态机驱动) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
/* ath9k TX:把帧写进 TX 环并触发 DMA。
* 描述符位域按 AR9002 队列描述符最小集构造;实机对拍日志见下方 raw_hex。
* 上层把数据帧交给本驱动时,若尚未安装密钥(未连接)则 panic(ATH-E04)。 */
static int ath9k_tx_frame(const u8 *frame, u32 len) {
if (!g_tx_ready || !g_tx_desc) return -1;
if (len > ATH9K_TX_BUF_SIZE) return -1;
u32 idx = g_tx_head;
u8 *buf = g_tx_buf + (u64)idx * ATH9K_TX_BUF_SIZE;
for (u32 i = 0; i < len; i++) buf[i] = frame[i];
struct ath9k_tx_desc *d = &g_tx_desc[idx];
d->ds_data = (u32)(g_tx_buf_dma.phys + (u64)idx * ATH9K_TX_BUF_SIZE);
d->ds_ctl0 = (len & ATH9K_TXCTL0_LEN_M); /* FRAME_LEN,TXQ=0(BE) */
d->ds_ctl1 = (len & ATH9K_TXCTL1_LEN_M); /* BUF_LEN,无 MORE */
d->ds_ctl2 = 0;
d->ds_ctl3 = 0;
__asm__ volatile("" ::: "memory");
/* 触发 TX:写 TXDP 指向队首描述符并置 TXE */
ath9k_reg_write(AR_TXDP(0), (u32)g_tx_desc_dma.phys);
ath9k_reg_write(AR_TXCFG, AR_TXCFG_TXE);
g_tx_head = (g_tx_head + 1) % ATH9K_TX_DESC_COUNT;
raw_hex("[ath9k] tx kick len=", len);
return 0;
}
/* ---- 数据帧 TX(已连接后由 net 栈调用;CCMP 加密) ---- */
static int ath9k_tx_impl(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_key_installed) {
/* 未安装密钥时不允许数据帧上路:零降级 panic(ATH-E04) */
g_log->panic("ATH-E04 DATA TX BEFORE KEY INSTALL");
return -1;
}
static u8 f[ATH9K_TX_BUF_SIZE];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* toDS 数据帧头:A1=BSSID, A2=STA, A3=DA(未解析,用 BSSID 占位) */
u16 fc = 0x0100u | 0x0008u;
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
int rc = wpa2_ccmp_encrypt(f, sizeof(f), (const u8 *)packet, length,
g_ptk + WPA2_KCK_LEN + WPA2_KEK_LEN, ++g_tx_pn);
if (rc <= 0) return -1;
return ath9k_tx_frame(f, (u32)rc);
}
/* ---- 4-way 消息处理 ---- */
/* 定位 802.11 数据帧中的 EAPOL-Key(返回 EAPOL 起始偏移,-1 未找到) */
static int eapol_find(const u8 *frame, u32 len, u32 *out_off) {
/* 24B MAC 头 + 8B LLC/SNAP,可能带 8B CCMP 头(加密 EAPOL 场景) */
u32 off = 24;
if (len < off + 8) return -1;
if (frame[off] == 0xAA && frame[off+1] == 0xAA) {
/* 明文 EAPOL */
} else if (len >= off + 16) {
off += 8; /* 跳过 CCMP 头 */
if (frame[off] != 0xAA) return -1;
} else {
return -1;
}
if (off + 8 + 4 > len) return -1;
if (frame[off+6] != 0x88 || frame[off+7] != 0x8E) return -1; /* ethertype EAPOL */
if (frame[off+8] != EAPOL_VERSION || frame[off+9] != EAPOL_TYPE_KEY) return -1;
*out_off = off + 8;
return 0;
}
static void ath9k_handle_eapol(const u8 *eap, u32 len) {
/* eap 指向 EAPOL 头(version,type,length,然后 Key 体) */
if (len < 4 + 1 + 2 + 2 + 8 + 32 + 16 + 8 + 8 + 16 + 2) return;
const u8 *k = eap + 4; /* Key descriptor 起始 */
u16 key_info = (u16)k[1] | ((u16)k[2] << 8);
const u8 *nonce = k + 13; /* key nonce 偏移:1(desc)+2(info)+2(len)+8(replay) */
const u8 *mic = k + 13 + 32 + 16 + 8 + 8;
u16 kd_len = (u16)k[13 + 32 + 16 + 8 + 8 + 16] | ((u16)k[13+32+16+8+8+16+1] << 8);
const u8 *kd = mic + 16 + 2;
int pairwise = (key_info & WPA_KEY_INFO_KEY_TYPE) != 0;
int ack = (key_info & WPA_KEY_INFO_ACK) != 0;
int has_mic = (key_info & WPA_KEY_INFO_MIC) != 0;
if (!pairwise) {
/* GTK 消息(M3 附带),此处只做 MIC 校验后记录,实际 GTK 由 M3 的 key data 携带 */
raw_log("[ath9k.raw] EAPOL GTK msg\n");
return;
}
if (ack && !has_mic) {
/* ---- M1:带 ANonce ---- */
raw_log("[ath9k.raw] EAPOL M1 recv\n");
for (int i = 0; i < 32; i++) g_anonce[i] = nonce[i];
/* replay counter 8 字节(小端) */
g_ptk_replay = 0;
for (int i = 0; i < 8; i++) g_ptk_replay |= (u64)k[5 + i] << (i * 8);
ath9k_make_snonce();
ath9k_derive_ptk();
raw_log("[ath9k.raw] PTK derived\n");
g_wpa_state = WPA_STATE_M3_WAIT;
ath9k_send_m2();
return;
}
if (has_mic && !ack) {
/* ---- M3:校验 MIC → 安装 PTK/GTK → 回 M4 ---- */
raw_log("[ath9k.raw] EAPOL M3 recv\n");
u64 rc = 0;
for (int i = 0; i < 8; i++) rc |= (u64)k[5 + i] << (i * 8);
g_ptk_replay = rc;
/* MIC 校验:MIC 字段清零后对整段 EAPOL 计算 HMAC-SHA1-128 再比对。
* 用本地副本避免改写 RX 缓冲(const 语义)。 */
u32 mic_rel = (u32)(mic - eap);
u8 tmp[256];
if (len > sizeof(tmp)) { raw_log("[ath9k.raw] EAPOL too long\n"); return; }
for (u32 i = 0; i < len; i++) tmp[i] = eap[i];
for (int i = 0; i < 16; i++) tmp[mic_rel + i] = 0;
u8 calc[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, tmp, len, calc);
u8 diff = 0;
for (int i = 0; i < 16; i++) diff |= calc[i] ^ mic[i];
if (diff) {
raw_log("[ath9k.raw] M3 MIC MISMATCH (bad PSK?)\n");
g_wpa_state = WPA_STATE_IDLE;
return;
}
raw_log("[ath9k.raw] M3 MIC ok\n");
/* key data 中可能含 GTK(KDE 0xDD + OUI 00-0F-AC + type 1,AES key wrap)。
* 若含 ENC_KEY,用 KEK unwrap 取 GTK。 */
if ((key_info & WPA_KEY_INFO_ENC_KEY) && kd_len > 0) {
/* 遍历 KDE */
u32 p = 0;
while (p + 2 <= kd_len) {
u8 ktype = kd[p];
u8 klen = kd[p+1];
if (p + 2 + klen > kd_len) break;
if (ktype == 0xDD && klen >= 6 &&
kd[p+2] == 0x00 && kd[p+3] == 0x0F && kd[p+4] == 0xAC && kd[p+5] == 0x01) {
const u8 *wrapped = kd + p + 6;
u32 wlen = (u32)klen - 6;
u8 gtk[32];
if (wpa2_key_unwrap(g_ptk + WPA2_KCK_LEN, wrapped, wlen, gtk) == 0) {
raw_log("[ath9k.raw] GTK unwrapped\n");
} else {
raw_log("[ath9k.raw] GTK unwrap FAILED\n");
}
}
p += 2 + klen;
}
}
g_key_installed = 1;
g_wpa_state = WPA_STATE_CONNECTED;
ath9k_send_m4();
raw_log("[ath9k.raw] === CONNECTED (WPA2-PSK) ===\n");
}
}
/* ---- 管理帧处理(认证/关联响应) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len) {
if (len < 24) return -1;
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
if ((fctl & 0x000C) != 0x0000) return -1; /* 非管理帧 */
u16 subtype = fctl & 0x00F0;
/* 只处理来自目标 AP 的帧 */
for (int i = 0; i < 6; i++) if (frame[10+i] != g_ap_mac[i]) return -1;
if (subtype == 0x00B0) { /* Authentication */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+4] | ((u16)frame[24+5] << 8);
if (status != 0) { raw_log("[ath9k.raw] auth FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] auth ok\n");
if (g_wpa_state == WPA_STATE_AUTH_SENT) {
g_wpa_state = WPA_STATE_ASSOC_SENT;
ath9k_send_assoc();
}
return 0;
}
if (subtype == 0x0010) { /* Association Response */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+2] | ((u16)frame[24+3] << 8);
if (status != 0) { raw_log("[ath9k.raw] assoc FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] assoc ok, waiting 4-way\n");
g_wpa_state = WPA_STATE_M1_WAIT;
return 0;
}
if (subtype == 0x00C0) { /* Deauth */
raw_log("[ath9k.raw] deauth from AP\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
return -1;
}
/* ---- TX 环初始化 ---- */
static int ath9k_tx_init(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
g_dma = dma;
/* 描述符环 1 页 + 缓冲 1 页 */
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_desc_dma) != 0) {
raw_log("[ath9k.raw] tx_init: desc alloc failed\n");
return -2;
}
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_buf_dma) != 0) {
raw_log("[ath9k.raw] tx_init: buf alloc failed\n");
return -3;
}
g_tx_desc = (struct ath9k_tx_desc *)g_tx_desc_dma.virt;
g_tx_buf = (u8 *)g_tx_buf_dma.virt;
for (u32 i = 0; i < sizeof(struct ath9k_tx_desc) * ATH9K_TX_DESC_COUNT; i++)
((u8 *)g_tx_desc)[i] = 0;
for (u32 i = 0; i < ATH9K_TX_DESC_COUNT; i++) {
g_tx_desc[i].ds_link = (u32)(g_tx_desc_dma.phys +
(((i + 1) % ATH9K_TX_DESC_COUNT) * sizeof(struct ath9k_tx_desc)));
}
__asm__ volatile("" ::: "memory");
g_tx_head = 0;
g_tx_ready = 1;
raw_log("[ath9k.raw] tx_init: rings allocated\n");
return 0;
}
static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity;
if (out_length) *out_length = 0;
/* 被动扫描内部消费所有帧,外部 rx_poll 总是返回无数据 */
return 1;
}
static void copy_mac(u8 dst[6], const u8 src[6]) {
for (int i = 0; i < 6; i++) dst[i] = src[i];
}
/* ---- 中断处理(PIC IRQ 路由) ---- */
static int ath9k_irq_handler(u8 irq) {
(void)irq;
if (!g_mmio) return 0;
u32 isr = ath9k_reg_read(AR_ISR_RAC);
if (isr) {
ath9k_reg_write(AR_ISR, isr);
if (isr & (AR_ISR_RXOK | AR_ISR_RXERR | AR_ISR_RXORN)) {
ath9k_rx_drain();
}
}
return 0;
}
/* ---- driver_init ---- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
dkm_tsc_calibrate();
raw_log("[ath9k.raw] driver_init entered\n");
if (!api || !api->log) return -1;
g_log = api->log;
/* 1. PCI 探测 */
u8 bus, dev;
if (ath9k_find(&bus, &dev) != 0) {
raw_log("[ath9k.raw] no Atheros WiFi device found\n");
return -1;
}
raw_log("[ath9k.raw] Atheros device found\n");
/* 2. 启用 PCI 命令(MEM + Bus Master) */
u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND);
pci_write(bus, dev, 0, PCI_COMMAND, cmd | PCI_CMD_MEM | PCI_CMD_BUSM);
/* 3. 读取 BAR0 */
u32 bar0 = pci_read(bus, dev, 0, PCI_BAR0);
raw_hex("[ath9k.raw] BAR0 raw=", bar0);
if ((bar0 & 0x1) != 0) {
raw_log("[ath9k.raw] BAR0 is IO space, unsupported\n");
return -2;
}
g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0u);
/* 检查是否 64-bit BAR */
if ((bar0 & 0x6) == 0x4) {
u32 bar1 = pci_read(bus, dev, 0, PCI_BAR1);
g_bar0_phys |= ((u64)bar1 << 32);
raw_hex("[ath9k.raw] BAR0 high=", (u64)bar1);
}
raw_hex("[ath9k.raw] BAR0 phys=", g_bar0_phys);
if (g_bar0_phys >= 0x100000000ULL) {
raw_log("[ath9k.raw] BAR0 above 4G, current HHDM cannot map\n");
return -3;
}
/* 4. HHDM 映射 BAR0 */
u64 hhdm = api->hhdm_offset;
raw_hex("[ath9k.raw] HHDM=", hhdm);
g_mmio = (volatile u8 *)(g_bar0_phys + hhdm);
/* 验证 MMIO 可读:读 AR_STA_ID1 */
u32 probe = ath9k_reg_read(AR_STA_ID1);
raw_hex("[ath9k.raw] AR_STA_ID1 probe=", probe);
/* 5. 读取 PCI device ID + IRQ line */
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
g_pci_device_id = (u16)(vd >> 16);
g_pci_irq = (u8)pci_read(bus, dev, 0, PCI_IRQ_LINE);
raw_hex("[ath9k.raw] device id=", g_pci_device_id);
raw_hex("[ath9k.raw] irq line=", g_pci_irq);
/* 6. 软件复位 */
if (ath9k_reset() != 0) {
raw_log("[ath9k.raw] reset failed\n");
return -4;
}
/* 7. EEPROM 读取 MAC 地址 */
/* AR5416 EEPROM 偏移:MAC 地址在 word 0x1D-0x1F(6 字节) */
u16 m0 = ath9k_eeprom_read(0x1D);
u16 m1 = ath9k_eeprom_read(0x1E);
u16 m2 = ath9k_eeprom_read(0x1F);
if (m0 == 0xffff && m1 == 0xffff && m2 == 0xffff) {
raw_log("[ath9k.raw] EEPROM MAC all-FF, fallback to all-zero\n");
for (int i = 0; i < 6; i++) g_mac[i] = 0;
} else {
g_mac[0] = (u8)(m0 & 0xFF);
g_mac[1] = (u8)(m0 >> 8);
g_mac[2] = (u8)(m1 & 0xFF);
g_mac[3] = (u8)(m1 >> 8);
g_mac[4] = (u8)(m2 & 0xFF);
g_mac[5] = (u8)(m2 >> 8);
}
{
static const char hh[] = "0123456789abcdef";
char mstr[18];
mstr[0]=hh[(g_mac[0]>>4)&0xf]; mstr[1]=hh[g_mac[0]&0xf]; mstr[2]=':';
mstr[3]=hh[(g_mac[1]>>4)&0xf]; mstr[4]=hh[g_mac[1]&0xf]; mstr[5]=':';
mstr[6]=hh[(g_mac[2]>>4)&0xf]; mstr[7]=hh[g_mac[2]&0xf]; mstr[8]=':';
mstr[9]=hh[(g_mac[3]>>4)&0xf]; mstr[10]=hh[g_mac[3]&0xf]; mstr[11]=':';
mstr[12]=hh[(g_mac[4]>>4)&0xf]; mstr[13]=hh[g_mac[4]&0xf]; mstr[14]=':';
mstr[15]=hh[(g_mac[5]>>4)&0xf]; mstr[16]=hh[g_mac[5]&0xf]; mstr[17]=0;
raw_log("[ath9k.raw] MAC=");
raw_log(mstr);
raw_log("\n");
}
/* 8. 硬件初始化 */
if (ath9k_hw_init() != 0) {
raw_log("[ath9k.raw] hw_init failed\n");
return -5;
}
/* 9. RX/TX 环初始化 */
if (api->dma && api->dma->alloc_pages) {
int rc = ath9k_rx_init(api->dma);
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
if (rc != 0) {
raw_log("[ath9k.raw] RX init failed, scan unavailable\n");
}
int trc = ath9k_tx_init(api->dma);
raw_hex("[ath9k.raw] tx_init rc=", (u64)(i8)trc);
if (trc != 0) raw_log("[ath9k.raw] TX init failed, connect unavailable\n");
} else {
raw_log("[ath9k.raw] no DMA api, scan/TX unavailable\n");
}
/* 9b. WPA2 加密底座自检(KAT):失败即 panic,绝不带病上路 */
{
int krc = wpa2_kat();
if (krc != 0) {
raw_hex("[ath9k.raw] WPA2 KAT FAILED case=", (u64)krc);
g_log->panic("ATH-E02 WPA2 CRYPTO KAT FAILED");
return -7;
}
raw_log("[ath9k.raw] WPA2 crypto KAT ok\n");
}
/* 10. 注册中断 */
if (api->irq_register && g_pci_irq) {
int rc = api->irq_register(g_pci_irq, ath9k_irq_handler);
raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc);
}
/* 11. 注册 netdev(无线 + RX/TX) */
struct dkm_net_device_desc ndesc;
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
static const char devname[] = "ath9k0";
ndesc.name = devname;
copy_mac(ndesc.mac, g_mac);
ndesc.flags = DKM_NET_F_WIRELESS |
(g_rx_ready ? DKM_NET_F_RX_READY : 0) |
(g_tx_ready ? DKM_NET_F_TX_READY : 0);
ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */
ndesc.tx = ath9k_tx_impl;
ndesc.rx_poll = ath9k_rx_poll_impl;
ndesc.scan_start = ath9k_scan_start_impl;
ndesc.scan_count = ath9k_scan_count_impl;
ndesc.scan_result = ath9k_scan_result_impl;
ndesc.is_wireless = ath9k_is_wireless_impl;
ndesc.connect = ath9k_connect_impl;
ndesc.disconnect = ath9k_disconnect_impl;
if (api->net && api->net->register_device) {
int rc = api->net->register_device(&ndesc);
raw_hex("[ath9k.raw] netdev registered, index=", (u64)rc);
} else {
raw_log("[ath9k.raw] no net api\n");
return -6;
}
/* 12. 启动后台被动扫描(同步执行,阻塞 driver_init 直到扫描完成)
* 注:DSK 加载驱动是顺序的,ath9k 在 stage3 末尾加载,
* 同步扫描会让 stage3 加载多花 ~5s(13 信道 × 400ms)。
* 这是可接受的,因为 FirstInit 在 DSK 之后才运行,能拿到完整扫描结果。
*/
if (g_rx_ready) {
raw_log("[ath9k.raw] starting initial passive scan\n");
ath9k_scan_start_impl(0);
}
raw_log("[ath9k.raw] driver_init done\n");
return 0;
}
__attribute__((visibility("default")))
void driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
if (g_mmio) {
ath9k_reg_write(AR_CR, AR_CR_RXD);
}
g_rx_ready = 0;
g_mmio = 0;
raw_log("[ath9k.raw] driver_exit\n");
}
-639
View File
@@ -1,639 +0,0 @@
/* wpa2_crypto.c — WPA2-PSK 加密底座实现(见 wpa2_crypto.h)
* 全部为恒定内存访问模式的最小实现,无堆、无 SSE。
*/
#include "wpa2_crypto.h"
/* ================= SHA1 (RFC 3174) ================= */
typedef struct {
u32 h[5];
u64 total_len;
u8 buf[64];
u32 buf_len;
} sha1_ctx;
static void sha1_block(u32 h[5], const u8 *p) {
u32 w[80];
for (int i = 0; i < 16; i++)
w[i] = ((u32)p[i*4] << 24) | ((u32)p[i*4+1] << 16) |
((u32)p[i*4+2] << 8) | (u32)p[i*4+3];
for (int i = 16; i < 80; i++) {
u32 x = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
w[i] = (x << 1) | (x >> 31); /* ROTL 1 */
}
u32 a=h[0], b=h[1], c=h[2], d=h[3], e=h[4];
for (int i = 0; i < 80; i++) {
u32 f, k;
if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }
else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
else { f = b ^ c ^ d; k = 0xCA62C1D6; }
u32 t = ((a << 5) | (a >> 27)) + f + e + k + w[i]; /* ROTL 5(a) */
e = d; d = c; c = (b << 30) | (b >> 2); b = a; a = t; /* ROTL 30(b) */
}
h[0]+=a; h[1]+=b; h[2]+=c; h[3]+=d; h[4]+=e;
}
static void sha1_init(sha1_ctx *c) {
c->h[0]=0x67452301; c->h[1]=0xEFCDAB89; c->h[2]=0x98BADCFE;
c->h[3]=0x10325476; c->h[4]=0xC3D2E1F0;
c->total_len = 0; c->buf_len = 0;
}
static void sha1_update(sha1_ctx *c, const u8 *data, u32 len) {
c->total_len += len;
while (len > 0) {
u32 take = 64 - c->buf_len;
if (take > len) take = len;
for (u32 i = 0; i < take; i++) c->buf[c->buf_len + i] = data[i];
c->buf_len += take; data += take; len -= take;
if (c->buf_len == 64) { sha1_block(c->h, c->buf); c->buf_len = 0; }
}
}
static void sha1_final(sha1_ctx *c, u8 out[20]) {
u64 bits = c->total_len * 8;
u8 pad = 0x80;
sha1_update(c, &pad, 1);
u8 z = 0;
while (c->buf_len != 56) sha1_update(c, &z, 1);
u8 lenb[8];
for (int i = 0; i < 8; i++) lenb[i] = (u8)(bits >> (56 - i * 8));
sha1_update(c, lenb, 8);
for (int i = 0; i < 5; i++) {
out[i*4] = (u8)(c->h[i] >> 24);
out[i*4+1] = (u8)(c->h[i] >> 16);
out[i*4+2] = (u8)(c->h[i] >> 8);
out[i*4+3] = (u8)(c->h[i]);
}
}
void wpa2_sha1(const u8 *data, u32 len, u8 out[20]) {
sha1_ctx c;
sha1_init(&c);
sha1_update(&c, data, len);
sha1_final(&c, out);
}
/* ================= HMAC-SHA1 (RFC 2202) ================= */
void wpa2_hmac_sha1(const u8 *key, u32 key_len,
const u8 *data, u32 data_len, u8 out[20]) {
u8 k[64], ipad[64], opad[64], kb[20];
for (int i = 0; i < 64; i++) k[i] = 0;
if (key_len > 64) {
wpa2_sha1(key, key_len, kb);
for (int i = 0; i < 20; i++) k[i] = kb[i];
} else {
for (u32 i = 0; i < key_len; i++) k[i] = key[i];
}
for (int i = 0; i < 64; i++) { ipad[i] = k[i] ^ 0x36; opad[i] = k[i] ^ 0x5C; }
u8 inner[20];
sha1_ctx c;
sha1_init(&c);
sha1_update(&c, ipad, 64);
sha1_update(&c, data, data_len);
sha1_final(&c, inner);
sha1_init(&c);
sha1_update(&c, opad, 64);
sha1_update(&c, inner, 20);
sha1_final(&c, out);
}
/* ================= PBKDF2-HMAC-SHA1 (RFC 2898) ================= */
static void pbkdf2_f(const u8 *pass, u32 pass_len,
const u8 *salt, u32 salt_len,
u32 iterations, u32 block_index, u8 out[20]) {
/* U1 = HMAC(pass, salt || INT_32_BE(block_index));U_i = HMAC(pass, U_{i-1}) */
u8 sbuf[36];
u32 n = salt_len;
if (n > 32) n = 32; /* SSID ≤ 32 字节,防溢出 */
for (u32 i = 0; i < n; i++) sbuf[i] = salt[i];
sbuf[n+0] = (u8)(block_index >> 24);
sbuf[n+1] = (u8)(block_index >> 16);
sbuf[n+2] = (u8)(block_index >> 8);
sbuf[n+3] = (u8)(block_index);
u8 u[20], t[20];
wpa2_hmac_sha1(pass, pass_len, sbuf, n + 4, u);
for (int i = 0; i < 20; i++) t[i] = u[i];
for (u32 it = 1; it < iterations; it++) {
wpa2_hmac_sha1(pass, pass_len, u, 20, u);
for (int i = 0; i < 20; i++) t[i] ^= u[i];
}
for (int i = 0; i < 20; i++) out[i] = t[i];
}
void wpa2_pbkdf2_sha1(const char *passphrase, const u8 *ssid, u32 ssid_len,
u8 psk[WPA2_PMK_LEN]) {
u32 plen = 0;
while (passphrase[plen]) plen++;
pbkdf2_f((const u8 *)passphrase, plen, ssid, ssid_len, 4096, 1, psk); /* DK1 → 前 20 字节 */
pbkdf2_f((const u8 *)passphrase, plen, ssid, ssid_len, 4096, 2, psk + 20); /* DK2 → 后 12 字节 */
}
/* ================= PRF-X (802.11i H.4) =================
* PTK = PRF-512(PMK, "Pairwise key expansion", 串接 seed)。
* seed 由调用方拼好:min(AA,SPA)||max(AA,SPA)||min(ANonce,SNonce)||max(...)。
* 输出 64 字节:迭代 0..63,每字节取 HMAC-SHA1(PMK, label||seed||counter)[0]。
*/
void wpa2_prf512(const u8 *pmk, const u8 *seed, u32 seed_len, u8 out[64]) {
static const char label[] = "Pairwise key expansion";
u32 label_len = sizeof(label) - 1; /* 22 */
u8 buf[22 + 64 + 1];
for (u32 i = 0; i < label_len; i++) buf[i] = (u8)label[i];
for (u32 i = 0; i < seed_len; i++) buf[label_len + i] = seed[i];
if (seed_len > 64) seed_len = 64;
for (int c = 0; c < 64; c++) {
buf[label_len + seed_len] = (u8)c;
u8 mac[20];
wpa2_hmac_sha1(pmk, 32, buf, label_len + seed_len + 1, mac);
out[c] = mac[0];
}
}
/* ================= AES-128 (FIPS-197,仅加密方向) ================= */
static const u8 aes_sbox[256] = {
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
};
static const u8 aes_rcon[11] = {
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
static u32 aes_sub_word(u32 w) {
return ((u32)aes_sbox[(w >> 24) & 0xFF] << 24) |
((u32)aes_sbox[(w >> 16) & 0xFF] << 16) |
((u32)aes_sbox[(w >> 8) & 0xFF] << 8) |
(u32)aes_sbox[w & 0xFF];
}
/* 展开轮密钥:rk[44](11 轮 × 4 word) */
static void aes_key_expand(const u8 key[16], u32 rk[44]) {
for (int i = 0; i < 4; i++)
rk[i] = ((u32)key[i*4] << 24) | ((u32)key[i*4+1] << 16) |
((u32)key[i*4+2] << 8) | (u32)key[i*4+3];
for (int i = 4; i < 44; i++) {
u32 t = rk[i-1];
if (i % 4 == 0) {
t = aes_sub_word((t << 8) | (t >> 24)) ^ ((u32)aes_rcon[i/4] << 24);
}
rk[i] = rk[i-4] ^ t;
}
}
void wpa2_aes128_enc_block(const u8 key[16], const u8 in[16], u8 out[16]) {
u32 rk[44];
aes_key_expand(key, rk);
u32 s0,s1,s2,s3, t0,t1,t2,t3;
s0 = rk[0] ^ ((u32)in[0] << 24 | (u32)in[1] << 16 | (u32)in[2] << 8 | in[3]);
s1 = rk[1] ^ ((u32)in[4] << 24 | (u32)in[5] << 16 | (u32)in[6] << 8 | in[7]);
s2 = rk[2] ^ ((u32)in[8] << 24 | (u32)in[9] << 16 | (u32)in[10] << 8 | in[11]);
s3 = rk[3] ^ ((u32)in[12] << 24 | (u32)in[13] << 16 | (u32)in[14] << 8 | in[15]);
#define AES_XTIME(x) ((u8)((((u8)(x)) << 1) ^ ((((u8)(x)) >> 7) * 0x1b)))
for (int r = 1; r <= 10; r++) {
/* SubBytes + ShiftRows(按列状态 s0..s3 每列 4 字节,state[col][row]) */
u8 sb[4][4];
u32 sv[4] = {s0, s1, s2, s3};
for (int cCol = 0; cCol < 4; cCol++)
for (int rRow = 0; rRow < 4; rRow++)
sb[rRow][cCol] = aes_sbox[(sv[cCol] >> (24 - rRow * 8)) & 0xFF];
/* ShiftRows:行 r 循环左移 r */
{
u8 t;
t = sb[1][0]; sb[1][0]=sb[1][1]; sb[1][1]=sb[1][2]; sb[1][2]=sb[1][3]; sb[1][3]=t;
t=sb[2][0]; sb[2][0]=sb[2][2]; sb[2][2]=t; t=sb[2][1]; sb[2][1]=sb[2][3]; sb[2][3]=t;
t = sb[3][0]; sb[3][0]=sb[3][3]; sb[3][3]=sb[3][2]; sb[3][2]=sb[3][1]; sb[3][1]=t;
}
/* MixColumns(末轮跳过) */
if (r < 10) {
u32 o[4];
for (int cCol = 0; cCol < 4; cCol++) {
u8 a0=sb[0][cCol], a1=sb[1][cCol], a2=sb[2][cCol], a3=sb[3][cCol];
u8 x1 = AES_XTIME(a0), x2 = AES_XTIME(a1);
u8 x3 = AES_XTIME(a2), x4 = AES_XTIME(a3);
sb[0][cCol] = (u8)(x1 ^ x2 ^ a1 ^ a2 ^ a3);
sb[1][cCol] = (u8)(a0 ^ x2 ^ x3 ^ a2 ^ a3);
sb[2][cCol] = (u8)(a0 ^ a1 ^ x3 ^ x4 ^ a3);
sb[3][cCol] = (u8)(x1 ^ a0 ^ a1 ^ a2 ^ x4);
}
o[0] = ((u32)sb[0][0] << 24) | ((u32)sb[1][0] << 16) | ((u32)sb[2][0] << 8) | sb[3][0];
o[1] = ((u32)sb[0][1] << 24) | ((u32)sb[1][1] << 16) | ((u32)sb[2][1] << 8) | sb[3][1];
o[2] = ((u32)sb[0][2] << 24) | ((u32)sb[1][2] << 16) | ((u32)sb[2][2] << 8) | sb[3][2];
o[3] = ((u32)sb[0][3] << 24) | ((u32)sb[1][3] << 16) | ((u32)sb[2][3] << 8) | sb[3][3];
t0 = o[0]; t1 = o[1]; t2 = o[2]; t3 = o[3];
} else {
t0 = ((u32)sb[0][0] << 24) | ((u32)sb[1][0] << 16) | ((u32)sb[2][0] << 8) | sb[3][0];
t1 = ((u32)sb[0][1] << 24) | ((u32)sb[1][1] << 16) | ((u32)sb[2][1] << 8) | sb[3][1];
t2 = ((u32)sb[0][2] << 24) | ((u32)sb[1][2] << 16) | ((u32)sb[2][2] << 8) | sb[3][2];
t3 = ((u32)sb[0][3] << 24) | ((u32)sb[1][3] << 16) | ((u32)sb[2][3] << 8) | sb[3][3];
}
s0 = t0 ^ rk[r*4+0];
s1 = t1 ^ rk[r*4+1];
s2 = t2 ^ rk[r*4+2];
s3 = t3 ^ rk[r*4+3];
}
out[0]=(u8)(s0>>24); out[1]=(u8)(s0>>16); out[2]=(u8)(s0>>8); out[3]=(u8)s0;
out[4]=(u8)(s1>>24); out[5]=(u8)(s1>>16); out[6]=(u8)(s1>>8); out[7]=(u8)s1;
out[8]=(u8)(s2>>24); out[9]=(u8)(s2>>16); out[10]=(u8)(s2>>8); out[11]=(u8)s2;
out[12]=(u8)(s3>>24); out[13]=(u8)(s3>>16); out[14]=(u8)(s3>>8); out[15]=(u8)s3;
}
/* ================= AES-128 解密方向(RFC 3394 unwrap 用) ================= */
static const u8 aes_inv_sbox[256] = {
0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d
};
/* GF(2^8) 乘法(AES 域,模 x^8+x^4+x^3+x+1) */
static u8 aes_gf_mul(u8 a, u8 b) {
u8 p = 0;
for (int i = 0; i < 8; i++) {
if (b & 1) p ^= a;
u8 hi = a & 0x80;
a = (u8)(a << 1);
if (hi) a ^= 0x1b;
b >>= 1;
}
return p;
}
/* AES-128 解密单块(等价逆密码结构,调试/unwrap 用) */
void wpa2_aes128_dec_block(const u8 key[16], const u8 in[16], u8 out[16]) {
u32 rk[44];
aes_key_expand(key, rk);
u32 s0 = rk[40] ^ ((u32)in[0] << 24 | (u32)in[1] << 16 | (u32)in[2] << 8 | in[3]);
u32 s1 = rk[41] ^ ((u32)in[4] << 24 | (u32)in[5] << 16 | (u32)in[6] << 8 | in[7]);
u32 s2 = rk[42] ^ ((u32)in[8] << 24 | (u32)in[9] << 16 | (u32)in[10] << 8 | in[11]);
u32 s3 = rk[43] ^ ((u32)in[12] << 24 | (u32)in[13] << 16 | (u32)in[14] << 8 | in[15]);
u32 sv[4];
for (int r = 9; r >= 0; r--) {
sv[0]=s0; sv[1]=s1; sv[2]=s2; sv[3]=s3;
u8 sb[4][4];
for (int cCol = 0; cCol < 4; cCol++)
for (int rRow = 0; rRow < 4; rRow++)
sb[rRow][cCol] = (u8)(sv[cCol] >> (24 - rRow * 8));
/* InvShiftRows(行 r 循环右移 r)+ InvSubBytes */
u8 t;
t = sb[1][3]; sb[1][3]=sb[1][2]; sb[1][2]=sb[1][1]; sb[1][1]=sb[1][0]; sb[1][0]=t;
t=sb[2][0]; sb[2][0]=sb[2][2]; sb[2][2]=t; t=sb[2][1]; sb[2][1]=sb[2][3]; sb[2][3]=t;
t = sb[3][0]; sb[3][0]=sb[3][1]; sb[3][1]=sb[3][2]; sb[3][2]=sb[3][3]; sb[3][3]=t;
for (int cCol = 0; cCol < 4; cCol++)
for (int rRow = 0; rRow < 4; rRow++)
sb[rRow][cCol] = aes_inv_sbox[sb[rRow][cCol]];
/* AddRoundKey(r) */
u32 nrk[4] = { rk[r*4+0], rk[r*4+1], rk[r*4+2], rk[r*4+3] };
{
u32 o[4];
for (int cCol = 0; cCol < 4; cCol++)
o[cCol] = ((u32)sb[0][cCol] << 24) | ((u32)sb[1][cCol] << 16) |
((u32)sb[2][cCol] << 8) | (u32)sb[3][cCol];
s0 = o[0] ^ nrk[0]; s1 = o[1] ^ nrk[1]; s2 = o[2] ^ nrk[2]; s3 = o[3] ^ nrk[3];
}
/* InvMixColumns(末轮 r=0 跳过):作用在 AddRoundKey 之后的状态上 */
if (r > 0) {
sv[0]=s0; sv[1]=s1; sv[2]=s2; sv[3]=s3;
for (int cCol = 0; cCol < 4; cCol++)
for (int rRow = 0; rRow < 4; rRow++)
sb[rRow][cCol] = (u8)(sv[cCol] >> (24 - rRow * 8));
for (int cCol = 0; cCol < 4; cCol++) {
u8 a0=sb[0][cCol], a1=sb[1][cCol], a2=sb[2][cCol], a3=sb[3][cCol];
sb[0][cCol] = (u8)(aes_gf_mul(a0,0x0e) ^ aes_gf_mul(a1,0x0b) ^ aes_gf_mul(a2,0x0d) ^ aes_gf_mul(a3,0x09));
sb[1][cCol] = (u8)(aes_gf_mul(a0,0x09) ^ aes_gf_mul(a1,0x0e) ^ aes_gf_mul(a2,0x0b) ^ aes_gf_mul(a3,0x0d));
sb[2][cCol] = (u8)(aes_gf_mul(a0,0x0d) ^ aes_gf_mul(a1,0x09) ^ aes_gf_mul(a2,0x0e) ^ aes_gf_mul(a3,0x0b));
sb[3][cCol] = (u8)(aes_gf_mul(a0,0x0b) ^ aes_gf_mul(a1,0x0d) ^ aes_gf_mul(a2,0x09) ^ aes_gf_mul(a3,0x0e));
}
u32 o[4];
for (int cCol = 0; cCol < 4; cCol++)
o[cCol] = ((u32)sb[0][cCol] << 24) | ((u32)sb[1][cCol] << 16) |
((u32)sb[2][cCol] << 8) | (u32)sb[3][cCol];
s0 = o[0]; s1 = o[1]; s2 = o[2]; s3 = o[3];
}
}
out[0]=(u8)(s0>>24); out[1]=(u8)(s0>>16); out[2]=(u8)(s0>>8); out[3]=(u8)s0;
out[4]=(u8)(s1>>24); out[5]=(u8)(s1>>16); out[6]=(u8)(s1>>8); out[7]=(u8)s1;
out[8]=(u8)(s2>>24); out[9]=(u8)(s2>>16); out[10]=(u8)(s2>>8); out[11]=(u8)s2;
out[12]=(u8)(s3>>24); out[13]=(u8)(s3>>16); out[14]=(u8)(s3>>8); out[15]=(u8)s3;
}
/* ================= RFC 3394 AES Key Unwrap ================= */
static const u8 wrap_iv[8] = {0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6};
int wpa2_key_unwrap(const u8 kek[16], const u8 *in, u32 in_len, u8 *out) {
if (in_len < 24 || (in_len % 8) != 0) return -1;
u32 n = in_len / 8 - 1;
u8 areg[8];
for (int i = 0; i < 8; i++) areg[i] = in[i];
/* R 块直接在 out 缓冲中处理(out 容量须 ≥ in_len-8,调用方保证) */
u8 *r = out;
for (u32 i = 0; i < n * 8; i++) r[i] = in[8 + i];
u8 blockin[16], b[16];
for (int j = 5; j >= 0; j--) {
for (u32 i = n; i >= 1; i--) {
/* RFC 3394: B = AES-1(K, (A ^ t) | R[i]),t = n*j + i */
u64 t = (u64)(n * (u32)j + i);
for (int k = 0; k < 8; k++)
blockin[k] = areg[k] ^ (u8)((t >> (56 - k * 8)) & 0xFF);
for (int k = 0; k < 8; k++) blockin[8+k] = r[(i-1)*8+k];
wpa2_aes128_dec_block(kek, blockin, b);
for (int k = 0; k < 8; k++) areg[k] = b[k];
for (int k = 0; k < 8; k++) r[(i-1)*8+k] = b[8+k];
}
}
for (int i = 0; i < 8; i++) if (areg[i] != wrap_iv[i]) return -1;
return 0;
}
/* ================= CCMP (RFC 3610 + 802.11i) =================
* STA 收 AP 数据帧方向:MIC(CBC-MAC) 验证 + CTR 解密。
* 帧 = MAC 头(24B, 3 地址) || CCMP 头(8B) || 密文 || MIC(8B)。
*/
#define CCMP_HDR_LEN 8u
#define CCMP_MIC_LEN 8u
static void ccmp_nonce16(const u8 *frame, const u8 *chdr, u8 nonce[16]) {
nonce[0] = 0; /* priority = 0 */
for (int i = 0; i < 6; i++) nonce[1+i] = frame[10+i]; /* A2 = SA */
/* CCMP 头布局:b0=PN0 b1=PN1 b2=rsvd b3=KeyID b4=PN2 b5=PN3 b6=PN4 b7=PN5 */
nonce[7] = chdr[0]; /* PN0 */
nonce[8] = chdr[1]; /* PN1 */
nonce[9] = chdr[4]; /* PN2 */
nonce[10] = chdr[5]; /* PN3 */
nonce[11] = chdr[6]; /* PN4 */
nonce[12] = chdr[7]; /* PN5 */
nonce[13] = 0; nonce[14] = 0; nonce[15] = 0;
}
static void ccmp_ctr_block(const u8 nonce[16], u16 counter, u8 ctr[16]) {
/* RFC 3610 A_i = flags(L-1=1) || 13B nonce || counter(2B) */
ctr[0] = 0x01;
for (int i = 0; i < 13; i++) ctr[1+i] = nonce[i];
ctr[14] = (u8)(counter >> 8);
ctr[15] = (u8)(counter);
}
/* ---- CCMP 头构造:PN 按 802.11i 散布写入 ---- */
static void ccmp_header_fill(u8 *chdr, u64 pn, u8 key_id) {
chdr[0] = (u8)(pn & 0xFF); /* PN0 */
chdr[1] = (u8)((pn >> 8) & 0xFF); /* PN1 */
chdr[2] = 0; /* Reserved */
chdr[3] = (u8)(0x20 | (key_id & 0x03)); /* ExtIV=1 | KeyID */
chdr[4] = (u8)((pn >> 16) & 0xFF); /* PN2 */
chdr[5] = (u8)((pn >> 24) & 0xFF); /* PN3 */
chdr[6] = (u8)((pn >> 32) & 0xFF); /* PN4 */
chdr[7] = (u8)((pn >> 40) & 0xFF); /* PN5 */
}
/* ---- CBC-MAC:B0 || AAD(24B) || 密文 → MIC(16B)(收发共用) ---- */
static void ccmp_cbc_mac(const u8 *frame, const u8 *nonce,
const u8 *ct, u32 ct_len, const u8 tk[16], u8 mac[16]) {
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
u16 fc = fctl & 0xC78F;
u16 sc = (u16)frame[22] | ((u16)frame[23] << 8);
sc &= 0x000F;
u8 abuf[24];
abuf[0] = 0; abuf[1] = 22;
abuf[2] = (u8)(fc & 0xFF); abuf[3] = (u8)(fc >> 8);
for (int i = 0; i < 18; i++) abuf[4+i] = frame[4+i];
abuf[22] = (u8)(sc & 0xFF); abuf[23] = (u8)(sc >> 8);
u8 blk[16], b0[16];
b0[0] = 0x19; /* Adata=0, M=3(8B), L-1=1 */
for (int i = 0; i < 13; i++) b0[1+i] = nonce[i];
b0[14] = (u8)(ct_len >> 8); b0[15] = (u8)ct_len;
wpa2_aes128_enc_block(tk, b0, mac);
for (int i = 0; i < 16; i++) blk[i] = abuf[i] ^ mac[i];
wpa2_aes128_enc_block(tk, blk, mac);
for (int i = 0; i < 8; i++) blk[i] = abuf[16+i] ^ mac[i];
for (int i = 8; i < 16; i++) blk[i] = mac[i];
wpa2_aes128_enc_block(tk, blk, mac);
for (u32 off = 0; off < ct_len; off += 16) {
u32 take = ct_len - off;
if (take > 16) take = 16;
for (u32 i = 0; i < 16; i++)
blk[i] = (i < take ? ct[off+i] : 0) ^ mac[i];
wpa2_aes128_enc_block(tk, blk, mac);
}
}
int wpa2_ccmp_decrypt(const u8 *frame, u32 len, const u8 tk[16], u8 *out, u32 out_cap) {
u32 hdr = 24; /* 3 地址 data 帧 */
if (len < hdr + CCMP_HDR_LEN + CCMP_MIC_LEN) return -2;
const u8 *chdr = frame + hdr;
const u8 *ct = frame + hdr + CCMP_HDR_LEN;
u32 ct_len = len - hdr - CCMP_HDR_LEN - CCMP_MIC_LEN;
if (ct_len == 0) return -2;
if (ct_len > out_cap) return -2;
u8 nonce[16]; /* 13B CCMP 非立即在 [0..12] */
ccmp_nonce16(frame, chdr, nonce);
/* ---- MIC 校验(基于密文) ---- */
u8 mac[16];
ccmp_cbc_mac(frame, nonce, ct, ct_len, tk, mac);
u8 diff = 0;
for (u32 i = 0; i < CCMP_MIC_LEN; i++) diff |= mac[i] ^ ct[ct_len + i];
if (diff) return -1; /* MIC 校验失败 */
/* ---- CTR 解密 ---- */
u16 counter = 1;
for (u32 off = 0; off < ct_len; off += 16) {
u8 ctr[16], s[16];
ccmp_ctr_block(nonce, counter++, ctr);
wpa2_aes128_enc_block(tk, ctr, s);
u32 take = ct_len - off;
if (take > 16) take = 16;
for (u32 i = 0; i < take; i++)
out[off+i] = ct[off+i] ^ s[i];
}
return (int)ct_len;
}
int wpa2_ccmp_encrypt(u8 *buf, u32 buf_cap, const u8 *msdu, u32 msdu_len,
const u8 tk[16], u64 pn) {
if (!buf || !msdu || msdu_len == 0) return -1;
if (msdu_len + 24 + CCMP_HDR_LEN + CCMP_MIC_LEN > buf_cap) return -1;
u8 *chdr = buf + 24;
ccmp_header_fill(chdr, pn, 0);
u8 *ct = chdr + CCMP_HDR_LEN;
u8 nonce[16];
ccmp_nonce16(buf, chdr, nonce);
/* CTR 加密 */
u16 counter = 1;
for (u32 off = 0; off < msdu_len; off += 16) {
u8 ctr[16], s[16];
ccmp_ctr_block(nonce, counter++, ctr);
wpa2_aes128_enc_block(tk, ctr, s);
u32 take = msdu_len - off;
if (take > 16) take = 16;
for (u32 i = 0; i < take; i++) ct[off+i] = msdu[off+i] ^ s[i];
}
u8 mac[16];
ccmp_cbc_mac(buf, nonce, ct, msdu_len, tk, mac);
for (u32 i = 0; i < CCMP_MIC_LEN; i++) ct[msdu_len + i] = mac[i];
return (int)(24 + CCMP_HDR_LEN + msdu_len + CCMP_MIC_LEN);
}
/* ================= KAT ================= */
static int buf_eq(const u8 *a, const u8 *b, u32 n) {
u8 d = 0;
for (u32 i = 0; i < n; i++) d |= a[i] ^ b[i];
return d == 0;
}
/* CCMP 自洽往返:用生产 encrypt 构造,再走生产 decrypt。
* 覆盖 nonce/AAD/B0/CTR 字节布局 + MIC 篡改检测,防索引回归。 */
static int ccmp_selftest(void) {
static const char payload[] = "Deshab CCMP round-trip payload 0123456789";
u32 plen = sizeof(payload) - 1;
u8 tk[16];
for (int i = 0; i < 16; i++) tk[i] = (u8)(0x30 + i);
u8 frame[96];
for (u32 i = 0; i < sizeof(frame); i++) frame[i] = 0;
/* 802.11 data 头:FC=0x0841 (data, toDS),A1/A2/A3,seq ctrl */
frame[0] = 0x41; frame[1] = 0x08;
for (int i = 0; i < 6; i++) frame[4+i] = (u8)(0xA0 + i); /* A1 = AP */
for (int i = 0; i < 6; i++) frame[10+i] = (u8)(0x10 + i); /* A2 = STA */
for (int i = 0; i < 6; i++) frame[16+i] = (u8)(0xB0 + i); /* A3 = DA */
frame[22] = 0x10; frame[23] = 0x00;
int flen = wpa2_ccmp_encrypt(frame, sizeof(frame), (const u8 *)payload, plen,
tk, 0x000000000001ULL);
if (flen != (int)(24 + 8 + plen + 8)) return -1;
u8 out[96];
int rc = wpa2_ccmp_decrypt(frame, (u32)flen, tk, out, sizeof(out));
if (rc != (int)plen) return -1;
for (u32 i = 0; i < plen; i++) if (out[i] != (u8)payload[i]) return -1;
/* 篡改密文 → 必须 MIC 校验失败 */
frame[24 + 8] ^= 0x01;
if (wpa2_ccmp_decrypt(frame, (u32)flen, tk, out, sizeof(out)) != -1) return -1;
return 0;
}
int wpa2_kat(void) {
u8 out[64];
/* 1. SHA1("abc") = a9993e36...9cd0d89d */
wpa2_sha1((const u8 *)"abc", 3, out);
{
static const u8 exp[20] = {
0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,
0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d };
if (!buf_eq(out, exp, 20)) return 1;
}
/* 2. HMAC-SHA1 RFC 2202 case 2: key "Jefe" data "what do ya want for nothing?"
* = effcdf6ae5eb2fa2d27416d5f184df9c259a7c79 */
wpa2_hmac_sha1((const u8 *)"Jefe", 4,
(const u8 *)"what do ya want for nothing?", 28, out);
{
static const u8 exp[20] = {
0xef,0xfc,0xdf,0x6a,0xe5,0xeb,0x2f,0xa2,0xd2,0x74,
0x16,0xd5,0xf1,0x84,0xdf,0x9c,0x25,0x9a,0x7c,0x79 };
if (!buf_eq(out, exp, 20)) return 2;
}
/* 3. PBKDF2-HMAC-SHA1 RFC 6070: ("password","salt",1,20)
* = 0c60c80f961f0e71f3a9b524af6012062fe037a6 */
{
u8 dk[32];
/* 手动 1 iter 版本走 pbkdf2_f 不可见,直接用 4096 向量(与 WPA 一致):
* RFC 6070 case 3: ("password","salt",4096,20)
* = 4b007901b765489abead49d926f721d065a429c1 */
wpa2_pbkdf2_sha1("password", (const u8 *)"salt", 4, dk);
static const u8 exp[20] = {
0x4b,0x00,0x79,0x01,0xb7,0x65,0x48,0x9a,0xbe,0xad,
0x49,0xd9,0x26,0xf7,0x21,0xd0,0x65,0xa4,0x29,0xc1 };
if (!buf_eq(dk, exp, 20)) return 3;
}
/* 4. AES-128 FIPS-197 C.1: key 000102...0f, pt 00112233...eeff
* = 69c4e0d86a7b0430d8cdb78070b4c55a */
{
u8 key[16], pt[16], ct[16];
for (int i = 0; i < 16; i++) { key[i]=(u8)i; pt[i]=(u8)(i*0x11); }
wpa2_aes128_enc_block(key, pt, ct);
static const u8 exp[16] = {
0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,
0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a };
if (!buf_eq(ct, exp, 16)) return 4;
}
/* 5. AES-128 FIPS-197 C.3 (256bit key 前半忽略,此处用 C.1 变体覆盖 key schedule):
* key 2b7e1516 28aed2a6 abf71588 09cf4f3c
* pt 6bc1bee2 2e409f96 e93d7e11 7393172a
* ct 3ad77bb4 0d7a3660 a89ecaf3 2466ef97 */
{
static const u8 key[16] = {0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,
0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
static const u8 pt[16] = {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,
0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a};
static const u8 exp[16] = {0x3a,0xd7,0x7b,0xb4,0x0d,0x7a,0x36,0x60,
0xa8,0x9e,0xca,0xf3,0x24,0x66,0xef,0x97};
u8 ct[16];
wpa2_aes128_enc_block(key, pt, ct);
if (!buf_eq(ct, exp, 16)) return 5;
}
/* 6. CCMP:构造→解密 往返 + 篡改检测 */
if (ccmp_selftest() != 0) return 6;
/* 7. RFC 3394 key unwrap(C.1 128-bit KEK 向量) */
{
static const u8 kek[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
static const u8 wrapped[24] = {0x1F,0xA6,0x8B,0x0A,0x81,0x12,0xB4,0x47,
0xAE,0xF3,0x4B,0xD8,0xFB,0x5A,0x7B,0x82,
0x9D,0x3E,0x86,0x23,0x71,0xD2,0xCF,0xE5};
static const u8 expect[16] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};
u8 plain[16];
if (wpa2_key_unwrap(kek, wrapped, 24, plain) != 0) return 7;
if (!buf_eq(plain, expect, 16)) return 7;
}
return 0;
}
-67
View File
@@ -1,67 +0,0 @@
/* wpa2_crypto.h — WPA2-PSK 握手所需加密底座(DKM ath9k 专用)
*
* 范围(802.11i RSNA,AKM 00-0F-AC:2 PSK):
* SHA1 — RFC 3174
* HMAC-SHA1 — RFC 2204(EAPOL-Key MIC ver2 = HMAC-SHA1-128)
* PBKDF2-HMAC-SHA1 — RFC 2898(passphrase → PSK,4096 iter)
* PRF-X — 802.11i H.4(PMK → 512bit PTK)
* AES-128 — FIPS-197(仅加密方向;CCMP 的 CTR 与 CBC-MAC 都用 AES-ENC)
* AES Key Unwrap — RFC 3394(KEK 解密 M3 key data 中的 GTK KDE)
* CCMP — RFC 3610(data 帧 MIC 验证 + 计数器解密;加密留 TX 数据路径)
*
* 自检:wpa2_kat() 内置 RFC 2202/6070/FIPS-197 已知答案向量,
* driver_init 时调用,任一失败 panic(ATH-E02)。
* 约束:无堆(静态/栈缓冲)、无除法热路径、-mno-sse 可编译。
*/
#ifndef ATH9K_WPA2_CRYPTO_H
#define ATH9K_WPA2_CRYPTO_H
#include "../dkm_shared.h"
#define WPA2_PMK_LEN 32
#define WPA2_PTK_LEN 48 /* KCK(16)+KEK(16)+TK(16)(802.11i CCMP 不取 TKIP MIC 子钥) */
#define WPA2_KCK_LEN 16
#define WPA2_KEK_LEN 16
#define WPA2_TK_LEN 16
/* SHA1:out[20] */
void wpa2_sha1(const u8 *data, u32 len, u8 out[20]);
/* 增量 SHA1(EAPOL MIC 需要对任意长帧做 HMAC,直接给一次性接口) */
void wpa2_hmac_sha1(const u8 *key, u32 key_len,
const u8 *data, u32 data_len, u8 out[20]);
/* PBKDF2-HMAC-SHA1:passphrase → psk[32](iter 固定 4096,salt=SSID) */
void wpa2_pbkdf2_sha1(const char *passphrase, const u8 *ssid, u32 ssid_len,
u8 psk[WPA2_PMK_LEN]);
/* PRF-512(802.11i H.4):PMK+label+addr/nonce 串接 → out[64] */
void wpa2_prf512(const u8 *pmk, const u8 *seed, u32 seed_len, u8 out[64]);
/* AES-128 单块加密(in/out 16 字节,可同址) */
void wpa2_aes128_enc_block(const u8 key[16], const u8 in[16], u8 out[16]);
/* AES-128 单块解密(RFC 3394 unwrap 用;CCMP 路径不使用) */
void wpa2_aes128_dec_block(const u8 key[16], const u8 in[16], u8 out[16]);
/* RFC 3394 key unwrap:in 为 n*8 字节密文(n>=2),out 得 (n-1)*8 明文;
* 返回 0 成功,-1 integrity check 失败(AIV != 0xA6A6A6A6A6A6A6A6)。 */
int wpa2_key_unwrap(const u8 kek[16], const u8 *in, u32 in_len, u8 *out);
/* CCMP 解密(STA 收 AP 数据帧,非 QoS,3 地址):
* frame 指向 802.11 MAC 头起始,len 为整帧长度(含 CCMP 8B 头 + 8B MIC)。
* 成功时把解密后的数据载荷(去掉 CCMP 头/MIC/FCS 前的明文 MSDU)
* 写入 out 并返回明文长度;MIC 错误返回 -1,参数错返回 -2。 */
int wpa2_ccmp_decrypt(const u8 *frame, u32 len, const u8 tk[16], u8 *out, u32 out_cap);
/* CCMP 加密(STA 发 AP 数据帧):在 buf 内就地构造
* [MAC 头(24B,调用方先填好)][CCMP 头(8B)][密文][MIC(8B)]。
* buf : 调用方提供的缓冲,前 24 字节须已填 MAC 头
* msdu : 明文 MSDU
* msdu_len: 明文长度(>0)
* pn : 发包序号(调用方递增维护)
* 返回写入 buf 的总帧长(24+8+msdu_len+8);缓冲不足返回 -1。 */
int wpa2_ccmp_encrypt(u8 *buf, u32 buf_cap, const u8 *msdu, u32 msdu_len,
const u8 tk[16], u64 pn);
/* KAT(已知答案自检):返回 0 = 全部通过;非 0 = 第几个用例失败(1 基)。
* driver_init 必须调用,失败方 panic(ATH-E02)。 */
int wpa2_kat(void);
#endif
-191
View File
@@ -1,191 +0,0 @@
/* DKM BootFS Driver — exposes Limine boot modules as a simple filesystem
* Stage 1, required, provides "bootfs".
* Lists all boot modules with path, size, and address.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci_api;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
/* minimal limine types matching kernel */
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_provides[] = { "bootfs" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "bootfs",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 7, /* DKM_CLASS_FS */
.stage = 1,
.flags = 1, /* DKM_F_REQUIRED */
.priority = 0,
.depends = NULL,
.depends_count = 0,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* Log helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static void log_hex(const char *prefix, u64 val) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0'; buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
if (!rsp) {
g_log->error("[bootfs] no boot module response");
return -1;
}
g_log->info("[bootfs] boot module filesystem");
log_hex("[bootfs] file count=", rsp->module_count);
for (u64 i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (!f) continue;
g_log->info("[bootfs] ----------");
if (f->path) {
g_log->info("[bootfs] file");
g_log->info(f->path);
}
log_hex("[bootfs] size=", f->size);
log_hex("[bootfs] addr=", (u64)(uintptr_t)f->address);
if (f->cmdline) {
g_log->info("[bootfs] cmd");
g_log->info(f->cmdline);
}
}
g_log->info("[bootfs] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-107
View File
@@ -1,107 +0,0 @@
/* DKM Console Framebuffer Driver
* Stage 1, optional, provides "console".
* Clears the framebuffer to a solid light-blue background so the screen is
* clean before DSK takes over and draws the Logo. The former 4-color arc
* loading ring has been removed — DSK now shows a static Logo instead.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version; u32 size; u64 feature_bits;
const struct dkm_log_api *log;
const void *mem, *utsm, *irq, *pci_api, *dma, *vfs, *net, *timer, *drr;
const void *rsdp_address, *fb_address;
u64 fb_width, fb_height, fb_pitch; u16 fbpp;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic; u16 abi_version; u16 desc_size;
const char *name, *version, *vendor;
u32 driver_class, stage, flags, priority;
const char *const *depends; u32 depends_count;
const char *const *provides; u32 provides_count;
u64 min_kernel_abi, feature_bits, reserved0, reserved1;
};
static const char *const g_provides[] = { "console" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="console_fb", .version="0.3.0", .vendor="Deshab",
.driver_class=5, .stage=1, .flags=0, .priority=0,
.depends=NULL, .depends_count=0,
.provides=g_provides, .provides_count=1,
.min_kernel_abi=1,
};
/* ---------------------------------------------------------------
* Colors
* --------------------------------------------------------------- */
static const u32 BG = 0xFFC8E0F0; /* light blue background — cleared before DSK Logo */
/* ---------------------------------------------------------------
* Drawing helpers – all integer arithmetic, no soft-float
* --------------------------------------------------------------- */
static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) {
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = color;
}
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
if (!api->fb_address || !api->fb_width) {
api->log->warn("[console_fb] no framebuffer");
return 0;
}
api->log->info("[console_fb] clearing boot screen");
/* Solid background fill only — DSK draws the Logo next. Clearing here
* avoids firmware/UEFI leftover garbage being visible during the
* UTSM→DSK handoff. */
u32 *fb = (u32 *)api->fb_address;
fb_fill(fb, api->fb_pitch, api->fb_width, api->fb_height, BG);
api->log->info("[console_fb] ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-282
View File
@@ -1,282 +0,0 @@
/* DKM DevFS Driver — virtual device filesystem
* Stage 2, optional, depends on "vfs", provides "devfs".
* Exposes kernel platform info as virtual files under /dev/.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
/* D6 万物互联:dkm_bus 镜像(布局对齐 UTSM include/utsm/dkm.h) */
struct dkm_bus_service {
char name[48];
u32 version;
u32 flags;
void *fn;
const void *provider;
};
struct dkm_bus_api {
u32 magic;
u32 version;
int (*register_service)(const char *name, u32 version, void *fn);
int (*unregister_service)(const char *name);
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void);
const struct dkm_bus_service *(*at)(u32 index);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci_api;
const void *dma;
const void *vfs_api;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
/* —— 尾部追加字段镜像(DKM_ABI v1;到 bus 为止) —— */
void *irq_register; /* int (*)(u8, void*) */
u64 hhdm_offset;
const void *block;
const void *mmio;
void *mm_map_mmio;
void *mm_unmap_mmio;
void *register_apic_eoi;
void *irq_vector_alloc;
void *irq_vector_free;
const void *sched;
const void *kapi;
const struct dkm_bus_api *bus;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u8 _pad[56];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_depends[] = { "vfs" };
static const char *const g_provides[] = { "devfs" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "devfs",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 7,
.stage = 2,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 1,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* Helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static void log_hex(const char *prefix, u64 val) {
static const char h[] = "0123456789abcdef";
char buf[19]; u32 p = 0;
buf[p++] = '0'; buf[p++] = 'x';
for (int i = 15; i >= 0; i--) buf[p++] = h[(val>>(i*4))&0xf];
buf[p] = 0;
g_log->info(prefix); g_log->info(buf);
}
/* ---------------------------------------------------------------
* Virtual file helpers
* --------------------------------------------------------------- */
static void devfs_file(const char *name, const char *content) {
g_log->info("[devfs] /dev/");
g_log->info(name);
g_log->info("[devfs] ");
g_log->info(content);
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
g_log->info("[devfs] virtual device filesystem");
/* /dev/version */
devfs_file("version", "Deshab kernel 0.1.0");
/* /dev/platform */
devfs_file("platform", "x86_64 SAS-R0");
/* /dev/fb0 */
if (api->fb_address) {
char info[64];
info[0] = 0;
/* manual number conversion for width */
{
u64 v = api->fb_width;
char tmp[24]; u32 p = 0;
if (v == 0) tmp[p++] = '0';
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
tmp[p++] = 'x';
v = api->fb_height;
if (v == 0) tmp[p++] = '0';
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
tmp[p++] = 'x';
v = api->fb_bpp;
if (v == 0) tmp[p++] = '0';
else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; }
tmp[p++] = ' ';
tmp[p++] = 'f';
tmp[p++] = 'b';
tmp[p++] = '0';
tmp[p] = 0;
for (u32 i = 0; i <= p; i++) info[i] = tmp[i];
}
devfs_file("fb0", info);
}
/* /dev/services — D6 万物互联:总线已注册服务清单 */
if (api->bus && api->bus->magic == 0x53425553u /* "SBUS" */) {
char cnt[24];
cnt[0] = '0'; cnt[1] = 'x';
u64 v = api->bus->count();
u32 p = 2;
if (v == 0) cnt[p++] = '0';
else {
char r[18]; u32 q = 0;
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
while (q) cnt[p++] = r[--q];
}
cnt[p] = 0;
devfs_file("services", cnt);
g_log->info("[devfs] /dev/services/");
for (u32 i = 0; i < api->bus->count(); i++) {
const struct dkm_bus_service *s = api->bus->at(i);
if (s && s->name[0]) {
g_log->info("[devfs] ");
g_log->info(s->name);
}
}
}
/* /dev/modules — count from boot_modules */
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
if (rsp) {
char buf[24];
buf[0] = '0'; buf[1] = 'x';
u64 v = rsp->module_count;
u32 p = 2;
if (v == 0) buf[p++] = '0';
else {
char r[18]; u32 q = 0;
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
while (q) buf[p++] = r[--q];
}
buf[p] = 0;
devfs_file("modules", buf);
/* /dev/boot — list paths */
g_log->info("[devfs] /dev/boot/");
for (u64 i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (f && f->path) {
g_log->info("[devfs] ");
g_log->info(f->path);
}
}
}
g_log->info("[devfs] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-87
View File
@@ -1,87 +0,0 @@
/* dkm_instr.h — DKM 驱动专用插桩宏(无 UTSM 头文件依赖)
*
* 设计原则:
* - 使用 dkm_shared.h 已有的 dkm_rdtsc()/dkm_tsc_per_ms
* - 日志通过 dkm_kernel_api->log 输出
* - 统计计数器是 file-local static
* - 禁用时全部宏展开为空(零开销)
* - 需在 driver_init 开头调用 dkm_instr_init(api) 初始化
*
* 使用方式:
* 1. #include "../dkm_instr.h"
* 2. 在 driver_init 开头: dkm_instr_init(api);
* 3. 使用 DKM_STAT_INC / DKM_TS / DKM_LOG_TRACE 宏
*/
#ifndef DKM_INSTR_H
#define DKM_INSTR_H
#include "dkm_shared.h"
/* ---- 全局开关(dkm_instr.c 定义,每个 .drv 引用同一符号) ----
* 注意: DKM .drv 是 ET_REL 可重定位对象,每个驱动有独立的数据段副本。
* 因此 dkm_instr_global_enable 在每个 .drv 中独立控制,由
* dkm_instr_init() 在 driver_init 中根据 kernel_api 设置。
*/
static u32 dkm_instr_global_enable __attribute__((unused)) = 0;
/* ---- DKM_STAT: 轻量级计数器 ----
* DKM_STAT_DECL(name) — 在函数/文件作用域声明计数器
* DKM_STAT_INC(name) — 自增(仅当 enabled)
* DKM_STAT_DUMP(name, log_info_fn) — 输出当前计数值
*/
#define DKM_STAT_DECL(name) static u64 name##_count = 0
#define DKM_STAT_INC(name) do { \
if (dkm_instr_global_enable) { name##_count++; } \
} while(0)
#define DKM_STAT_DUMP(name, log_info_fn) do { \
if (dkm_instr_global_enable && name##_count > 0) { \
log_info_fn("[DKM:STAT] " #name " count="); \
/* 简单 u64 to hex 串口输出 */ \
static const char _hx[] = "0123456789abcdef"; \
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(name##_count>>((15-_i)*4))&0xf]; \
_buf[18]=0; log_info_fn(_buf); \
} \
} while(0)
/* ---- DKM_TS: 时间戳测量 ----
* DKM_TS_DECL(name) — 声明时间戳变量
* DKM_TS_BEGIN(name) — 记录起始 TSC
* DKM_TS_END(name, log_info_fn, label) — 计算耗时并输出
*/
#define DKM_TS_DECL(name) static u64 name##_ts = 0
#define DKM_TS_BEGIN(name) do { name##_ts = dkm_rdtsc(); } while(0)
#define DKM_TS_END(name, log_info_fn, label) do { \
if (dkm_instr_global_enable && dkm_tsc_per_ms) { \
u64 _d = dkm_rdtsc() - name##_ts; \
u64 _ms = _d / dkm_tsc_per_ms; \
log_info_fn("[DKM:TS] " label " elapsed_ms="); \
static const char _hx[] = "0123456789abcdef"; \
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(_ms>>((15-_i)*4))&0xf]; \
_buf[18]=0; log_info_fn(_buf); \
} \
} while(0)
/* ---- DKM_LOG_TRACE: 调试级别日志 ----
* 仅当插桩启用时通过 api->log->info 输出
*/
#define DKM_LOG_TRACE(log_api, msg) do { \
if (dkm_instr_global_enable && (log_api)) (log_api)->info(msg); \
} while(0)
/* ---- 初始化:读取 kernel_api 中的插桩标志 ----
* kernel_api->feature_bits bit0 = 插桩启用标志
* 由 UTSM dkm_fill_platform_info() 根据 FUCK debug.dkm_trace 设置
*/
static __inline__ void dkm_instr_init(const struct dkm_kernel_api *api) {
if (api && (api->feature_bits & 0x01)) {
dkm_instr_global_enable = 1;
}
}
#endif /* DKM_INSTR_H */
-365
View File
@@ -1,365 +0,0 @@
/* DKM 共享头文件 — 消除各驱动重复的结构体/宏/PCI/TSC 代码
*
* 设计要点:
* - 结构体定义与 UTSM/include/utsm/dkm.h 保持布局一致 (canonical)
* - PCI I/O 函数使用 static __inline__, 每个 .drv 文件获得独立副本, 避免链接冲突
* - TSC 校准函数使用 static __inline__, 每个 .drv 文件获得自己的 dkm_tsc_per_ms
* - 驱动源文件只需 #include "../dkm_shared.h", 保留各自的 driver_desc 和 driver_init
*
* 使用约定:
* 1. 在 driver_init 开头调用 dkm_tsc_calibrate() 完成校准
* 2. 时序等待使用 dkm_delay_ms() / dkm_delay_us()
* 3. 串口发送等待使用 dkm_serial_wait_tx(COM1)
* 4. MMIO 寄存器读取等待使用 dkm_rdtsc() + dkm_tsc_per_ms 直接计算 deadline
*/
#ifndef DKM_SHARED_H
#define DKM_SHARED_H
#include <stdint.h>
/* ---- 基本类型 (与 UTSM/types.h 一致) ---- */
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char i8;
typedef short i16;
typedef int i32;
typedef long long i64;
#ifndef NULL
#define NULL ((void *)0)
#endif
/* ---- DKM magic / ABI 版本 ---- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
/* ---- DKM 驱动类别 (driver_desc.driver_class) ---- */
#define DKM_CLASS_PLATFORM 1u
#define DKM_CLASS_BUS 2u
#define DKM_CLASS_INTERRUPT 3u
#define DKM_CLASS_TIMER 4u
#define DKM_CLASS_CONSOLE 5u
#define DKM_CLASS_STORAGE 6u
#define DKM_CLASS_FS 7u
#define DKM_CLASS_NET 8u
#define DKM_CLASS_INPUT 9u
#define DKM_CLASS_GPU 10u
#define DKM_CLASS_MISC 11u
/* ---- DKM 驱动标志 (driver_desc.flags) ---- */
#define DKM_F_REQUIRED (1u << 0)
#define DKM_F_BOOT_MODULE (1u << 1)
#define DKM_F_NO_UNLOAD (1u << 2)
#define DKM_F_STRONG_RECOVERY (1u << 3)
#define DKM_F_DMA_REQUIRED (1u << 4)
#define DKM_F_EARLY_LOG (1u << 5)
/* ---- 网卡链路/能力标志 ---- */
#define DKM_NET_F_LINK_UP (1u << 0)
#define DKM_NET_F_TX_READY (1u << 1)
#define DKM_NET_F_RX_READY (1u << 2)
#define DKM_NET_F_WIRELESS (1u << 3)
/* ---- WiFi 扫描加密类型 ---- */
#define DKM_NET_SEC_OPEN 0u
#define DKM_NET_SEC_WEP 1u
#define DKM_NET_SEC_WPA 2u
#define DKM_NET_SEC_WPA2 3u
#define DKM_NET_SEC_WPA3 4u
/* ---- DKM 结构体定义 (与 UTSM/include/utsm/dkm.h 布局一致) ---- */
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_dma_buffer {
void *virt;
u64 phys;
u64 size;
};
struct dkm_dma_api {
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out);
};
struct dkm_net_scan_result {
char ssid[33];
u8 bssid[6];
u8 channel;
i8 rssi;
u8 security;
};
struct dkm_net_device_desc {
const char *name;
u8 mac[6];
u32 flags;
void *ctx;
int (*tx)(void *ctx, const void *packet, u32 length);
int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length);
/* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */
int (*scan_start)(void *ctx);
int (*scan_count)(void *ctx);
int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(void *ctx);
/* 关联扩展(尾部追加,ABI 兼容;与 UTSM/include/utsm/net.h 布局一致)。 */
int (*connect)(void *ctx, const char *ssid, const char *password);
int (*disconnect)(void *ctx);
};
struct dkm_net_stats {
u64 tx_ok;
u64 tx_err;
u64 rx_ok;
u64 rx_err;
u64 rx_overflow;
u64 link_changes;
};
struct dkm_net_api {
int (*register_device)(const struct dkm_net_device_desc *desc);
u32 (*device_count)(void);
int (*device_info)(u32 index, void *out);
int (*tx)(u32 index, const void *packet, u32 length);
int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length);
int (*scan_start)(u32 index);
int (*scan_count)(u32 index);
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(u32 index);
int (*connect)(u32 index, const char *ssid, const char *password);
int (*disconnect)(u32 index);
int (*device_stats)(u32 index, struct dkm_net_stats *out);
};
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
typedef int (*dkm_block_write_fn)(void *ctx, u64 lba, u32 count, const void *buffer);
struct dkm_block_device_desc {
const char *name;
u64 sector_size;
u64 sector_count;
void *ctx;
int (*read)(void *ctx, u64 lba, u32 count, void *buffer);
int (*write)(void *ctx, u64 lba, u32 count, const void *buffer);
};
struct dkm_block_api {
int (*register_device)(const struct dkm_block_device_desc *desc);
u32 (*device_count)(void);
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
u64 (*sector_size)(u32 index);
const char *(*device_name)(u32 index);
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
};
/* 与 UTSM/include/utsm/paging.h 中的 dkm_mmio_api 布局一致 */
struct dkm_mmio_api {
/* 四级页表 walk。返回 0=未映射;1=4K PTE;2=2M PDE;3=1G PDPTE */
int (*is_mapped)(u64 vaddr);
/* 把物理区间映射到 hhdm_offset + phys(4KiB UC 页),返回 0 成功 */
int (*map_mmio)(u64 phys, u64 size);
/* 当前 CR3 物理地址(诊断用) */
u64 (*cr3)(void);
};
/* 与 UTSM/include/utsm/dkm.h 中的 dkm_kernel_api 布局完全一致.
* 字段顺序/类型不可调整 — UTSM 端按此偏移填充. */
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const struct dkm_dma_api *dma;
const void *vfs;
const struct dkm_net_api *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
const struct dkm_block_api *block; /* 不使用 block 的驱动可不访问 */
const struct dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(HHDM 空洞原地补映射) */
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
/* B7 阶段2 尾部追加(旧内核无此字段 → 判空使用) */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3 尾部追加:动态 IDT 向量分配器(MSI/MSI-X),池 0x40-0xDF */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
/* ---- PCI 配置空间常量 ---- */
#define PCI_ADDR 0xCF8
#define PCI_DATA 0xCFC
#define PCI_VENDOR_ID 0x00
#define PCI_DEVICE_ID 0x02
#define PCI_COMMAND 0x04
#define PCI_PROG_IF 0x09
#define PCI_SUBCLASS 0x0A
#define PCI_CLASS 0x0B
#define PCI_HEADER 0x0E
#define PCI_CAP_PTR 0x34
#define PCI_IRQ_LINE 0x3C
#define PCI_BAR0 0x10
#define PCI_BAR1 0x14
#define PCI_BAR2 0x18
#define PCI_BAR3 0x1C
#define PCI_BAR4 0x20
#define PCI_BAR5 0x24
#define PCI_CMD_IO (1u << 0)
#define PCI_CMD_MEM (1u << 1)
#define PCI_CMD_BUSM (1u << 2)
/* ---- PCI I/O 端口内联函数 (每个 .drv 获得独立副本) ---- */
static __inline__ void dkm_outl(u16 port, u32 value) {
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u32 dkm_inl(u16 port) {
u32 value;
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static __inline__ void dkm_outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u8 dkm_inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static __inline__ u32 dkm_pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
u32 addr = (1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC);
dkm_outl(PCI_ADDR, addr);
return dkm_inl(PCI_DATA);
}
static __inline__ void dkm_pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
u32 addr = (1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC);
dkm_outl(PCI_ADDR, addr);
dkm_outl(PCI_DATA, value);
}
static __inline__ u8 dkm_pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
u32 v = dkm_pci_read(bus, dev, func, reg);
return (u8)((v >> ((reg & 3) * 8)) & 0xff);
}
/* ---- TSC 时序: 实机要求用 CPU 频率计算, 不用循环 ----
* 每个 .drv 包含此头文件后获得自己的 file-local dkm_tsc_per_ms 副本,
* 通过 dkm_tsc_calibrate() 在 driver_init 开头初始化.
*/
static u64 dkm_tsc_per_ms __attribute__((unused)) = 0;
static __inline__ u64 dkm_rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
/* 通过 PIT ch0 + TSC 校准 ~10ms 区间, 推算每毫秒 TSC 周期数. */
static __inline__ void dkm_tsc_calibrate(void) {
dkm_outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
dkm_outb(0x40, 0x7c); /* 11932 low = ~10ms */
dkm_outb(0x40, 0x2e); /* 11932 high */
u64 tsc_start = dkm_rdtsc();
u16 prev = 0; u64 loops = 0;
for (;;) {
dkm_outb(0x43, 0x00);
u16 cur = (u16)dkm_inb(0x40) | ((u16)dkm_inb(0x40) << 8);
if (cur > prev && loops > 10) break;
prev = cur; loops++;
}
u64 tsc_end = dkm_rdtsc();
dkm_tsc_per_ms = (tsc_end - tsc_start) / 10;
}
static __inline__ void dkm_delay_ms(u32 ms) {
if (!dkm_tsc_per_ms) {
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
return;
}
u64 target = dkm_tsc_per_ms * ms;
u64 start = dkm_rdtsc();
while (dkm_rdtsc() - start < target) __asm__ volatile("pause");
}
static __inline__ void dkm_delay_us(u32 us) {
if (!dkm_tsc_per_ms) {
for (volatile u32 i = 0; i < 100 * us; i++) __asm__ volatile("pause");
return;
}
u64 target = dkm_tsc_per_ms * us / 1000;
u64 start = dkm_rdtsc();
while (dkm_rdtsc() - start < target) __asm__ volatile("pause");
}
/* 实机: 基于 TSC 的串口发送等待 (100us 超时). com_port = 0x3F8 (COM1). */
static __inline__ void dkm_serial_wait_tx(u16 com_port) {
if (!dkm_tsc_per_ms) {
for (volatile u32 i = 0; i < 100000; i++) {
if (dkm_inb((u16)(com_port + 5)) & 0x20) break;
__asm__ volatile("pause");
}
return;
}
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms / 10;
while (dkm_rdtsc() < deadline) {
if (dkm_inb((u16)(com_port + 5)) & 0x20) break;
__asm__ volatile("pause");
}
}
#endif /* DKM_SHARED_H */
-832
View File
@@ -1,832 +0,0 @@
/* DKM e1000 Driver — Intel 8254x/8257x Gigabit Ethernet
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
* Supports classic e1000 (82540EM/82545/82546) and e1000e (82574L/I217/I218/I219).
* Finds device via PCI, maps MMIO, reads MAC (RAL/RAH or EEPROM),
* initializes RX/TX rings, provides link/speed/duplex diagnostics.
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(e1k_irq_count);
DKM_STAT_DECL(e1k_rx_packets);
DKM_STAT_DECL(e1k_tx_packets);
static const char *const g_depends[] = {"pci","irq"};
static const char *const g_provides[] = {"netdev"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="e1000",.version="0.2.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=2,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
};
/* e1000 MMIO registers */
#define E1000_CTRL 0x0000
#define E1000_STATUS 0x0008
#define E1000_EERD 0x0014
#define E1000_ICR 0x00C0
#define E1000_IMS 0x00D0
#define E1000_IMC 0x00D8
#define E1000_RAL 0x5400
#define E1000_RAH 0x5404
#define E1000_RDBAL 0x2800
#define E1000_RDBAH 0x2804
#define E1000_RDLEN 0x2808
#define E1000_RDH 0x2810
#define E1000_RDT 0x2818
#define E1000_RXDCTL 0x2828
#define E1000_RCTL 0x0100
#define E1000_TDBAL 0x3800
#define E1000_TDBAH 0x3804
#define E1000_TDLEN 0x3808
#define E1000_TDH 0x3810
#define E1000_TDT 0x3818
#define E1000_TXDCTL 0x3828
#define E1000_TCTL 0x0400
#define E1000_TIPG 0x0410
#define E1000_MANC 0x0580
/* STATUS register bits (link/speed/duplex diagnostic) */
#define E1000_STATUS_FD (1u << 0) /* full duplex */
#define E1000_STATUS_LU (1u << 1) /* link up */
#define E1000_STATUS_FUNC_SHIFT 2 /* PCI function number (ro) */
#define E1000_STATUS_TXOFF (1u << 4) /* transmitter paused */
#define E1000_STATUS_SPEEDMASK (3u << 6)
#define E1000_STATUS_SPEED_10 (0u << 6)
#define E1000_STATUS_SPEED_100 (1u << 6)
#define E1000_STATUS_SPEED_1000 (2u << 6) /* classic e1000 only */
/* CTRL register bits */
#define E1000_CTRL_FD (1u << 0)
#define E1000_CTRL_SLU (1u << 6)
#define E1000_CTRL_RST (1u << 26) /* software reset */
#define E1000_CTRL_ASDE (1u << 11) /* auto-speed detect enable */
#define E1000_CTRL_FRCSPD (1u << 12) /* force speed */
#define E1000_CTRL_FRCDPLX (1u << 13) /* force duplex */
/* e1000e (82574L/I217/I219) speed encoding in STATUS differs */
#define E1000_STATUS_SPEED_1000_E1000E (3u << 6)
#define E1000_RX_DESC_COUNT 8u
#define E1000_TX_DESC_COUNT 8u
#define E1000_RX_BUF_SIZE 2048u
#define E1000_TX_BUF_SIZE 2048u
#define E1000_RCTL_EN (1u << 1)
#define E1000_RCTL_SBP (1u << 2)
#define E1000_RCTL_UPE (1u << 3)
#define E1000_RCTL_MPE (1u << 4)
#define E1000_RCTL_BAM (1u << 15)
#define E1000_RCTL_SECRC (1u << 26)
#define E1000_RCTL_BSIZE_2048 0u
#define E1000_TCTL_EN (1u << 1)
#define E1000_TCTL_PSP (1u << 3)
#define E1000_TCTL_CT_SHIFT 4
#define E1000_TCTL_COLD_SHIFT 12
#define E1000_TX_CMD_EOP (1u << 0)
#define E1000_TX_CMD_IFCS (1u << 1)
#define E1000_TX_CMD_RS (1u << 3)
#define E1000_TX_STA_DD (1u << 0)
#define E1000_RX_STA_DD (1u << 0)
/* ---- e1000 device ID table ---- */
/* 分类: 0=经典 e1000 (8254x), 1=e1000e (8257x/I217/I218/I219) */
struct e1000_device_id {
u16 did;
u8 family; /* 0=classic, 1=e1000e */
const char *name;
};
static const struct e1000_device_id g_e1000_ids[] = {
/* 经典 e1000 (8254x 系列) — EERD 接口兼容 */
{0x100E, 0, "82540EM"}, /* QEMU e1000 default */
{0x1004, 0, "82543GC"}, /* Copper */
{0x100F, 0, "82545EM"}, /* Copper */
{0x1010, 0, "82546EB"}, /* Copper dual-port */
{0x1011, 0, "82545EM-Fiber"},
{0x1012, 0, "82546EB-Fiber"},
{0x1013, 0, "82541EI"},
{0x1015, 0, "82540EM-LOM"},
{0x1016, 0, "82540EP"},
{0x1017, 0, "82540EP-LOM"},
{0x1018, 0, "82541EI-LOM"},
{0x1019, 0, "82547EI"},
{0x101A, 0, "82547EI-LOM"},
{0x101D, 0, "82546EB-Quad"},
{0x1026, 0, "82545GM"},
{0x1027, 0, "82545GM-Fiber"},
{0x1028, 0, "82545GM-SerDes"},
{0x1075, 0, "82547GI"},
{0x1076, 0, "82541GI"},
{0x1077, 0, "82541GI-LOM"},
{0x1078, 0, "82541ER"},
{0x1079, 0, "82546GB"},
{0x107A, 0, "82546GB-Fiber"},
{0x107B, 0, "82546GB-SerDes"},
{0x107C, 0, "82541GI-Mobile"},
{0x108B, 0, "82573E"},
{0x108C, 0, "82573E-IAMT"},
{0x109A, 0, "82573L"},
/* e1000e (8257x/I217/I218/I219) 系列 — 寄存器兼容但 EERD/STATUS 编码有差异 */
{0x10D3, 1, "82574L"}, /* QEMU e1000e default */
{0x10F5, 1, "82578DM"},
{0x10F6, 1, "82578DC"},
{0x1502, 1, "82579V"},
{0x1503, 1, "82579V-2"},
{0x1533, 1, "I217-V"},
{0x1534, 1, "I217-LM"},
{0x153A, 1, "I218-V"},
{0x153B, 1, "I218-LM"},
{0x156F, 1, "I219-V"},
{0x1570, 1, "I219-LM"},
{0x157B, 1, "I219-V-2"},
{0x157A, 1, "I219-LM-2"},
{0x15D3, 1, "I219-V-3"},
{0x15D8, 1, "I219-LM-3"},
{0x15E3, 1, "I219-V-4"},
{0x15D6, 1, "I219-V-5"},
{0x15BD, 1, "I219-V-6"},
{0x15B8, 1, "I219-LM-4"},
{0x15BB, 1, "I219-LM-5"},
{0x15DF, 1, "I219-LM-6"},
{0x15E0, 1, "I219-V-7"},
{0x15E1, 1, "I219-LM-7"},
{0x15E2, 1, "I219-LM-8"},
{0x15E4, 1, "I219-V-8"},
{0x15E5, 1, "I219-V-9"},
{0x15F9, 1, "I219-LM-9"},
{0x15FA, 1, "I219-V-10"},
{0x15FB, 1, "I219-LM-10"},
{0x15FC, 1, "I219-LM-11"},
{0x15FD, 1, "I219-V-11"},
{0x15FE, 1, "I219-LM-12"},
{0x15FF, 1, "I219-V-12"},
/* I210/I211 (igb 系列) — 与 e1000e MMIO 兼容 */
{0x1537, 1, "I210"},
{0x1536, 1, "I211-AT"},
/* 82575/82576/82580 (igb 系列, 多队列, 基本兼容) */
{0x10A7, 1, "82575EB"},
{0x10A9, 1, "82575EB-SerDes"},
{0x10C9, 1, "82576"},
{0x10E6, 1, "82576-Fiber"},
{0x10E7, 1, "82576-Quad"},
{0x150D, 1, "82580"},
{0x1516, 1, "82580-Fiber"},
{0x1526, 1, "82576-2"},
{0, 0, 0} /* 终止符 */
};
struct e1000_rx_desc {
u64 addr;
u16 length;
u16 checksum;
u8 status;
u8 errors;
u16 special;
} __attribute__((packed));
struct e1000_tx_desc {
u64 addr;
u16 length;
u8 cso;
u8 cmd;
u8 status;
u8 css;
u16 special;
} __attribute__((packed));
/* ---- 统计计数器(用于真机诊断) ---- */
static u64 g_stat_tx_ok; /* TX 成功帧数 */
static u64 g_stat_tx_err; /* TX 失败帧数 */
static u64 g_stat_tx_timeout; /* TX 超时次数 */
static u64 g_stat_rx_ok; /* RX 成功帧数 */
static u64 g_stat_rx_err; /* RX 错误帧数 */
static u64 g_stat_rx_overflow; /* RX buffer 太小丢包 */
static u64 g_stat_irq_count; /* IRQ 中断次数 */
/* 前向声明 */
static void raw_log(const char *s);
static void raw_hex(const char *prefix, u64 v);
/* 每 256 次数据路径事件打印一行摘要(避免刷串口)。 */
static void net_stat_tick(void) {
u64 sum = g_stat_tx_ok + g_stat_tx_err + g_stat_tx_timeout +
g_stat_rx_ok + g_stat_rx_err + g_stat_rx_overflow;
if ((sum & 0xFF) != 0) return;
raw_log("[e1000.raw] stat tx_ok=");
raw_hex("", g_stat_tx_ok);
raw_log("[e1000.raw] stat tx_err=");
raw_hex("", g_stat_tx_err);
raw_log("[e1000.raw] stat rx_ok=");
raw_hex("", g_stat_rx_ok);
raw_log("[e1000.raw] stat rx_err=");
raw_hex("", g_stat_rx_err);
raw_log("[e1000.raw] stat rx_overflow=");
raw_hex("", g_stat_rx_overflow);
}
static const struct dkm_log_api *g_log;
static volatile u32 *g_mmio;
static u64 g_bar0_phys;
static struct e1000_rx_desc *g_rx_desc;
static struct e1000_tx_desc *g_tx_desc;
static u64 g_rx_desc_phys;
static u64 g_tx_desc_phys;
static u8 *g_rx_buf[E1000_RX_DESC_COUNT];
static u64 g_rx_buf_phys[E1000_RX_DESC_COUNT];
static u8 *g_tx_buf[E1000_TX_DESC_COUNT];
static u64 g_tx_buf_phys[E1000_TX_DESC_COUNT];
static u32 g_rx_tail;
static u32 g_tx_tail;
static int g_rings_ready;
static u8 g_device_family; /* 0=classic e1000, 1=e1000e */
static u16 g_device_id; /* PCI device ID */
#define COM1 0x3F8
/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */
static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); }
static void raw_log(const char *s) {
while (*s) {
char c = *s++;
if (c == '\n') {
serial_wait_tx();
dkm_outb(COM1, '\r');
}
serial_wait_tx();
dkm_outb(COM1, (u8)c);
}
}
static void raw_hex(const char *prefix, u64 v) {
static const char h[] = "0123456789abcdef";
raw_log(prefix);
raw_log("0x");
for (int i=15; i>=0; i--) {
serial_wait_tx();
dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]);
}
raw_log("\n");
}
/* 打印 MAC 地址 */
static void raw_mac(const char *prefix, const u8 mac[6]) {
static const char h[] = "0123456789abcdef";
raw_log(prefix);
for (int i = 0; i < 6; i++) {
serial_wait_tx();
dkm_outb(COM1, (u8)h[(mac[i] >> 4) & 0xf]);
serial_wait_tx();
dkm_outb(COM1, (u8)h[mac[i] & 0xf]);
if (i < 5) {
serial_wait_tx();
dkm_outb(COM1, ':');
}
}
raw_log("\n");
}
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
dkm_pci_write(bus, dev, func, reg, v);
}
/* 根据 PCI Device ID 查找设备信息 */
static const struct e1000_device_id *e1000_lookup_id(u16 did) {
for (int i = 0; g_e1000_ids[i].did != 0; i++) {
if (g_e1000_ids[i].did == did) return &g_e1000_ids[i];
}
return 0;
}
/* 多总线扫描 e1000 设备。真机网卡可能在 bus>0 上。 */
static int e1000_find(u8 *out_bus, u8 *out_dev, u8 *out_func,
const struct e1000_device_id **out_id) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
if ((vd & 0xffff) != 0x8086) continue;
/* 检查是否为多功能设备 */
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
if ((vd2 & 0xffff) != 0x8086) continue;
u16 did = (u16)(vd2 >> 16);
const struct e1000_device_id *id = e1000_lookup_id(did);
if (id) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
if (out_id) *out_id = id;
return 0;
}
}
}
}
return -1;
}
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。
* 经典 e1000: EERD bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位。
* e1000e: EERD 接口类似但地址字段位置不同 (addr<<2, bit1=START, bit2=DONE)。
* 失败返回 0xffff。 */
static u16 e1000_eeprom_read(u16 addr) {
if (g_device_family == 1) {
/* e1000e: EERD addr<<2 | START(bit1), DONE=bit2, data=bits[31:16] */
g_mmio[E1000_EERD/4] = ((u32)addr << 2) | 0x2u;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 v = g_mmio[E1000_EERD/4];
if (v & (1u << 2)) return (u16)(v >> 16);
__asm__ volatile("pause");
}
} else {
/* 经典 e1000: EERD addr<<8 | START(bit0), DONE=bit4 */
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 v = g_mmio[E1000_EERD/4];
if (v & (1u << 4)) return (u16)(v >> 16);
__asm__ volatile("pause");
}
}
return 0xffff;
}
/* 检测链路状态,返回 0=down, 1=up */
static int e1000_link_up(void) {
if (!g_mmio) return 0;
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_LU) ? 1 : 0;
}
/* 读取当前速度 (Mbps),返回 10/100/1000/0(未知) */
static u32 e1000_link_speed(void) {
if (!g_mmio) return 0;
u32 status = g_mmio[E1000_STATUS/4];
u32 speed_bits = status & E1000_STATUS_SPEEDMASK;
if (g_device_family == 1) {
/* e1000e speed encoding: 00=reserved, 01=100, 10=1000, 11=1000 */
if (speed_bits == E1000_STATUS_SPEED_1000_E1000E ||
speed_bits == (2u << 6))
return 1000;
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
return 0;
} else {
/* 经典 e1000 speed encoding: 00=10, 01=100, 10=1000 */
if (speed_bits == E1000_STATUS_SPEED_1000) return 1000;
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
return 0;
}
}
/* 读取当前双工模式,返回 1=全双工, 0=半双工 */
static int e1000_link_full_duplex(void) {
if (!g_mmio) return 0;
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_FD) ? 1 : 0;
}
/* 打印链路诊断信息 */
static void e1000_log_link_status(void) {
int up = e1000_link_up();
if (up) {
u32 speed = e1000_link_speed();
int fd = e1000_link_full_duplex();
const char *speed_str = "unknown";
char speed_buf[8];
if (speed == 1000) speed_str = "1000";
else if (speed == 100) speed_str = "100";
else if (speed == 10) speed_str = "10";
else {
/* 手动转数字 */
speed_buf[0] = '0' + (char)(speed / 1000 % 10);
speed_buf[1] = '0' + (char)(speed / 100 % 10);
speed_buf[2] = '0' + (char)(speed / 10 % 10);
speed_buf[3] = '0' + (char)(speed % 10);
speed_buf[4] = 0;
speed_str = speed_buf;
}
raw_log("[e1000] link=up speed=");
raw_log(speed_str);
raw_log("Mbps duplex=");
raw_log(fd ? "full" : "half");
raw_log("\n");
} else {
raw_log("[e1000] link=down\n");
}
raw_hex("[e1000] STATUS=", g_mmio ? g_mmio[E1000_STATUS/4] : 0);
}
/* 等待链路就绪(最长 2 秒),实机 PHY 自动协商可能需要时间 */
static int e1000_wait_for_link(void) {
if (!g_mmio) return -1;
/* 设置 ASDE (Auto-Speed Detect Enable) 让 PHY 自动协商 */
if (g_device_family == 1) {
/* e1000e: 建议先清除 FRCSPD/FRCDPLX,让自动协商生效 */
u32 ctrl = g_mmio[E1000_CTRL/4];
ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPLX);
ctrl |= E1000_CTRL_SLU | E1000_CTRL_ASDE;
g_mmio[E1000_CTRL/4] = ctrl;
}
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
if (e1000_link_up()) return 0;
__asm__ volatile("pause");
}
return -1; /* 链路未就绪但驱动继续初始化(后续可能 link up) */
}
static int irq_handler(u8 irq) {
(void)irq;
if (!g_mmio) return 0;
u32 icr = g_mmio[E1000_ICR/4];
if (icr) {
g_stat_irq_count++;
DKM_STAT_INC(e1k_irq_count);
g_mmio[E1000_ICR/4] = icr; /* clear */
}
return 0;
}
static void copy_bytes(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i=0;i<len;i++) d[i]=s[i];
}
static void zero_bytes(void *dst, u32 len) {
u8 *d = (u8 *)dst;
for (u32 i=0;i<len;i++) d[i]=0;
}
static void mmio_flush(void) {
if (g_mmio) (void)g_mmio[E1000_STATUS/4];
}
static void dma_fence(void) {
__asm__ volatile("" ::: "memory");
}
static int e1000_init_rings(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
struct dkm_dma_buffer rx_ring, tx_ring;
if (dma->alloc_pages(1, 16, 0x100000000ULL, &rx_ring) != 0) return -2;
if (dma->alloc_pages(1, 16, 0x100000000ULL, &tx_ring) != 0) return -3;
g_rx_desc = (struct e1000_rx_desc *)rx_ring.virt;
g_tx_desc = (struct e1000_tx_desc *)tx_ring.virt;
g_rx_desc_phys = rx_ring.phys;
g_tx_desc_phys = tx_ring.phys;
for (u32 i=0;i<E1000_RX_DESC_COUNT;i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -4;
g_rx_buf[i] = (u8 *)b.virt;
g_rx_buf_phys[i] = b.phys;
zero_bytes(&g_rx_desc[i], sizeof(g_rx_desc[i]));
g_rx_desc[i].addr = b.phys;
}
for (u32 i=0;i<E1000_TX_DESC_COUNT;i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -5;
g_tx_buf[i] = (u8 *)b.virt;
g_tx_buf_phys[i] = b.phys;
zero_bytes(&g_tx_desc[i], sizeof(g_tx_desc[i]));
g_tx_desc[i].addr = b.phys;
g_tx_desc[i].status = E1000_TX_STA_DD;
}
dma_fence();
raw_log("[e1000] ring/buffers allocated\n");
g_mmio[E1000_RCTL/4] = 0;
g_mmio[E1000_TCTL/4] = 0;
mmio_flush();
g_mmio[E1000_RDBAL/4] = (u32)(g_rx_desc_phys & 0xffffffffu);
g_mmio[E1000_RDBAH/4] = (u32)(g_rx_desc_phys >> 32);
g_mmio[E1000_RDLEN/4] = E1000_RX_DESC_COUNT * sizeof(struct e1000_rx_desc);
g_mmio[E1000_RDH/4] = 0;
g_rx_tail = E1000_RX_DESC_COUNT - 1;
g_mmio[E1000_RDT/4] = g_rx_tail;
g_mmio[E1000_TDBAL/4] = (u32)(g_tx_desc_phys & 0xffffffffu);
g_mmio[E1000_TDBAH/4] = (u32)(g_tx_desc_phys >> 32);
g_mmio[E1000_TDLEN/4] = E1000_TX_DESC_COUNT * sizeof(struct e1000_tx_desc);
g_mmio[E1000_TDH/4] = 0;
g_tx_tail = 0;
g_mmio[E1000_TDT/4] = 0;
/* e1000e/部分实现需要显式打开队列描述符控制;经典 e1000 会忽略/兼容。 */
g_mmio[E1000_RXDCTL/4] = (1u << 25);
g_mmio[E1000_TXDCTL/4] = (1u << 25);
g_mmio[E1000_TIPG/4] = 0x0060200Au;
dma_fence();
g_mmio[E1000_RCTL/4] = E1000_RCTL_EN | E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_BAM | E1000_RCTL_SECRC | E1000_RCTL_BSIZE_2048;
g_mmio[E1000_TCTL/4] = E1000_TCTL_EN | E1000_TCTL_PSP | (0x0Fu << E1000_TCTL_CT_SHIFT) | (0x40u << E1000_TCTL_COLD_SHIFT);
mmio_flush();
g_rings_ready = 1;
raw_log("[e1000] RX/TX rings initialized\n");
return 0;
}
static int e1000_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) {
g_stat_tx_err++;
return -1;
}
u32 index = g_tx_tail;
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) {
g_stat_tx_err++;
return -2; /* TX ring full */
}
copy_bytes(g_tx_buf[index], packet, length);
dma_fence();
g_tx_desc[index].length = (u16)length;
g_tx_desc[index].cso = 0;
g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS;
g_tx_desc[index].css = 0;
g_tx_desc[index].special = 0;
g_tx_desc[index].status = 0;
dma_fence();
g_tx_tail = (index + 1) % E1000_TX_DESC_COUNT;
g_mmio[E1000_TDT/4] = g_tx_tail;
mmio_flush();
/* 实机: 1秒 TSC 超时等待 TX 完成 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
dma_fence();
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
g_stat_tx_ok++;
DKM_STAT_INC(e1k_tx_packets);
net_stat_tick();
return 0;
}
__asm__ volatile("pause");
}
g_stat_tx_timeout++;
g_stat_tx_err++;
raw_log("[e1000] tx timeout\n");
raw_hex("[e1000] STATUS=", g_mmio[E1000_STATUS/4]);
raw_hex("[e1000] TCTL=", g_mmio[E1000_TCTL/4]);
raw_hex("[e1000] TDH=", g_mmio[E1000_TDH/4]);
raw_hex("[e1000] TDT=", g_mmio[E1000_TDT/4]);
return -3;
}
static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx;
if (out_length) *out_length = 0;
if (!g_rings_ready || !buffer || !out_length) return -1;
u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT;
dma_fence();
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1; /* no data */
u32 len = g_rx_desc[next].length;
if (len > capacity) {
g_stat_rx_overflow++;
g_rx_desc[next].status = 0;
dma_fence();
g_rx_tail = next;
g_mmio[E1000_RDT/4] = g_rx_tail;
mmio_flush();
return -2;
}
copy_bytes(buffer, g_rx_buf[next], len);
*out_length = len;
g_rx_desc[next].status = 0;
dma_fence();
g_rx_tail = next;
g_mmio[E1000_RDT/4] = g_rx_tail;
mmio_flush();
g_stat_rx_ok++;
DKM_STAT_INC(e1k_rx_packets);
net_stat_tick();
return 0;
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
dkm_tsc_calibrate();
raw_log("[e1000] driver_init entered\n");
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
u8 bus, dev, func;
const struct e1000_device_id *dev_id = 0;
if (e1000_find(&bus, &dev, &func, &dev_id) != 0) {
raw_log("[e1000] device not found\n");
return 0;
}
g_device_id = (u16)(pci_read(bus, dev, func, PCI_VENDOR_ID) >> 16);
g_device_family = dev_id ? dev_id->family : 0;
raw_log("[e1000] found device: ");
raw_log(dev_id ? dev_id->name : "unknown");
raw_log(g_device_family == 1 ? " (e1000e)" : " (classic)");
raw_log("\n");
raw_hex("[e1000] PCI bus:dev:func=", ((u64)bus << 16) | ((u64)dev << 8) | func);
/* 检查 PCI class code 确认是网络设备 (class=0x02) */
u32 class_reg = pci_read(bus, dev, func, 0x08);
u8 class_code = (u8)(class_reg >> 24);
if (class_code != 0x02) {
raw_log("[e1000] WARNING: PCI class is not Network\n");
}
u32 cmd = pci_read(bus, dev, func, PCI_COMMAND);
cmd |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, cmd);
raw_log("[e1000] PCI command enabled\n");
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
if (bar0 & 1u) {
raw_log("[e1000] IO BAR unsupported\n");
return 0;
}
g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0ULL);
if ((bar0 & 0x6u) == 0x4u) {
u32 bar1 = pci_read(bus, dev, func, PCI_BAR1);
g_bar0_phys |= ((u64)bar1 << 32);
}
raw_hex("[e1000] BAR0 phys=", g_bar0_phys);
if (!g_bar0_phys) {
raw_log("[e1000] BAR0 unavailable\n");
return 0;
}
/* 真机 BAR0 可能在 4G 以上(如 I219),需 mm_map_mmio 检查 */
u64 hhdm = api->hhdm_offset;
g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys);
/* 尝试读取 STATUS 寄存器验证映射是否有效 */
u32 status_val = g_mmio[E1000_STATUS/4];
(void)status_val;
raw_log("[e1000] MMIO status read ok\n");
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_RST;
/* 实机: 1秒 TSC 超时等待复位完成 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (!(g_mmio[E1000_CTRL/4] & E1000_CTRL_RST)) break;
__asm__ volatile("pause");
}
if (g_mmio[E1000_CTRL/4] & E1000_CTRL_RST) {
raw_log("[e1000] WARNING: reset did not complete\n");
} else {
raw_log("[e1000] reset done\n");
}
/* 等待 EEPROM 完成(实机复位后 EEPROM 状态机需要时间) */
dkm_delay_ms(1);
/* 读 MAC 地址:先尝试 RAL/RAH,空时走 EEPROM */
u32 ral = g_mmio[E1000_RAL/4];
u32 rah = g_mmio[E1000_RAH/4];
u8 mac[6];
mac[0] = (u8)(ral & 0xff);
mac[1] = (u8)((ral>>8) & 0xff);
mac[2] = (u8)((ral>>16) & 0xff);
mac[3] = (u8)((ral>>24) & 0xff);
mac[4] = (u8)(rah & 0xff);
mac[5] = (u8)((rah>>8) & 0xff);
/* RAL/RAH 为空时从 EEPROM 读 MAC */
if (ral == 0 && (rah & 0xffff) == 0) {
u16 w0 = e1000_eeprom_read(0);
u16 w1 = e1000_eeprom_read(1);
u16 w2 = e1000_eeprom_read(2);
if (!(w0==0xffff && w1==0xffff && w2==0xffff)) {
mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8);
mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8);
mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8);
raw_log("[e1000] MAC from EEPROM\n");
} else {
raw_log("[e1000] EEPROM read failed (all 0xffff)\n");
}
} else {
raw_log("[e1000] MAC from RAL/RAH\n");
}
/* 仍为全 0 或多播位错误时——严格错误策略插桩:MAC 读不到 = 网卡
* 身份不可知,零降级 panic(E1K-E01),不再用硬编码 MAC 冒充。 */
if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) ||
(mac[0] & 1)) {
raw_log("[e1000] MAC read failed (zero/multicast)\n");
g_log->panic("E1K-E01 MAC READ FAILED");
/* fallback:本地管理单播地址(bit1=LAA,bit0=0),"DESHB" */
mac[0]=0x02; mac[1]=0x44; mac[2]=0x45; mac[3]=0x53; mac[4]=0x48; mac[5]=0x42;
raw_log("[e1000] fallback: using locally administered MAC\n");
}
raw_mac("[e1000] MAC=", mac);
/* 写入 RAL/RAH 并置 AV(bit31) 使能接收该单播地址 */
g_mmio[E1000_RAL/4] = (u32)mac[0] | ((u32)mac[1]<<8) | ((u32)mac[2]<<16) | ((u32)mac[3]<<24);
g_mmio[E1000_RAH/4] = (u32)mac[4] | ((u32)mac[5]<<8) | (1u<<31);
dma_fence();
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
raw_hex("[e1000] IRQ line=", irq_line);
/* 设置链路:FD + SLU,并允许自动协商 */
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU;
mmio_flush();
g_mmio[E1000_IMC/4] = 0xffffffffu;
(void)g_mmio[E1000_ICR/4];
raw_log("[e1000] interrupts masked\n");
/* 等待链路就绪(实机 PHY 自动协商可能需要 1-2 秒) */
int link_rc = e1000_wait_for_link();
if (link_rc == 0) {
raw_log("[e1000] link up after auto-negotiation\n");
} else {
raw_log("[e1000] link not yet up (will retry during operation)\n");
}
/* 打印链路诊断 */
e1000_log_link_status();
int ring_rc = e1000_init_rings(api->dma);
if (ring_rc != 0) {
raw_log("[e1000] ring init failed\n");
raw_hex("[e1000] ring_rc=", (u64)(i32)ring_rc);
}
/* 根据链路状态设置 netdev flags */
u32 netdev_flags = 0;
if (e1000_link_up()) netdev_flags |= DKM_NET_F_LINK_UP;
if (g_rings_ready) netdev_flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "e1000";
for (int i=0;i<6;i++) netdev.mac[i] = mac[i];
netdev.flags = netdev_flags;
netdev.ctx = 0;
netdev.tx = g_rings_ready ? e1000_tx : 0;
netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0;
int net_index = api->net->register_device(&netdev);
raw_hex("[e1000] netdev registered, index=", (u64)(i32)net_index);
} else {
raw_log("[e1000] net API unavailable\n");
}
g_mmio[E1000_IMC/4] = 0xffffffffu;
if (api->irq_register && irq_line < 16 && irq_line > 0) {
api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler);
raw_log("[e1000] IRQ registered\n");
} else {
raw_log("[e1000] IRQ not registered (polling mode)\n");
}
/* 最终诊断快照 */
e1000_log_link_status();
raw_log("[e1000] driver ready\n");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* 关闭 RX/TX,释放 rings */
if (g_mmio) {
g_mmio[E1000_RCTL/4] = 0;
g_mmio[E1000_TCTL/4] = 0;
g_mmio[E1000_IMC/4] = 0xffffffffu;
mmio_flush();
}
g_rings_ready = 0;
g_mmio = 0;
return 0;
}
-1172
View File
@@ -1,1172 +0,0 @@
/* DKM EHCI Driver — USB2 host controller + Mass Storage Class (BOT/SCSI)
*
* Stage 1 optional storage driver, depends on "pci", provides "block".
* Target: Intel 6/7-series EHCI (PCI class 0C/03/20, device 1C2D/1C26)
* and any standard EHCI controller — the USB2 path used on platforms
* without xHCI (e.g. H61/B75 with only EHCI controllers).
*
* Synchronous polling model (no IRQ): async list + QH/TD execution,
* transfer completion detected by polling the QH overlay token.
*
* Pipeline:
* 1. PCI discovery: class=0x0C/03/20
* 2. MMIO mapping: BAR0 via mm_map_mmio / HHDM
* 3. HC init: HCRESET, port power, RUN
* 4. Port scan: reset, wait PED, read speed
* 5. Enumeration: SET_ADDRESS, GET_DESCRIPTOR(dev), SET_CONFIGURATION
* 6. MSC: BOT (CBW/CSW) + SCSI (INQUIRY/RD_CAP/READ10/WRITE10)
* 7. Block provider: register usb2 via kernel_api.block
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(ehci_pci_found);
DKM_STAT_DECL(ehci_port_ok);
DKM_STAT_DECL(ehci_msc_found);
DKM_STAT_DECL(ehci_block_reads);
DKM_STAT_DECL(ehci_block_writes);
static const char *const g_depends[] = { "pci" };
static const char *const g_provides[] = { "block" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "ehci",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 1,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ================================================================
* Constants — EHCI registers, QH/TD, USB protocol
* ================================================================ */
#define EHCI_CLASS_SERIAL 0x0C
#define EHCI_SUBCLASS_USB 0x03
#define EHCI_PROGIF_EHCI 0x20
/* Capability registers (MMIO + 0x00) */
#define EHCI_CAP_CAPLENGTH 0x00
#define EHCI_CAP_HCSPARAMS 0x04
#define EHCI_CAP_HCCPARAMS 0x08
/* Operational registers (MMIO + caplength) */
#define EHCI_OP_USBCMD 0x00
#define EHCI_OP_USBSTS 0x04
#define EHCI_OP_USBINTR 0x08
#define EHCI_OP_FRINDEX 0x0C
#define EHCI_OP_ASYNCLIST 0x18
#define EHCI_OP_CONFIGFLAG 0x40
#define EHCI_OP_PORTSC 0x44
#define EHCI_PORT_STRIDE 0x04
/* USBCMD bits */
#define USBCMD_RUN (1u << 0)
#define USBCMD_HCRESET (1u << 1)
#define USBCMD_PSE (1u << 4)
#define USBCMD_ASE (1u << 5)
/* USBSTS bits */
#define USBSTS_USBINT (1u << 0)
#define USBSTS_PCD (1u << 2)
#define USBSTS_FLR (1u << 3)
#define USBSTS_HSE (1u << 4)
#define USBSTS_IAA (1u << 6)
#define USBSTS_HCHALT (1u << 12)
/* PORTSC bits — 与 QEMU include/hw/usb/ehci-regs.h / EHCI 1.0 spec 一致
* BUG-FIX: 原定义位错(PED=bit1 应为 bit2,speed 在 LINESTAT bit10:11 而非
* bit14:15,change 位是 bit1/3/5 而非 bit17/18/20/21)——端口永不使能、
* 速度永远读 0,导致 HC 不执行该端口上的传输。 */
#define PORTSC_CONNECT (1u << 0) /* CCS */
#define PORTSC_CSC (1u << 1) /* Connect Status Change (W1C) */
#define PORTSC_PED (1u << 2) /* Port Enable */
#define PORTSC_PEDC (1u << 3) /* Port Enable/Disable Change (W1C) */
#define PORTSC_OCA (1u << 4) /* Over-Current Active */
#define PORTSC_OCC (1u << 5) /* Over-Current Change (W1C) */
#define PORTSC_FPR (1u << 6) /* Force Port Resume */
#define PORTSC_SUSP (1u << 7) /* Suspend */
#define PORTSC_PR (1u << 8) /* Port Reset */
#define PORTSC_LS_SHIFT 10 /* Line Status = 端口速度 (bit10:11) */
#define PORTSC_LS_MASK 0x3
#define PORTSC_PP (1u << 12) /* Port Power */
#define PORTSC_POWNER (1u << 13)
/* RWC 位(W1C):CSC|PEDC|OCC —— 清 change 时写这些位 */
#define PORTSC_RWC (PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC)
/* PLS/LS 值(Line Status 字段,非 PLS 状态机) */
#define LS_FULL 0
#define LS_LOW 1
#define LS_HIGH 2
/* TD token bits */
#define TD_ACTIVE (1u << 7)
#define TD_HALTED (1u << 6)
#define TD_BUFERR (1u << 5)
#define TD_BABBLE (1u << 4)
#define TD_XACTERR (1u << 3)
#define TD_PID_SHIFT 8
#define TD_PID_MASK 0x3
#define TD_CERR_SHIFT 10
#define TD_CERR_MASK 0x3
#define TD_IOC (1u << 15)
#define TD_BYTES_SHIFT 16
#define TD_BYTES_MASK 0x7FFF
#define TD_TOGGLE (1u << 31)
#define PID_OUT 0
#define PID_IN 1
#define PID_SETUP 2
/* QH 链接:最低位 1 = 列表尾(T 位),0 = QH 指针 */
#define QH_TERMINATE 0x1u
/* USB speeds (EPCAP/QH ep speed 字段) */
#define USB_SPEED_FULL 0
#define USB_SPEED_LOW 1
#define USB_SPEED_HIGH 2
/* BOT signatures */
#define BOT_CBW_SIG 0x43425355u /* "USBC" */
#define BOT_CSW_SIG 0x53425355u /* "USBS" */
/* SCSI opcodes */
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
#define SCSI_READ_CAPACITY10 0x25
#define SCSI_READ10 0x28
#define SCSI_WRITE10 0x2A
/* ================================================================
* Data structures
* ================================================================ */
/* EHCI Transfer Descriptor — 32 字节,32 字节对齐 */
typedef struct {
u32 next; /* +0 */
u32 alt_next; /* +4 */
u32 token; /* +8 */
u32 buf[5]; /* +12 */
} ehci_td;
/* EHCI Queue Head — 44 字节,32 字节对齐
* 布局必须与 QEMU v11 EHCIqh / EHCI 1.0 spec 完全一致:
* +0 hlink(next), +4 epchar, +8 epcap(端点能力: hub addr+smask/cmask),
* +12 current_qtd, +16 next_qtd(overlay), +20 altnext(overlay), +24 token(overlay),
* +28 buf[5]
* BUG-FIX(关键): epcap 字段不能删!QEMU 从 +16 读 next_qtd、+20 读 altnext、
* +24 读 token。删掉 epcap 使 overlay 错位 4 字节——驱动写的 ov_next 落到
* current_qtd 位、ov_alt(0x1=T) 落到 next_qtd 位 → QEMU 认为 QH 无 TD
* 永不 FETCHQTD(token 恒 ACTIVE 0x80280)。 */
typedef struct {
u32 hlink; /* +0 */
u32 epchar; /* +4 */
u32 epcap; /* +8 */
u32 curqtd; /* +12 */
u32 ov_next; /* +16 overlay.next_qtd */
u32 ov_alt; /* +20 overlay.altnext */
u32 ov_token; /* +24 overlay.token */
u32 ov_buf[5]; /* +28 */
} ehci_qh;
/* MSC device state */
typedef struct {
u8 dev_addr;
u8 speed;
u8 bulk_out_ep; /* endpoint number (1..15) */
u8 bulk_in_ep;
u16 bulk_out_mps;
u16 bulk_in_mps;
u32 cbw_tag;
u64 sector_count;
u32 sector_size;
int ready;
} ehci_msc_dev;
/* ================================================================
* Global state
* ================================================================ */
static const struct dkm_kernel_api *g_api;
static const struct dkm_log_api *g_log;
static volatile u8 *g_mmio; /* EHCI BAR0 virtual */
static u32 g_caplen;
static u32 g_nports;
static u32 g_64bit; /* HCCPARAMS bit0 */
static u32 g_ppc; /* per-port power control bit4 */
/* DMA:异步列表区(QH/TD 池)+ 数据缓冲 + CSW 缓冲 */
static struct dkm_dma_buffer g_qh_dma; /* 4KB:QH×4 + TD×8 */
static struct dkm_dma_buffer g_xfer; /* 数据 bounce buffer(>=64KB) */
static struct dkm_dma_buffer g_csw; /* CSW 独立缓冲(防覆盖数据区前 13 字节) */
static ehci_qh *g_qh_ctrl;
static ehci_qh *g_qh_bulk_out;
static ehci_qh *g_qh_bulk_in;
static ehci_td *g_td_pool;
static u32 g_td_next;
static ehci_msc_dev g_msc;
static u8 g_pci_bus, g_pci_dev, g_pci_func;
/* ================================================================
* Utility
* ================================================================ */
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--)
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void ehci_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static void ehci_memcpy(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_mmio + off);
}
static void mmio_write32(u32 off, u32 value) {
*(volatile u32 *)(void *)(g_mmio + off) = value;
}
static void ehci_mb(void) {
__asm__ volatile("" ::: "memory");
}
/* 把物理地址编码进 32 位链接字段(64 位控制器用 CTRLDSSEGMENT 高 32 位) */
/* (内联使用,见 qh_submit_control/qh_enqueue) */
/* ================================================================
* Phase 1: PCI discovery + MMIO mapping
* ================================================================ */
static int ehci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == EHCI_CLASS_SERIAL &&
subclass == EHCI_SUBCLASS_USB &&
prog_if == EHCI_PROGIF_EHCI) {
*out_bus = (u8)bus;
*out_dev = dev;
*out_func = func;
return 0;
}
}
}
}
return -1;
}
static volatile u8 *ehci_map_bar(u64 bar_phys) {
u64 bar_virt = g_api->hhdm_offset + bar_phys;
if (g_api->mm_map_mmio) {
void *mapped = g_api->mm_map_mmio(bar_phys, 0x1000);
if (mapped) {
g_log->info("[ehci] BAR mapped via mm_map_mmio");
return (volatile u8 *)mapped;
}
}
if (g_api->mmio && g_api->mmio->is_mapped) {
if (g_api->mmio->is_mapped(bar_virt) > 0) {
g_log->info("[ehci] HHDM covers BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
if (g_api->mmio->map_mmio) {
if (g_api->mmio->map_mmio(bar_phys, 0x1000) == 0 &&
g_api->mmio->is_mapped(bar_virt) > 0) {
g_log->info("[ehci] BAR mapped via page tables");
return (volatile u8 *)(uintptr_t)bar_virt;
}
}
}
if (bar_phys < 0x100000000ULL) {
g_log->info("[ehci] assuming HHDM covers low BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
g_log->error("[ehci] BAR MMIO unavailable");
return 0;
}
/* ================================================================
* Phase 1: HC reset and start
* ================================================================ */
static int ehci_hc_reset(void) {
/* 等待 HCHALT=1(Halted 时才能写 HCRESET) */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
if (mmio_read32(g_caplen + EHCI_OP_USBSTS) & USBSTS_HCHALT) break;
__asm__ volatile("pause");
}
mmio_write32(g_caplen + EHCI_OP_USBCMD, USBCMD_HCRESET);
deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
if (!(mmio_read32(g_caplen + EHCI_OP_USBCMD) & USBCMD_HCRESET)) {
g_log->info("[ehci] HC reset complete");
return 0;
}
__asm__ volatile("pause");
}
g_log->error("[ehci] HC reset timeout");
return -1;
}
/* ================================================================
* Phase 2: QH/TD management
* ================================================================ */
static ehci_td *td_alloc(void) {
if (g_td_next >= 8) return 0;
ehci_td *td = &g_td_pool[g_td_next++];
ehci_zero(td, sizeof(ehci_td));
td->next = QH_TERMINATE;
td->alt_next = QH_TERMINATE;
return td;
}
/* 初始化一个 QH:endpoint=0 控制 / 指定端点 bulk。
* 注意:不能动 hlink —— ehci_hc_start 在启动时已把 ctrl/out/in 三个 QH
* 链成环;每次提交只更新 epchar/overlay,hlink 必须保留(否则环断裂,
* HC 停止遍历,overlay token 恒 0 → qh_wait 假成功返回空数据)。 */
static void qh_init(ehci_qh *qh, u8 dev_addr, u8 ep_num, u8 speed,
u16 mps, int control) {
qh->curqtd = 0;
qh->ov_next = QH_TERMINATE;
qh->ov_alt = QH_TERMINATE;
qh->ov_token = 0; /* inactive */
u32 epchar = (u32)dev_addr; /* [6:0] */
epchar |= ((u32)ep_num << 8); /* [11:8] */
epchar |= ((u32)speed << 12); /* [14:12] */
epchar |= ((u32)mps << 16); /* [26:16] */
if (control) epchar |= (1u << 27); /* control endpoint */
/* DTC 清 0:由 HC 管理 data toggle。
* H 位(bit15) 保留:ctrl 是环头 QH,每次提交重建 epchar 后必须
* 重新置 H,否则 QEMU WAITLISTHEAD 找不到入口(只在 H=1 的 QH
* 处进入 FETCHENTRY)。 */
u32 keep_h = qh->epchar & (1u << 15);
qh->epchar = epchar | keep_h;
/* epcap(端点能力):直连根端口 hub addr=0, smask/cmask=0。
* 该字段必须存在且清零——QEMU 依赖它使 overlay 偏移正确。 */
qh->epcap = 0;
}
/* 构建控制传输 TD 链:SETUP -> [DATA] -> STATUS,挂到 qh。 */
static int qh_submit_control(ehci_qh *qh, const u8 *setup8,
u8 *data, u32 data_len, u8 dir) {
/* 清空旧链:重置 overlay */
qh->curqtd = 0;
qh->ov_next = QH_TERMINATE;
qh->ov_token = 0;
g_td_next = 0; /* 复用池(单传输假设) */
ehci_td *t_setup = td_alloc();
ehci_td *t_data = 0;
ehci_td *t_status = td_alloc();
if (!t_setup || !t_status) return -1;
if (data_len > 0) {
t_data = td_alloc();
if (!t_data) return -2;
}
/* SETUP TD:8 字节,toggle=0,pid=SETUP
* 注意:必须先分配 t_data 再算 t_setup->next(原实现引用未分配
* 的 t_data → TD 链指向垃圾地址 → HC 走链崩溃/超时)。 */
if (data_len > 0) {
t_setup->next = (u32)(g_qh_dma.phys + (u64)((u8 *)t_data - (u8 *)g_qh_dma.virt));
} else {
t_setup->next = (u32)(g_qh_dma.phys + (u64)((u8 *)t_status - (u8 *)g_qh_dma.virt));
}
/* setup 数据嵌入 QH overlay buffer?标准做法:SETUP TD buffer 指向含 8 字节 setup 的内存 */
/* 简化:setup 包拷贝到 xfer 缓冲头部,TD buffer 指向它 */
{
u8 *sb = (u8 *)g_xfer.virt;
ehci_memcpy(sb, setup8, 8);
__asm__ volatile("wbinvd" ::: "memory");
t_setup->buf[0] = (u32)(g_xfer.phys & 0xFFFFFFFFu);
u32 tok = TD_ACTIVE | (PID_SETUP << TD_PID_SHIFT) | (8u << TD_BYTES_SHIFT);
t_setup->token = tok; /* toggle=0 */
}
/* DATA TD(可选)——t_data 已在上方分配,此处不再重复分配 */
if (data_len > 0 && t_data) {
/* 数据在 g_xfer.virt + 8 偏移处(IN:设备写这里;OUT:软件先拷入) */
u8 *db = (u8 *)g_xfer.virt + 8;
if (dir == PID_OUT) {
ehci_memcpy(db, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
t_data->buf[0] = (u32)((g_xfer.phys + 8) & 0xFFFFFFFFu);
t_data->next = (u32)(g_qh_dma.phys + (u64)((u8 *)t_status - (u8 *)g_qh_dma.virt));
u32 tok = TD_ACTIVE | ((u32)dir << TD_PID_SHIFT) | ((u32)data_len << TD_BYTES_SHIFT) | TD_TOGGLE;
t_data->token = tok; /* toggle=1 */
}
/* STATUS TD:反向 pid,toggle=1,长度 0 */
{
u8 spid = (dir == PID_IN) ? PID_OUT : PID_IN;
if (data_len == 0) spid = (dir == PID_IN) ? PID_OUT : PID_IN;
u32 tok = TD_ACTIVE | ((u32)spid << TD_PID_SHIFT) | TD_TOGGLE;
t_status->token = tok;
t_status->next = QH_TERMINATE;
}
/* QH overlay 指向第一个 TD */
qh->ov_next = (u32)(g_qh_dma.phys + (u64)((u8 *)t_setup - (u8 *)g_qh_dma.virt));
/* 写完 QH/TD 后刷新 CPU cache——DMA 控制器读物理内存需要;
* 缺此步在 WHPX 下 QEMU 读不到 ov_next(竞态),TCG 下侥幸通过。 */
__asm__ volatile("wbinvd" ::: "memory");
ehci_mb();
return 0;
}
/* 构建 bulk 传输单 TD:dir 0=OUT 1=IN。
* in_dma: IN 方向的目标 DMA 缓冲(数据用 g_xfer,CSW 用 g_csw——
* 防止 CSW 读覆盖数据区前 13 字节)。OUT 方向 in_dma 忽略(数据先拷入
* in_dma 指向的缓冲)。 */
static int qh_submit_bulk(ehci_qh *qh, u8 dir, const void *data, u32 data_len,
struct dkm_dma_buffer *in_dma) {
qh->curqtd = 0;
qh->ov_next = QH_TERMINATE;
qh->ov_token = 0;
g_td_next = 0;
ehci_td *td = td_alloc();
if (!td) return -1;
struct dkm_dma_buffer *dst = in_dma ? in_dma : &g_xfer;
u8 *buf = (u8 *)dst->virt;
if (dir == PID_OUT) {
ehci_memcpy(buf, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
td->buf[0] = (u32)(dst->phys & 0xFFFFFFFFu);
u32 tok = TD_ACTIVE | ((u32)dir << TD_PID_SHIFT) | ((u32)data_len << TD_BYTES_SHIFT);
td->token = tok; /* toggle 由 HC 管理(DTC=0) */
qh->ov_next = (u32)(g_qh_dma.phys + (u64)((u8 *)td - (u8 *)g_qh_dma.virt));
__asm__ volatile("wbinvd" ::: "memory");
ehci_mb();
return 0;
}
/* 等待 QH 上的传输完成。轮询首个 TD 的 token(HC 执行后清 ACTIVE、
* 出错置 HALTED)——比检查 overlay 可靠:HC 未执行时 overlay token
* 保持 inactive,轮询 overlay 会假成功返回空数据。first_td 传 TD 池指针,
* 由调用方从 qh->ov_next 反推或直接记录。
* BUG-FIX(缓存一致性):EHCI HC 经 DMA 写 TD 到物理内存,CPU 轮询前
* 必须失效缓存行,否则读到 write-back cache 旧值(Active 恒 1)→ 假超时。
* QEMU 复现证据:first_td token=0x80280 显示 SETUP 已执行成功(Active 清、
* 无错误位),但轮询超时——正是 cache 陈旧。 */
static int qh_wait(ehci_qh *qh, ehci_td *first_td, u32 timeout_ms) {
(void)qh;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
for (;;) {
/* 失效 TD 所在缓存行,强制从内存读 HC 的 DMA 写结果 */
__asm__ volatile("clflush (%0)" : : "r"(first_td) : "memory");
u32 tok = first_td->token;
if (!(tok & TD_ACTIVE)) {
if (tok & TD_HALTED) {
g_log->warn("[ehci] transfer halted");
log_hex("[ehci] token=", tok);
return -2;
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_log->warn("[ehci] transfer timeout");
log_hex("[ehci] first_td token=", tok);
return -1;
}
__asm__ volatile("pause");
}
}
/* 启动 HC:把三个 QH 链成环(ctrl→bulk_out→bulk_in→ctrl),
* 在 ASE=0(async schedule 停止)时写一次 ASYNCLIST,然后 RUN|ASE。
* 之后每次提交只改 QH overlay,不再碰 ASYNCLIST(规范要求该寄存器
* 只能在 async schedule 停止时写——QEMU 会拒绝运行期写入)。 */
static int ehci_hc_start(void) {
u64 ctrl_phys = g_qh_dma.phys + (u64)((u8 *)g_qh_ctrl - (u8 *)g_qh_dma.virt);
u64 out_phys = g_qh_dma.phys + (u64)((u8 *)g_qh_bulk_out - (u8 *)g_qh_dma.virt);
u64 in_phys = g_qh_dma.phys + (u64)((u8 *)g_qh_bulk_in - (u8 *)g_qh_dma.virt);
/* hlink 链接指针必须带类型位 bit1=1(QH 类型)。
* QEMU v11 的 NLPTR_TYPE_GET(x)=((x)>>1)&3,QH 类型=1 → 写物理地址|2。
* BUG-FIX: 裸地址(bit1=0)→ QEMU fetchentry 报 "non queue head
* request in async schedule" 并重置 HC,TD 永不执行。 */
g_qh_ctrl->hlink = (u32)((out_phys & 0xFFFFFFFFu) | 2u);
g_qh_bulk_out->hlink = (u32)((in_phys & 0xFFFFFFFFu) | 2u);
g_qh_bulk_in->hlink = (u32)((ctrl_phys & 0xFFFFFFFFu) | 2u);
/* H 位(bit15) = 回收头标记:QEMU 的 WAITLISTHEAD 状态只在环中找到
* H=1 的 QH 时进入 FETCHENTRY→FETCHQH。不置 H 位 → 状态机永远停在
* WAITLISTHEAD,TD 永不执行。ctrl 作为环头必须置 H。 */
g_qh_ctrl->epchar |= (1u << 15);
ehci_mb();
/* 确保 ASE=0(复位后默认 0) */
u32 cmd = mmio_read32(g_caplen + EHCI_OP_USBCMD);
mmio_write32(g_caplen + EHCI_OP_USBCMD, cmd & ~USBCMD_ASE);
ehci_mb();
mmio_write32(g_caplen + EHCI_OP_ASYNCLIST, (u32)((ctrl_phys & 0xFFFFFFFFu) | 2u));
ehci_mb();
mmio_write32(g_caplen + EHCI_OP_USBCMD,
mmio_read32(g_caplen + EHCI_OP_USBCMD) | USBCMD_ASE | USBCMD_RUN);
ehci_mb();
g_log->info("[ehci] async schedule started");
return 0;
}
/* 简单控制传输封装 */
static int ehci_control(u8 dev_addr, u8 speed, u16 mps0,
const u8 *setup8, u8 *data, u32 data_len, u8 dir) {
qh_init(g_qh_ctrl, dev_addr, 0, speed, mps0, 1);
if (qh_submit_control(g_qh_ctrl, setup8, data, data_len, dir) != 0) return -1;
int rc = qh_wait(g_qh_ctrl, &g_td_pool[0], 5000);
/* IN 方向:失效数据缓冲缓存行后把 HC DMA 写入的数据拷回调用方 */
if (rc == 0 && dir == PID_IN && data && data_len > 0) {
__asm__ volatile("clflush (%0)" : : "r"(g_xfer.virt) : "memory");
__asm__ volatile("clflush 64(%0)" : : "r"(g_xfer.virt) : "memory");
ehci_memcpy(data, (u8 *)g_xfer.virt + 8, data_len);
}
return rc;
}
/* 简单 bulk 传输封装(端点号已含方向编码在 QH)。
* in_dma: IN 方向的目标 DMA 缓冲(g_xfer 或 g_csw)。
* BUG-FIX: CSW 必须用独立缓冲——若与数据共用 g_xfer,13 字节 CSW 会
* 覆盖数据区前 13 字节(bps 字段被清零)。 */
static int ehci_bulk(ehci_qh *qh, u8 dev_addr, u8 speed, u16 mps,
u8 ep_num, u8 dir, const void *data, u32 data_len,
struct dkm_dma_buffer *in_dma) {
qh_init(qh, dev_addr, ep_num, speed, mps, 0);
if (qh_submit_bulk(qh, dir, data, data_len, in_dma) != 0) return -1;
int rc = qh_wait(qh, &g_td_pool[0], 10000);
if (rc == 0 && dir == PID_IN) {
struct dkm_dma_buffer *dst = in_dma ? in_dma : &g_xfer;
/* 失效缓存行后拷贝 */
__asm__ volatile("clflush (%0)" : : "r"(dst->virt) : "memory");
ehci_memcpy((void *)data, dst->virt, data_len);
}
return rc;
}
/* ================================================================
* Phase 3: Port scan and reset
* ================================================================ */
static int ehci_reset_port(u32 port) {
u32 po = g_caplen + EHCI_OP_PORTSC + port * EHCI_PORT_STRIDE;
u32 ps = mmio_read32(po);
if (!(ps & PORTSC_CONNECT)) return -1;
/* 复位序列(EHCI 1.0 §4.2.4 + QEMU 模型兼容):
* 1) 置 PR=1 请求复位
* 2) 保持 PR=1 一小段时间(QEMU 不置 PRC,等待只是给模拟器处理窗口)
* 3) 清 PR —— QEMU 在 PR 1→0 下降沿执行 usb_port_reset 并置 PED(仅高速)
* 4) 轮询 PED 确认使能
* BUG-FIX: 原实现等待 PRC(bit20),但 QEMU 从不置该位 → 200ms 必超时
* 枚举中止(QEMU hcd-ehci.c ehci_port_write 只处理 PR 下降沿)。 */
mmio_write32(po, ps | PORTSC_PR);
dkm_delay_ms(50);
/* 清 PR + 清 change 位(1→0 下降沿触发复位) */
ps = mmio_read32(po);
mmio_write32(po, (ps & ~PORTSC_PR) | PORTSC_RWC);
/* 轮询 PED */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 200;
while (dkm_rdtsc() < deadline) {
ps = mmio_read32(po);
if (ps & PORTSC_PED) {
DKM_STAT_INC(ehci_port_ok);
return 0;
}
if (!(ps & PORTSC_CONNECT)) return -2; /* 复位中断开 */
__asm__ volatile("pause");
}
g_log->warn("[ehci] port not enabled after reset");
return -3;
}
static u8 ehci_port_speed(u32 port) {
u32 po = g_caplen + EHCI_OP_PORTSC + port * EHCI_PORT_STRIDE;
u32 ps = mmio_read32(po);
return (u8)((ps >> PORTSC_LS_SHIFT) & PORTSC_LS_MASK);
}
/* ================================================================
* Phase 4: USB enumeration
* ================================================================ */
/* 前置声明(BOT/SCSI 定义在 enum_msc 之后) */
static int ehci_scsi_inquiry(void);
static int ehci_bot_send_cbw(u8 lun, u8 flags, u32 data_len,
const u8 *cb, u8 cb_len);
static int ehci_bot_receive_csw(void);
static int ehci_get_device_descriptor(u8 *out, u32 len) {
u8 setup[8];
setup[0] = 0x80; setup[1] = 0x06;
setup[2] = 0x00; setup[3] = 0x01; /* Device descriptor */
setup[4] = 0x00; setup[5] = 0x00;
setup[6] = (u8)(len & 0xFF); setup[7] = (u8)((len >> 8) & 0xFF);
ehci_zero(out, len);
/* GET_DESCRIPTOR 阶段 EP0 MPS 未知,按速度用默认值 */
u16 mps0 = 64;
if (g_msc.speed == LS_LOW) mps0 = 8;
return ehci_control(g_msc.dev_addr, g_msc.speed, mps0, setup, out, len, PID_IN);
}
static int ehci_set_address(u8 addr, u16 mps0) {
u8 setup[8];
setup[0] = 0x00; setup[1] = 0x05;
setup[2] = (u8)(addr & 0xFF); setup[3] = 0x00;
setup[4] = 0x00; setup[5] = 0x00;
setup[6] = 0x00; setup[7] = 0x00;
return ehci_control(0, g_msc.speed, mps0, setup, 0, 0, PID_OUT);
}
static int ehci_set_configuration(u8 addr, u8 cfg, u16 mps0) {
u8 setup[8];
setup[0] = 0x00; setup[1] = 0x09;
setup[2] = cfg; setup[3] = 0x00;
setup[4] = 0x00; setup[5] = 0x00;
setup[6] = 0x00; setup[7] = 0x00;
return ehci_control(addr, g_msc.speed, mps0, setup, 0, 0, PID_OUT);
}
static int ehci_get_config_descriptor(u8 addr, u8 *out, u32 len) {
u8 setup[8];
setup[0] = 0x80; setup[1] = 0x06;
setup[2] = 0x00; setup[3] = 0x02; /* Configuration descriptor */
setup[4] = 0x00; setup[5] = 0x00;
setup[6] = (u8)(len & 0xFF); setup[7] = (u8)((len >> 8) & 0xFF);
ehci_zero(out, len);
return ehci_control(addr, g_msc.speed, 64, setup, out, len, PID_IN);
}
/* 枚举 MSC 设备:SET_ADDRESS -> GET_DESCRIPTOR -> SET_CONFIGURATION -> 端点解析 */
static int ehci_enum_msc(u32 port) {
g_msc.speed = ehci_port_speed(port);
log_hex("[ehci] port speed=", g_msc.speed);
u16 mps0 = 64; /* 全速/高速 EP0 默认 64 */
if (g_msc.speed == LS_LOW) mps0 = 8;
/* SET_ADDRESS(用默认 pipe,mps 按速度) */
if (ehci_set_address(1, mps0) != 0) {
g_log->warn("[ehci] SET_ADDRESS failed");
return -1;
}
g_msc.dev_addr = 1;
g_msc.bulk_out_mps = mps0; /* 后续控制传输用已知 mps0 */
/* GET_DESCRIPTOR(device, 18) */
u8 dev_desc[18];
if (ehci_get_device_descriptor(dev_desc, 18) != 0) {
g_log->warn("[ehci] GET_DESCRIPTOR(Device) failed");
return -2;
}
u8 dev_class = dev_desc[4];
u16 vid = (u16)dev_desc[8] | ((u16)dev_desc[9] << 8);
u16 pid = (u16)dev_desc[10] | ((u16)dev_desc[11] << 8);
u8 real_mps0 = dev_desc[7];
log_hex("[ehci] dev class=", dev_class);
log_hex("[ehci] VID=", vid);
log_hex("[ehci] PID=", pid);
log_hex("[ehci] bMaxPacketSize0=", real_mps0);
/* 设备声明的真实 EP0 MPS 与猜测不同时,用真实值 */
if (real_mps0 >= 8 && real_mps0 <= 64) mps0 = real_mps0;
/* SET_CONFIGURATION(1) */
if (ehci_set_configuration(g_msc.dev_addr, 1, mps0) != 0) {
g_log->warn("[ehci] SET_CONFIGURATION failed");
return -3;
}
/* GET_DESCRIPTOR(config, 255) 并解析 MSC 接口 + bulk 端点 */
u8 cfg_desc[255];
if (ehci_get_config_descriptor(g_msc.dev_addr, cfg_desc, 255) != 0) {
g_log->warn("[ehci] GET_DESCRIPTOR(Config) failed");
return -4;
}
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
u8 bulk_out_ep = 0, bulk_in_ep = 0;
u16 bulk_out_mps = 512, bulk_in_mps = 512;
int msc_found = 0;
u32 pos = 9; /* 跳过 config descriptor */
while (pos + 2 <= (u32)total_len && pos + cfg_desc[pos] <= (u32)total_len) {
u8 desc_len = cfg_desc[pos];
u8 desc_type = cfg_desc[pos + 1];
if (desc_type == 4 && desc_len >= 9) {
u8 iface_class = cfg_desc[pos + 5];
u8 iface_sub = cfg_desc[pos + 6];
u8 iface_proto = cfg_desc[pos + 7];
if (iface_class == 0x08 && iface_sub == 0x06 && iface_proto == 0x50) {
msc_found = 1;
g_log->info("[ehci] MSC BOT interface found");
u32 ep_pos = pos + desc_len;
while (ep_pos + 2 <= (u32)total_len) {
u8 ep_len = cfg_desc[ep_pos];
if (ep_len == 0 || ep_pos + ep_len > (u32)total_len) break;
if (cfg_desc[ep_pos + 1] == 5) {
u8 ep_addr = cfg_desc[ep_pos + 2];
u8 ep_attr = cfg_desc[ep_pos + 3];
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
if ((ep_attr & 0x03) == 0x02) {
if (ep_addr & 0x80) {
bulk_in_ep = ep_addr & 0x0F;
bulk_in_mps = ep_mps;
} else {
bulk_out_ep = ep_addr & 0x0F;
bulk_out_mps = ep_mps;
}
}
} else if (cfg_desc[ep_pos + 1] == 4) {
break;
}
ep_pos += ep_len;
}
break;
}
}
pos += desc_len;
}
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
g_log->warn("[ehci] no MSC BOT interface found");
return -5;
}
log_hex("[ehci] Bulk OUT EP=", bulk_out_ep);
log_hex("[ehci] Bulk IN EP=", bulk_in_ep);
log_hex("[ehci] Bulk OUT MPS=", bulk_out_mps);
log_hex("[ehci] Bulk IN MPS=", bulk_in_mps);
g_msc.bulk_out_ep = bulk_out_ep;
g_msc.bulk_in_ep = bulk_in_ep;
g_msc.bulk_out_mps = bulk_out_mps;
g_msc.bulk_in_mps = bulk_in_mps;
g_msc.cbw_tag = 1;
/* INQUIRY 验证设备是块设备 */
if (ehci_scsi_inquiry() != 0) {
g_log->warn("[ehci] INQUIRY failed or not block device");
}
return 0;
}
/* ================================================================
* Phase 5: BOT + SCSI(复用 xhci 已验证的 CBW/CSW/SCSI 组包逻辑)
* ================================================================ */
static int ehci_bot_send_cbw(u8 lun, u8 flags, u32 data_len,
const u8 *cb, u8 cb_len) {
u8 cbw[31];
ehci_zero(cbw, 31);
cbw[0] = 0x55; cbw[1] = 0x53; cbw[2] = 0x42; cbw[3] = 0x43;
u32 tag = g_msc.cbw_tag++;
cbw[4] = (u8)(tag & 0xFF);
cbw[5] = (u8)((tag >> 8) & 0xFF);
cbw[6] = (u8)((tag >> 16) & 0xFF);
cbw[7] = (u8)((tag >> 24) & 0xFF);
cbw[8] = (u8)(data_len & 0xFF);
cbw[9] = (u8)((data_len >> 8) & 0xFF);
cbw[10] = (u8)((data_len >> 16) & 0xFF);
cbw[11] = (u8)((data_len >> 24) & 0xFF);
cbw[12] = flags;
cbw[13] = lun;
cbw[14] = cb_len;
for (u8 i = 0; i < cb_len && i < 16; i++) cbw[15 + i] = cb[i];
return ehci_bulk(g_qh_bulk_out, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_out_mps, g_msc.bulk_out_ep, PID_OUT, cbw, 31, 0);
}
static int ehci_bot_receive_csw(void) {
u8 csw[13];
/* CSW 用独立 DMA 缓冲,避免覆盖数据阶段读入 g_xfer 的数据 */
int rc = ehci_bulk(g_qh_bulk_in, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_in_mps, g_msc.bulk_in_ep, PID_IN, csw, 13, &g_csw);
if (rc != 0) return rc;
if (csw[0] != 0x55 || csw[1] != 0x53 || csw[2] != 0x42 || csw[3] != 0x53) {
g_log->error("[ehci] CSW signature mismatch");
return -2;
}
if (csw[12] != 0) {
log_hex("[ehci] CSW status=", csw[12]);
return -3;
}
return 0;
}
static int ehci_scsi_inquiry(void) {
u8 cb[16];
ehci_zero(cb, 16);
cb[0] = SCSI_INQUIRY;
cb[4] = 36;
if (ehci_bot_send_cbw(0, 0x80, 36, cb, 6) != 0) return -1;
u8 inq[36];
if (ehci_bulk(g_qh_bulk_in, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_in_mps, g_msc.bulk_in_ep, PID_IN, inq, 36, &g_xfer) != 0) return -2;
if (ehci_bot_receive_csw() != 0) return -3;
log_hex("[ehci] INQ PeripheralType=", inq[0] & 0x1F);
return ((inq[0] & 0x1F) == 0) ? 0 : -4;
}
static int ehci_scsi_read_capacity(u64 *out_sectors, u32 *out_blk) {
u8 cb[16];
ehci_zero(cb, 16);
cb[0] = SCSI_READ_CAPACITY10;
if (ehci_bot_send_cbw(0, 0x80, 8, cb, 10) != 0) return -1;
u8 cap[8];
if (ehci_bulk(g_qh_bulk_in, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_in_mps, g_msc.bulk_in_ep, PID_IN, cap, 8, &g_xfer) != 0) return -2;
if (ehci_bot_receive_csw() != 0) return -3;
u32 max_lba = ((u32)cap[0] << 24) | ((u32)cap[1] << 16) | ((u32)cap[2] << 8) | (u32)cap[3];
u32 blk = ((u32)cap[4] << 24) | ((u32)cap[5] << 16) | ((u32)cap[6] << 8) | (u32)cap[7];
*out_sectors = (u64)max_lba + 1;
*out_blk = blk;
log_hex("[ehci] capacity sectors=", *out_sectors);
log_hex("[ehci] block size=", blk);
return 0;
}
static int ehci_scsi_test_unit_ready(void) {
u8 cb[16];
ehci_zero(cb, 16);
cb[0] = SCSI_TEST_UNIT_READY;
if (ehci_bot_send_cbw(0, 0x00, 0, cb, 6) != 0) return -1;
return ehci_bot_receive_csw();
}
static int ehci_scsi_read10(u64 lba, u32 count, void *buffer) {
u8 cb[16];
ehci_zero(cb, 16);
cb[0] = SCSI_READ10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
if (ehci_bot_send_cbw(0, 0x80, data_len, cb, 10) != 0) return -1;
if (ehci_bulk(g_qh_bulk_in, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_in_mps, g_msc.bulk_in_ep, PID_IN, buffer, data_len, &g_xfer) != 0) return -2;
return ehci_bot_receive_csw();
}
static int ehci_scsi_write10(u64 lba, u32 count, const void *buffer) {
u8 cb[16];
ehci_zero(cb, 16);
cb[0] = SCSI_WRITE10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
if (ehci_bot_send_cbw(0, 0x00, data_len, cb, 10) != 0) return -1;
if (ehci_bulk(g_qh_bulk_out, g_msc.dev_addr, g_msc.speed,
g_msc.bulk_out_mps, g_msc.bulk_out_ep, PID_OUT, buffer, data_len, 0) != 0) return -2;
return ehci_bot_receive_csw();
}
/* ================================================================
* Block provider
* ================================================================ */
static int ehci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(ehci_block_reads);
u8 *buf = (u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count;
if (ehci_scsi_read10(lba, chunk, g_xfer.virt) != 0) {
log_hex("[ehci] READ10 failed lba=", lba);
return -1;
}
ehci_memcpy(buf, g_xfer.virt, chunk * 512);
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
static int ehci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(ehci_block_writes);
const u8 *buf = (const u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count;
ehci_memcpy(g_xfer.virt, buf, chunk * 512);
__asm__ volatile("wbinvd" ::: "memory");
if (ehci_scsi_write10(lba, chunk, g_xfer.virt) != 0) {
log_hex("[ehci] WRITE10 failed lba=", lba);
return -1;
}
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
/* ================================================================
* driver_init
* ================================================================ */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_api = api;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[ehci] init begin");
/* PCI 发现 0C/03/20 */
u8 bus = 0, dev = 0, func = 0;
if (ehci_find(&bus, &dev, &func) != 0) {
g_log->info("[ehci] EHCI controller not found");
g_log->info("[ehci] driver ready");
return 0;
}
g_pci_bus = bus; g_pci_dev = dev; g_pci_func = func;
DKM_STAT_INC(ehci_pci_found);
g_log->info("[ehci] EHCI controller found");
log_hex("[ehci] bus=", bus);
log_hex("[ehci] dev=", dev);
log_hex("[ehci] func=", func);
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[ehci] vendor=", vd & 0xffff);
log_hex("[ehci] device=", vd >> 16);
/* 使能 MEM + BUSM */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[ehci] PCI command=", command);
/* BAR0(EHCI 通常是 32 位 MMIO) */
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
u64 bar_phys;
if ((bar0_lo & 0x7) == 0x4) {
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
} else {
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
}
log_hex("[ehci] BAR0 phys=", bar_phys);
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[ehci] missing HHDM or BAR0");
g_log->info("[ehci] driver ready");
return 0;
}
g_mmio = ehci_map_bar(bar_phys);
if (!g_mmio) {
g_log->error("[ehci] BAR MMIO unavailable");
g_log->info("[ehci] driver ready");
return 0;
}
/* 能力寄存器 */
g_caplen = mmio_read32(EHCI_CAP_CAPLENGTH) & 0xFF;
u32 hcsp = mmio_read32(EHCI_CAP_HCSPARAMS);
u32 hccp = mmio_read32(EHCI_CAP_HCCPARAMS);
g_nports = hcsp & 0x0F;
g_64bit = hccp & 1u;
g_ppc = (hcsp >> 4) & 1u;
log_hex("[ehci] CAPLEN=", g_caplen);
log_hex("[ehci] nports=", g_nports);
log_hex("[ehci] 64bit=", g_64bit);
log_hex("[ehci] PPC=", g_ppc);
if (g_nports == 0) {
g_log->error("[ehci] no ports");
g_log->info("[ehci] driver ready");
return 0;
}
/* HC 复位 */
if (ehci_hc_reset() != 0) {
g_log->error("[ehci] HC reset failed");
g_log->info("[ehci] driver ready");
return 0;
}
/* 64 位控制器:CTRLDSSEGMENT = 0 */
if (g_64bit) mmio_write32(g_caplen + 0x10, 0);
/* DMA:异步列表页 + 数据缓冲 */
if (!api->dma) {
g_log->warn("[ehci] DMA API unavailable");
g_log->info("[ehci] driver ready");
return 0;
}
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_qh_dma) != 0) {
g_log->error("[ehci] async list alloc failed");
g_log->info("[ehci] driver ready");
return 0;
}
if (api->dma->alloc_pages(16, 4096, 0x100000000ULL, &g_xfer) != 0) {
g_log->error("[ehci] xfer buf alloc failed");
g_log->info("[ehci] driver ready");
return 0;
}
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_csw) != 0) {
g_log->error("[ehci] csw buf alloc failed");
g_log->info("[ehci] driver ready");
return 0;
}
ehci_zero(g_qh_dma.virt, g_qh_dma.size);
ehci_zero(g_xfer.virt, g_xfer.size);
ehci_zero(g_csw.virt, g_csw.size);
/* QH 布局:必须 32 字节对齐(EHCI 规范)。ctrl@0, out@64, in@128;
* TD 池 @256(TD 32 字节对齐)。原 48 字节间距导致 out/in 未对齐,
* HC 无法处理 QH → TD 永不被执行 → 超时。 */
g_qh_ctrl = (ehci_qh *)((u8 *)g_qh_dma.virt + 0);
g_qh_bulk_out = (ehci_qh *)((u8 *)g_qh_dma.virt + 64);
g_qh_bulk_in = (ehci_qh *)((u8 *)g_qh_dma.virt + 128);
g_td_pool = (ehci_td *)((u8 *)g_qh_dma.virt + 256);
/* 建环并启动 async schedule(必须在 CONFIGFLAG/端口操作前) */
ehci_hc_start();
/* CONFIGFLAG=1(OS 所有端口) */
mmio_write32(g_caplen + EHCI_OP_CONFIGFLAG, 1u);
/* 端口电源 */
for (u32 p = 0; p < g_nports; p++) {
u32 po = g_caplen + EHCI_OP_PORTSC + p * EHCI_PORT_STRIDE;
u32 ps = mmio_read32(po);
if (g_ppc && !(ps & PORTSC_PP)) {
mmio_write32(po, ps | PORTSC_PP);
dkm_delay_ms(20);
}
}
dkm_delay_ms(1500); /* 控制器复位后 U 盘重新连接 */
/* 扫描端口,枚举 MSC。第一遍没有就再等一次。 */
g_msc.ready = 0;
for (int pass = 0; pass < 2 && !g_msc.ready; pass++) {
if (pass == 1) dkm_delay_ms(1500);
for (u32 p = 0; p < g_nports; p++) {
u32 po = g_caplen + EHCI_OP_PORTSC + p * EHCI_PORT_STRIDE;
u32 ps = mmio_read32(po);
log_hex("[ehci] port=", p);
log_hex("[ehci] PORTSC=", ps);
if (!(ps & PORTSC_CONNECT)) continue;
if (ehci_reset_port(p) != 0) {
g_log->warn("[ehci] port reset failed");
continue;
}
dkm_delay_ms(100);
if (ehci_enum_msc(p) != 0) {
g_log->warn("[ehci] MSC enumeration failed");
continue;
}
/* TEST UNIT READY(重试 5 次) */
for (int i = 0; i < 5; i++) {
if (ehci_scsi_test_unit_ready() == 0) break;
dkm_delay_ms(100);
}
/* READ CAPACITY */
u64 sectors = 0;
u32 blk = 512;
if (ehci_scsi_read_capacity(&sectors, &blk) != 0) {
g_log->warn("[ehci] READ CAPACITY failed");
sectors = 0; blk = 512;
}
g_msc.sector_count = sectors;
g_msc.sector_size = blk;
g_msc.ready = 1;
DKM_STAT_INC(ehci_msc_found);
log_hex("[ehci] MSC device ready, sectors=", sectors);
log_hex("[ehci] MSC device block_size=", blk);
break;
}
}
/* 注册 block provider */
if (g_msc.ready && api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "usb2";
desc.sector_size = g_msc.sector_size;
desc.sector_count = g_msc.sector_count;
desc.ctx = 0;
desc.read = ehci_block_read;
desc.write = ehci_block_write;
int index = api->block->register_device(&desc);
log_hex("[ehci] block provider index=", (u64)(i64)index);
} else {
g_log->info("[ehci] no MSC device found; block provider not registered");
}
g_log->info("[ehci] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
if (g_mmio) {
mmio_write32(g_caplen + EHCI_OP_USBCMD,
mmio_read32(g_caplen + EHCI_OP_USBCMD) & ~USBCMD_RUN);
}
return 0;
}
-267
View File
@@ -1,267 +0,0 @@
/* DKM FAT32 Driver — read-only FAT32 filesystem parser
* Stage 2, optional, depends on "vfs" and "block".
* Reads from an embedded FAT32 test image (boot module "dkm:test_fat32").
* Parses BPB, walks FAT, reads root dir, lists files, reads a file.
*/
#include <stdint.h>
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(fat0_reads);
DKM_STAT_DECL(fat0_fat_walks);
/* ---------------------------------------------------------------
* FAT32-specific types (DKM ABI types come from dkm_shared.h)
* --------------------------------------------------------------- */
struct limine_file {
u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56];
};
struct limine_module_response {
u64 revision; u64 module_count; struct limine_file **modules;
};
/* ---------------------------------------------------------------
* FAT32 BPB
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
u8 jmp[3];
char oem[8];
u16 bytes_per_sector;
u8 sectors_per_cluster;
u16 reserved_sector_count;
u8 fat_count;
u16 root_entry_count;
u16 total_sectors_16;
u8 media;
u16 sectors_per_fat_16;
u16 sectors_per_track;
u16 heads;
u32 hidden_sectors;
u32 total_sectors_32;
/* FAT32 extended */
u32 sectors_per_fat;
u16 flags;
u16 version;
u32 root_cluster;
u16 fsinfo_sector;
u16 backup_boot;
u8 reserved[12];
u8 drive;
u8 nt_flags;
u8 signature;
u32 serial;
char label[11];
char type[8];
u8 code[420];
u16 boot_sig;
} fat32_bpb;
typedef struct __attribute__((packed)) {
char name[11];
u8 attr;
u8 nt_reserved;
u8 creation_tenth;
u16 creation_time;
u16 creation_date;
u16 access_date;
u16 cluster_high;
u16 write_time;
u16 write_date;
u16 cluster_low;
u32 file_size;
} fat32_dir_entry;
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_depends[] = { "vfs", "block" };
static const char *const g_provides[] = { "fat32" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="fat32", .version="0.1.0", .vendor="Deshab",
.driver_class=7, .stage=2, .flags=0, .priority=0,
.depends=g_depends, .depends_count=2,
.provides=g_provides, .provides_count=1,
.min_kernel_abi=1,
};
/* ---------------------------------------------------------------
* Helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static u8 g_block_image[65536];
static int fat32_bpb_valid(const u8 *disk, u64 disk_size) {
if (!disk || disk_size < 512) return 0;
const fat32_bpb *bpb = (const fat32_bpb *)disk;
if (bpb->boot_sig != 0xAA55) return 0;
if (bpb->bytes_per_sector != 512) return 0;
if (bpb->sectors_per_cluster == 0) return 0;
if (bpb->reserved_sector_count == 0) return 0;
if (bpb->fat_count == 0) return 0;
if (bpb->sectors_per_fat == 0) return 0;
if (bpb->root_cluster < 2) return 0;
return 1;
}
static void log_hex(const char *p, u64 v) {
static const char h[] = "0123456789abcdef";
char b[19]; u32 i=0; b[i++]='0'; b[i++]='x';
for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf];
b[i]=0; g_log->info(p); g_log->info(b);
}
static u32 read_u32_le(const u8 *p) {
return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24);
}
static u16 read_u16_le(const u8 *p) {
return (u16)p[0]|((u16)p[1]<<8);
}
static int strneq(const char *a, const char *b, int n) {
for (int i=0;i<n;i++) { if (a[i]!=b[i]) return 0; }
return 1;
}
static void log_name11(const char *n) {
char buf[13]; int j=0;
for (int i=0;i<8 && n[i]!=0x20 && n[i];i++) buf[j++]=n[i];
if (n[8]!=0x20 || (n[9]!=0x20 && n[9]!=0)) {
buf[j++]='.';
for (int i=8;i<11 && n[i]!=0x20 && n[i];i++) buf[j++]=n[i];
}
buf[j]=0; g_log->info(buf);
}
static const u8 *fat32_find_boot_module_image(const struct dkm_kernel_api *api, u64 *size_out) {
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
if (!rsp) return NULL;
for (u64 i=0;i<rsp->module_count;i++) {
struct limine_file *f=rsp->modules[i];
if (f&&f->cmdline&&strneq(f->cmdline,"dkm:test_fat32",13)) {
if (size_out) *size_out=f->size;
return (const u8*)f->address;
}
}
return NULL;
}
static const u8 *fat32_select_disk_image(const struct dkm_kernel_api *api, u64 *size_out) {
if (api->block && api->block->device_count && api->block->read && api->block->sector_size) {
u32 count = api->block->device_count();
log_hex("[fat32] block devices=", count);
if (count > 0 && api->block->sector_size(0) == 512) {
const char *name = api->block->device_name ? api->block->device_name(0) : NULL;
g_log->info("[fat32] trying block provider");
if (name) g_log->info(name);
int st = api->block->read(0, 0, 128, g_block_image);
log_hex("[fat32] block read status=", (u64)(i64)st);
if (st == 0 && fat32_bpb_valid(g_block_image, sizeof(g_block_image))) {
g_log->info("[fat32] using block provider image");
if (size_out) *size_out = sizeof(g_block_image);
return g_block_image;
}
g_log->warn("[fat32] block provider image is not FAT32; fallback");
}
}
u64 module_size = 0;
const u8 *module = fat32_find_boot_module_image(api, &module_size);
if (module) {
g_log->info("[fat32] using boot module image");
if (size_out) *size_out = module_size;
return module;
}
return NULL;
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
u64 disk_size = 0;
const u8 *disk = fat32_select_disk_image(api, &disk_size);
if (!disk) { g_log->warn("[fat32] no FAT32 image source"); return 0; }
if (!fat32_bpb_valid(disk, disk_size)) { g_log->warn("[fat32] invalid FAT32 BPB"); return 0; }
g_log->info("[fat32] parsing FAT32 image");
const fat32_bpb *bpb=(const fat32_bpb*)disk;
u32 bps=bpb->bytes_per_sector;
u32 spc=bpb->sectors_per_cluster;
u32 rsc=bpb->reserved_sector_count;
u32 spf=bpb->sectors_per_fat;
u32 root_clus=bpb->root_cluster;
log_hex("[fat32] bytes/sec=",bps);
log_hex("[fat32] sec/cluster=",spc);
log_hex("[fat32] fat start sec=",rsc);
log_hex("[fat32] sec/fat=",spf);
log_hex("[fat32] root cluster=",root_clus);
/* read root directory */
u32 cluster_size = bps * spc;
u32 fat_start = rsc * bps;
u32 data_start = fat_start + spf * bps;
g_log->info("[fat32] root dir entries:");
u32 clus = root_clus;
while (clus >= 2 && clus < 0x0FFFFFF8) {
u32 clus_off = data_start + (clus - 2) * cluster_size;
const fat32_dir_entry *dir = (const fat32_dir_entry *)(disk + clus_off);
for (u32 e=0; e*32 < cluster_size; e++) {
if (dir[e].name[0]==0) break;
if ((u8)dir[e].name[0]==0xE5) continue;
if (dir[e].attr==0x0F) continue; /* skip LFN */
if (dir[e].attr & 0x08) continue; /* skip volume label */
g_log->info("[fat32] ");
log_name11(dir[e].name);
log_hex("[fat32] size=",dir[e].file_size);
/* try to read README.TXT content */
if (strneq(dir[e].name,"README TXT",11)) {
DKM_STAT_INC(fat0_reads);
u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low);
u32 f_off = data_start + (f_clus - 2)*cluster_size;
u32 f_size = dir[e].file_size;
g_log->info("[fat32] --- README.TXT content ---");
u32 n = f_size < 128 ? f_size : 128;
char line[80]; u32 lp=0;
for (u32 k=0;k<n;k++) {
u8 c = disk[f_off+k];
line[lp++]=c;
if (c=='\n'||lp>=78||k==n-1) { line[lp]=0; g_log->info("[fat32] "); g_log->info(line); lp=0; }
}
g_log->info("[fat32] --- end ---");
}
}
/* read next cluster from FAT */
u32 fat_ent_off = fat_start + clus * 4;
clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF;
DKM_STAT_INC(fat0_fat_walks);
}
g_log->info("[fat32] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle; return 0;
}
-721
View File
@@ -1,721 +0,0 @@
/* DKM mt7920 — MediaTek MT7920 / Wi-Fi 6 (PCI 14c3:7920), ConnAC2 PCIe
*
* 本机无线网卡。驱动分阶段落地,当前阶段 = MCU 固件下载(patch + RAM),
* 到 "N9 firmware ready" 为止。扫描/关联仍为显式 panic(MT7920-E10/E11),
* 不伪装成功。
*
* 固件通过 Limine boot module 送入(limine.conf fw:mt7920_patch / fw:mt7920_ram),
* 驱动用 boot_modules_response 按 cmdline 定位,不嵌进 .drv。
*
* 寄存器/流程对拍 Linux mt76(drivers/net/wireless/mediatek/mt76),
* 版本见 .build_tmp/mt76ref。全程串口日志,便于实机逐步 bring-up。
*
* 这是硬件 bring-up 第一阶段:本环境(QEMU 无此卡)无法验证,需实机串口迭代。
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
/* ---- limine boot module 结构(与 bootfs.drv 一致) ---- */
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
static const char *const g_depends[] = {"pci"};
static const char *const g_provides[] = {"netdev"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="mt7920",.version="0.2.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=1,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
};
#define MT_VID 0x14C3u
#define MT_DID 0x7920u
/* ===== 寄存器(对拍 mt792x_regs.h / mt7921/regs.h,物理地址) ===== */
#define MT_HW_CHIPID 0x70010200u
#define MT_HW_REV 0x70010204u
#define MT_HW_BOUND 0x70010020u
#define MT_TOP_LPCR_HOST_BAND0 0x18060010u
#define MT_TOP_LPCR_HOST_FW_OWN (1u<<0)
#define MT_TOP_LPCR_HOST_DRV_OWN (1u<<1)
#define MT_WFSYS_RST 0x18000140u
#define WFSYS_SW_RST_B (1u<<0)
#define WFSYS_SW_INIT_DONE (1u<<4)
/* conn_host_csr_top 0x7c060000 → BAR0 0xe0000 */
#define MT_CONN_ON_LPCTL 0x0e0010u
#define PCIE_LPCR_HOST_SET_OWN (1u<<0)
#define PCIE_LPCR_HOST_CLR_OWN (1u<<1)
#define PCIE_LPCR_HOST_OWN_SYNC (1u<<2)
#define MT_CONN_ON_MISC 0x0e00f0u
#define MT_TOP_MISC_FW_STATE 0x7u /* GENMASK(2,0) */
#define MT_TOP_MISC2_FW_PWR_ON (1u<<0)
#define MT_TOP_MISC2_FW_N9_RDY 0x3u /* GENMASK(1,0) */
/* WFDMA0 直接 BAR0 偏移(base 0xd4000) */
#define MT_WFDMA0(ofs) (0xd4000u + (ofs))
#define MT_WFDMA0_RST MT_WFDMA0(0x100)
#define MT_WFDMA0_RST_LOGIC_RST (1u<<4)
#define MT_WFDMA0_RST_DMASHDL_ALL_RST (1u<<5)
#define MT_MCU_CMD MT_WFDMA0(0x1f0)
#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208)
#define MT_WFDMA0_GLO_CFG_TX_DMA_EN (1u<<0)
#define MT_WFDMA0_GLO_CFG_TX_DMA_BUSY (1u<<1)
#define MT_WFDMA0_GLO_CFG_RX_DMA_EN (1u<<2)
#define MT_WFDMA0_GLO_CFG_RX_DMA_BUSY (1u<<3)
#define MT_WFDMA0_GLO_CFG_TX_WB_DDONE (1u<<6)
#define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN (1u<<12)
#define MT_WFDMA0_GLO_CFG_RX_WB_DDONE (1u<<13)
#define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN (1u<<15)
#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO (1u<<28)
#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 (1u<<21)
#define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS (1u<<30)
#define MT_WFDMA0_GLO_CFG_DMA_SIZE_SHIFT 4
#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c)
#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0)
#define MT_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0)
#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN (1u<<6)
#define MT_DMASHDL_SW_CONTROL (0x7c026004u) /* via fixed map below */
#define MT_DMASHDL_DMASHDL_BYPASS (1u<<28)
#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204)
#define MT_WFDMA0_HOST_INT_STA MT_WFDMA0(0x200)
#define MT_MCU2HOST_SW_INT_ENA MT_WFDMA0(0x1f4)
#define MT_WFDMA_DUMMY_CR (0x54000120u)
#define MT_WFDMA_NEED_REINIT (1u<<1)
#define MT_TX_RING_BASE MT_WFDMA0(0x300)
#define MT_RX_EVENT_RING_BASE MT_WFDMA0(0x500)
#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x520)
#define MT_WFDMA0_TX_RING0_EXT_CTRL MT_WFDMA0(0x600)
#define MT_RING_SIZE 0x10u
#define MT_PCIE_MAC_INT_ENABLE 0x010188u /* PCIE_MAC_IREG 0x74030000→0x10000 +0x188 */
/* L1 remap(高位地址窗口),MT_INFRA 0xfe000 */
#define MT_HIF_REMAP_L1 0x0fe24cu
#define MT_HIF_REMAP_BASE_L1 0x40000u
/* ConnAC2 队列索引 */
#define TXQ_FWDL 16
#define TXQ_MCU_WM 17
#define RXQ_MCU_WM 0
/* MCU 命令 */
#define MCU_CMD_TARGET_ADDRESS_LEN_REQ 0x01
#define MCU_CMD_FW_START_REQ 0x02
#define MCU_CMD_NIC_POWER_CTRL 0x04
#define MCU_CMD_PATCH_START_REQ 0x05
#define MCU_CMD_PATCH_FINISH_REQ 0x07
#define MCU_CMD_PATCH_SEM_CONTROL 0x10
#define MCU_CMD_FW_SCATTER 0xee
#define PATCH_SEM_RELEASE 0
#define PATCH_SEM_GET 1
#define PATCH_NOT_DL_SEM_SUCCESS 2
#define PATCH_IS_DL 3
#define PATCH_REL_SEM_SUCCESS 4
#define DL_MODE_NEED_RSP (1u<<31)
#define FW_START_OVERRIDE (1u<<0)
#define FW_FEATURE_OVERRIDE_ADDR (1u<<5)
#define FW_FEATURE_NON_DL (1u<<6)
#define PATCH_SEC_TYPE_MASK 0xffffu
#define PATCH_SEC_TYPE_INFO 0x2
#define MCU_PKT_ID 0xa0u
#define FWDL_MAX_LEN 4096
/* ===== 固件头结构(big-endian patch header,little-endian ram trailer) ===== */
struct patch_hdr {
char build_date[16];
char platform[4];
u32 hw_sw_ver; /* BE */
u32 patch_ver; /* BE */
u16 checksum;
u16 rsv;
struct {
u32 patch_ver;
u32 subsys;
u32 feature;
u32 n_region; /* BE */
u32 crc;
u32 rsv[11];
} desc;
};
struct patch_sec {
u32 type; /* BE */
u32 offs; /* BE */
u32 size; /* BE */
u32 addr; /* BE (info.addr) */
u32 len; /* BE (info.len) */
u32 sec_key_idx;
u32 align_len;
u32 rsv[9];
};
struct fw_trailer {
u8 chip_id;
u8 eco_code;
u8 n_region;
u8 format_ver;
u8 format_flag;
u8 rsv[2];
char fw_ver[10];
char build_date[15];
u32 crc;
};
struct fw_region {
u32 decomp_crc;
u32 decomp_len;
u32 decomp_blk_sz;
u8 rsv[4];
u32 addr; /* LE */
u32 len; /* LE */
u8 feature_set;
u8 type;
u8 rsv1[14];
};
/* mt76 descriptor */
struct mt_desc { u32 buf0; u32 ctrl; u32 buf1; u32 info; };
#define MT_DMA_CTL_SD_LEN0_SHIFT 16
#define MT_DMA_CTL_LAST_SEC0 (1u<<30)
#define MT_DMA_CTL_DMA_DONE (1u<<31)
/* ===== 驱动状态 ===== */
static struct dkm_log_api *g_log;
static const struct dkm_kernel_api *g_api;
static volatile u8 *g_bar; /* BAR0 虚拟基址 */
static u8 g_bus, g_dev, g_func;
static u8 g_mcu_seq;
struct mt_queue {
struct mt_desc *desc; /* 虚拟 */
u64 desc_phys;
u32 ndesc;
u32 head;
u32 ring_base; /* 寄存器基 */
u32 hw_idx; /* ring index within base */
};
static struct mt_queue g_q_fwdl, g_q_wm, g_q_rxmcu;
static struct dkm_dma_buffer g_tx_scratch; /* TX 命令/scatter 暂存 */
static struct dkm_dma_buffer g_rx_bufs; /* RX buffer 池 */
#define RX_NDESC 8u
#define TX_SCRATCH_SZ 8192u
/* ===== 串口日志 ===== */
#define COM1 0x3F8
static void rlog(const char *s){ while(*s){char c=*s++; if(c=='\n'){dkm_serial_wait_tx(COM1);dkm_outb(COM1,'\r');} dkm_serial_wait_tx(COM1);dkm_outb(COM1,(u8)c);} }
static void rhex(const char *p, u64 v){ static const char hx[]="0123456789abcdef"; char b[19]; rlog(p); b[0]='0';b[1]='x'; for(int i=0;i<16;i++)b[2+i]=hx[(v>>((15-i)*4))&0xf]; b[18]=0; rlog(b); rlog("\n"); }
/* strict 模式 g_log->panic 不返回;fallback 模式返回,调用点随后 return -1 */
static void mt_panic(const char *sym){ if(g_log)g_log->panic(sym); else for(;;)__asm__ volatile("cli;hlt"); }
/* ===== 基础 MMIO ===== */
static inline u32 bar_rr(u32 off){ return *(volatile u32*)(g_bar+off); }
static inline void bar_wr(u32 off,u32 v){ *(volatile u32*)(g_bar+off)=v; }
static inline void bar_set(u32 off,u32 m){ bar_wr(off, bar_rr(off)|m); }
static inline void bar_clr(u32 off,u32 m){ bar_wr(off, bar_rr(off)&~m); }
/* 固定映射:把高位物理地址翻成 BAR0 偏移(对拍 __mt7921_reg_addr 子集) */
static u32 fixed_map(u32 addr){
struct { u32 phys, maps, size; } m[] = {
{0x54000000,0x02000,0x01000},
{0x55000000,0x03000,0x01000},
{0x7c000000,0xf0000,0x10000},
{0x7c020000,0xd0000,0x10000},
{0x7c060000,0xe0000,0x10000},
{0x74030000,0x10000,0x10000},
};
if(addr<0x100000) return addr;
for(u32 i=0;i<sizeof(m)/sizeof(m[0]);i++){
if(addr<m[i].phys) continue;
u32 ofs=addr-m[i].phys;
if(ofs>m[i].size) continue;
return m[i].maps+ofs;
}
return 0; /* 需 L1 remap */
}
/* L1 remap 访问高位寄存器(0x18xxxxxx / 0x70xxxxxx 等) */
static u32 l1_addr(u32 addr){
u32 base=addr>>16, offset=addr&0xffff;
u32 cur=bar_rr(MT_HIF_REMAP_L1);
bar_wr(MT_HIF_REMAP_L1,(cur&~0xffffu)|(base&0xffffu));
(void)bar_rr(MT_HIF_REMAP_L1);
return MT_HIF_REMAP_BASE_L1+offset;
}
static u32 reg_rr(u32 phys){ u32 o=fixed_map(phys); if(!o)o=l1_addr(phys); return bar_rr(o); }
static void reg_wr(u32 phys,u32 v){ u32 o=fixed_map(phys); if(!o)o=l1_addr(phys); bar_wr(o,v); }
static void reg_set(u32 phys,u32 m){ reg_wr(phys,reg_rr(phys)|m); }
static void reg_clr(u32 phys,u32 m){ reg_wr(phys,reg_rr(phys)&~m); }
static int poll_reg(u32 phys,u32 mask,u32 val,u32 ms){
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
do { if((reg_rr(phys)&mask)==val) return 0; __asm__ volatile("pause"); } while(dkm_rdtsc()<dl);
return (reg_rr(phys)&mask)==val?0:-1;
}
static int poll_bar(u32 off,u32 mask,u32 val,u32 ms){
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
do { if((bar_rr(off)&mask)==val) return 0; __asm__ volatile("pause"); } while(dkm_rdtsc()<dl);
return (bar_rr(off)&mask)==val?0:-1;
}
/* ===== PCI ===== */
#define PCI_ADDR_PORT 0xCF8u
#define PCI_DATA_PORT 0xCFCu
static inline void outl_(u16 p,u32 v){__asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p));}
static inline u32 inl_(u16 p){u32 v;__asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p));return v;}
static u32 pci_rd(u8 b,u8 d,u8 f,u8 r){ outl_(PCI_ADDR_PORT,(1u<<31)|((u32)b<<16)|((u32)d<<11)|((u32)f<<8)|(r&0xFC)); return inl_(PCI_DATA_PORT); }
static void pci_wr(u8 b,u8 d,u8 f,u8 r,u32 v){ outl_(PCI_ADDR_PORT,(1u<<31)|((u32)b<<16)|((u32)d<<11)|((u32)f<<8)|(r&0xFC)); outl_(PCI_DATA_PORT,v); }
static int mt_find(void){
for(u32 b=0;b<256;b++) for(u8 d=0;d<32;d++){
u32 vd=pci_rd((u8)b,d,0,0);
if((vd&0xffff)==0xffff) continue;
if((u16)(vd&0xffff)==MT_VID && (u16)(vd>>16)==MT_DID){ g_bus=(u8)b; g_dev=d; g_func=0; return 0; }
}
return -1;
}
/* ===== boot module 固件定位 ===== */
static int fw_find(const char *tag, const u8 **data, u64 *size){
const struct limine_module_response *rsp=(const struct limine_module_response*)g_api->boot_modules_response;
if(!rsp) return -1;
for(u64 i=0;i<rsp->module_count;i++){
struct limine_file *f=rsp->modules[i];
if(!f||!f->cmdline) continue;
const char *a=f->cmdline,*b=tag;
while(*a&&*a==*b){a++;b++;}
if(*a==0&&*b==0){ *data=(const u8*)f->address; *size=f->size; return 0; }
}
return -1;
}
static u32 be32(const u8 *p){ return ((u32)p[0]<<24)|((u32)p[1]<<16)|((u32)p[2]<<8)|p[3]; }
static u32 le32(const u8 *p){ return ((u32)p[3]<<24)|((u32)p[2]<<16)|((u32)p[1]<<8)|p[0]; }
static void memcpy_(void *d,const void *s,u64 n){ u8 *dd=d; const u8 *ss=s; while(n--)*dd++=*ss++; }
static void memset_(void *d,u8 v,u64 n){ u8 *dd=d; while(n--)*dd++=v; }
/* ===== DMA 队列 ===== */
static int q_alloc(struct mt_queue *q,u32 ndesc,u32 ring_base,u32 hw_idx){
struct dkm_dma_buffer buf;
u64 bytes=(u64)ndesc*sizeof(struct mt_desc);
u64 pages=(bytes+4095)/4096;
if(g_api->dma->alloc_pages(pages,4096,0xFFFFFFFFull,&buf)!=0) return -1;
memset_(buf.virt,0,pages*4096);
q->desc=(struct mt_desc*)buf.virt;
q->desc_phys=buf.phys;
q->ndesc=ndesc;
q->head=0;
q->ring_base=ring_base;
q->hw_idx=hw_idx;
return 0;
}
/* 把队列的 desc_base/ring_size 写入硬件 ring 寄存器,并清 cpu/dma idx */
static void q_sync(struct mt_queue *q){
u32 r=q->ring_base+q->hw_idx*MT_RING_SIZE;
bar_wr(r+0x00,(u32)q->desc_phys); /* desc_base */
bar_wr(r+0x04,q->ndesc); /* ring_size */
bar_wr(r+0x08,0); /* cpu_idx */
bar_wr(r+0x0c,0); /* dma_idx (hw) */
q->head=0;
}
static void q_kick(struct mt_queue *q){
__asm__ volatile("sfence":::"memory");
bar_wr(q->ring_base+q->hw_idx*MT_RING_SIZE+0x08,q->head);
}
/* 把一段数据丢进 TX 队列(单段),info=0 */
static int tx_raw(struct mt_queue *q,u64 phys,u32 len){
u32 idx=q->head;
if((q->head+1)%q->ndesc==0 && q->ndesc<=1) return -1;
struct mt_desc *d=&q->desc[idx];
u32 ctrl=((len&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT)|MT_DMA_CTL_LAST_SEC0;
d->buf0=(u32)phys;
d->buf1=0;
d->info=0;
__asm__ volatile("sfence":::"memory");
d->ctrl=ctrl;
q->head=(q->head+1)%q->ndesc;
q_kick(q);
return 0;
}
/* 等待 RX MCU ring 出现一个响应,拷到 out(最多 cap),返回长度;超时 -1 */
static int rx_wait(u8 *out,u32 cap,u32 ms){
u32 r=g_q_rxmcu.ring_base+g_q_rxmcu.hw_idx*MT_RING_SIZE;
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
u32 tail=g_q_rxmcu.head;
do {
u32 dma_idx=bar_rr(r+0x0c);
if(dma_idx!=tail){
struct mt_desc *d=&g_q_rxmcu.desc[tail];
u32 ctrl=d->ctrl;
u32 len=(ctrl>>MT_DMA_CTL_SD_LEN0_SHIFT)&0x3fff;
const u8 *src=(const u8*)(g_api->hhdm_offset + d->buf0);
u32 n=len<cap?len:cap;
memcpy_(out,src,n);
/* 回收:重新武装该 desc(重填 buffer 长度) */
d->ctrl=((2048u&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT);
tail=(tail+1)%g_q_rxmcu.ndesc;
g_q_rxmcu.head=tail;
bar_wr(r+0x08,tail); /* cpu_idx 前进,归还 buffer */
return (int)n;
}
__asm__ volatile("pause");
} while(dkm_rdtsc()<dl);
return -1;
}
/* 发一条标准 MCU 命令(带 64 字节 txd 头),payload 在 g_tx_scratch 之后;
* wait=1 时等响应并返回首字节(PATCH_SEM / PATCH_FINISH 用)。 */
static int mcu_send(int cmd_id,int ext_id,const void *payload,u32 plen,int wait,u8 *rbyte){
u8 *buf=(u8*)g_tx_scratch.virt;
u32 hdr=64;
memset_(buf,0,hdr);
u32 total=hdr+plen;
if(total>TX_SCRATCH_SZ) return -2;
memcpy_(buf+hdr,payload,plen);
u8 seq=++g_mcu_seq & 0xf; if(!seq) seq=++g_mcu_seq & 0xf;
u32 *txd=(u32*)buf;
/* txd[0]: TX_BYTES(total) | PKT_FMT(CMD=2)<<23 | Q_IDX(0x20)<<25 */
txd[0]=(total&0xffff)|(2u<<23)|(0x20u<<25);
/* txd[1]: LONG_FORMAT bit31 | HDR_FORMAT(CMD=1)<<16 */
txd[1]=(1u<<31)|(1u<<16);
/* mcu_txd 覆盖在 offset 32 */
u8 *m=buf+32;
u16 mlen=(u16)(total-32);
m[0]=(u8)(mlen&0xff); m[1]=(u8)(mlen>>8); /* len */
u16 pq=(u16)((1u<<15)|(0x20u<<10));
m[2]=(u8)(pq&0xff); m[3]=(u8)(pq>>8); /* pq_id */
m[4]=(u8)cmd_id; /* cid */
m[5]=MCU_PKT_ID; /* pkt_type */
m[6]=ext_id?2:0; /* set_query: SET=2 for ext */
m[7]=seq; /* seq */
m[9]=(u8)ext_id; /* ext_cid */
m[10]=0; /* s2d_index=H2N */
m[11]=ext_id?1:0; /* ext_cid_ack */
if(tx_raw(&g_q_wm,g_tx_scratch.phys,total)!=0) return -3;
if(!wait) { dkm_delay_ms(2); return 0; }
static u8 rbuf[2048];
int n=rx_wait(rbuf,sizeof(rbuf),1000);
if(n<0) return -4;
/* rxd 头 36 字节;seq 在 offset 33(rxd[6]=24, len+pkt=4 →28, eid=28,seq=29) */
if(n>=34){
u8 rseq=rbuf[29];
if(rseq!=seq) { /* 不匹配也不致命,返回 0 继续 */ }
if(rbyte) *rbyte=rbuf[32]; /* PATCH_SEM/FINISH: sizeof(rxd)-4 = 32 */
}
return 0;
}
/* FW_SCATTER:无头,走 FWDL 队列,分片 4096 */
static int fw_scatter(const u8 *data,u32 len){
u8 *scr=(u8*)g_tx_scratch.virt;
while(len>0){
u32 cur=len<FWDL_MAX_LEN?len:FWDL_MAX_LEN;
memcpy_(scr,data,cur);
__asm__ volatile("sfence":::"memory");
if(tx_raw(&g_q_fwdl,g_tx_scratch.phys,cur)!=0) return -1;
/* 等 fwdl dma_idx 追上(简化:轮询 dma_idx==head) */
u32 r=g_q_fwdl.ring_base+g_q_fwdl.hw_idx*MT_RING_SIZE;
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*200;
while(bar_rr(r+0x0c)!=g_q_fwdl.head && dkm_rdtsc()<dl) __asm__ volatile("pause");
data+=cur; len-=cur;
}
return 0;
}
static int mcu_init_download(u32 addr,u32 len,u32 mode){
struct { u32 addr,len,mode; } req={addr,len,mode};
int cmd = (addr==0x900000)?MCU_CMD_PATCH_START_REQ:MCU_CMD_TARGET_ADDRESS_LEN_REQ;
return mcu_send(cmd,0,&req,sizeof(req),1,0);
}
/* ===== driver own / wfsys reset / dma ===== */
static int drv_own(void){
for(int i=0;i<10;i++){
reg_wr(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_CLR_OWN);
if(poll_reg(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_OWN_SYNC,0,50)==0) return 0;
}
return -1;
}
static int fw_own(void){
for(int i=0;i<10;i++){
reg_wr(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_SET_OWN);
if(poll_reg(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_OWN_SYNC,PCIE_LPCR_HOST_OWN_SYNC,50)==0) return 0;
}
return -1;
}
static int top_drv_own(void){
reg_wr(MT_TOP_LPCR_HOST_BAND0,MT_TOP_LPCR_HOST_DRV_OWN);
return poll_reg(MT_TOP_LPCR_HOST_BAND0,MT_TOP_LPCR_HOST_FW_OWN,0,500);
}
static int wfsys_reset(void){
reg_clr(MT_WFSYS_RST,WFSYS_SW_RST_B);
dkm_delay_ms(50);
reg_set(MT_WFSYS_RST,WFSYS_SW_RST_B);
return poll_reg(MT_WFSYS_RST,WFSYS_SW_INIT_DONE,WFSYS_SW_INIT_DONE,500);
}
static int dma_disable(int force){
bar_clr(MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN|MT_WFDMA0_GLO_CFG_RX_DMA_EN|
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN|
MT_WFDMA0_GLO_CFG_OMIT_TX_INFO|MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
if(poll_bar(MT_WFDMA0_GLO_CFG,MT_WFDMA0_GLO_CFG_TX_DMA_BUSY|MT_WFDMA0_GLO_CFG_RX_DMA_BUSY,0,100)!=0)
return -1;
bar_clr(MT_WFDMA0_GLO_CFG_EXT0,MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN);
reg_set(MT_DMASHDL_SW_CONTROL,MT_DMASHDL_DMASHDL_BYPASS);
if(force){
bar_clr(MT_WFDMA0_RST,MT_WFDMA0_RST_DMASHDL_ALL_RST|MT_WFDMA0_RST_LOGIC_RST);
bar_set(MT_WFDMA0_RST,MT_WFDMA0_RST_DMASHDL_ALL_RST|MT_WFDMA0_RST_LOGIC_RST);
}
return 0;
}
static int dma_enable(void){
bar_wr(MT_WFDMA0_RST_DTX_PTR,~0u);
bar_wr(MT_WFDMA0_PRI_DLY_INT_CFG0,0);
bar_set(MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_WB_DDONE|MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN|
MT_WFDMA0_GLO_CFG_CLK_GAT_DIS|MT_WFDMA0_GLO_CFG_OMIT_TX_INFO|
(3u<<MT_WFDMA0_GLO_CFG_DMA_SIZE_SHIFT)|MT_WFDMA0_GLO_CFG_RX_WB_DDONE|
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN|MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
bar_set(MT_WFDMA0_GLO_CFG,MT_WFDMA0_GLO_CFG_TX_DMA_EN|MT_WFDMA0_GLO_CFG_RX_DMA_EN);
bar_set(MT_WFDMA_DUMMY_CR,MT_WFDMA_NEED_REINIT);
return 0;
}
/* RX MCU ring 填充 buffer */
static int rx_fill(void){
u64 pages=(RX_NDESC*2048+4095)/4096;
if(g_api->dma->alloc_pages(pages,4096,0xFFFFFFFFull,&g_rx_bufs)!=0) return -1;
memset_(g_rx_bufs.virt,0,pages*4096);
for(u32 i=0;i<RX_NDESC;i++){
struct mt_desc *d=&g_q_rxmcu.desc[i];
u64 bp=g_rx_bufs.phys+(u64)i*2048;
d->buf0=(u32)bp;
d->buf1=0;
d->info=0;
d->ctrl=((2048u&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT);
}
__asm__ volatile("sfence":::"memory");
/* cpu_idx 指到最后,表示全部 buffer 可用 */
u32 r=g_q_rxmcu.ring_base+g_q_rxmcu.hw_idx*MT_RING_SIZE;
bar_wr(r+0x08,RX_NDESC-1);
g_q_rxmcu.head=0;
return 0;
}
static int dma_init(void){
if(dma_disable(1)!=0){ rlog("[mt7920] dma_disable timeout\n"); }
if(q_alloc(&g_q_wm,256,MT_TX_RING_BASE,TXQ_MCU_WM)!=0) return -1;
if(q_alloc(&g_q_fwdl,128,MT_TX_RING_BASE,TXQ_FWDL)!=0) return -1;
if(q_alloc(&g_q_rxmcu,RX_NDESC,MT_RX_EVENT_RING_BASE,RXQ_MCU_WM)!=0) return -1;
q_sync(&g_q_wm);
q_sync(&g_q_fwdl);
q_sync(&g_q_rxmcu);
if(rx_fill()!=0) return -1;
dma_enable();
return 0;
}
/* ===== 固件下载 ===== */
static int load_patch(const u8 *fw,u64 fwsz){
u8 sem=0;
struct { u32 op; } semreq;
semreq.op=PATCH_SEM_GET;
mcu_send(MCU_CMD_PATCH_SEM_CONTROL,0,&semreq,sizeof(semreq),1,&sem);
rhex("[mt7920] patch sem=",sem);
if(sem==PATCH_IS_DL) return 0;
if(sem!=PATCH_NOT_DL_SEM_SUCCESS){ rlog("[mt7920] patch sem fail\n"); }
if(fwsz<sizeof(struct patch_hdr)){ mt_panic("MT7920-E20 PATCH FW TOO SMALL"); return -1; }
const struct patch_hdr *h=(const struct patch_hdr*)fw;
u32 nreg=be32((const u8*)&h->desc.n_region);
rhex("[mt7920] patch regions=",nreg);
int ret=0;
for(u32 i=0;i<nreg;i++){
const struct patch_sec *s=(const struct patch_sec*)(fw+sizeof(struct patch_hdr)+i*sizeof(struct patch_sec));
if((be32((const u8*)&s->type)&PATCH_SEC_TYPE_MASK)!=PATCH_SEC_TYPE_INFO){ mt_panic("MT7920-E21 PATCH SEC TYPE"); ret=-1; break; }
u32 addr=be32((const u8*)&s->addr);
u32 len=be32((const u8*)&s->len);
u32 offs=be32((const u8*)&s->offs);
u32 mode=DL_MODE_NEED_RSP; /* plain,MT7920 patch 非加密段 */
rhex("[mt7920] patch addr=",addr);
rhex("[mt7920] patch len=",len);
if(mcu_init_download(addr,len,mode)!=0){ rlog("[mt7920] init_download fail\n"); ret=-1; break; }
if(fw_scatter(fw+offs,len)!=0){ rlog("[mt7920] scatter fail\n"); ret=-1; break; }
}
/* PATCH_FINISH */
struct { u8 check_crc; u8 rsv[3]; } fin; memset_(&fin,0,sizeof(fin));
mcu_send(MCU_CMD_PATCH_FINISH_REQ,0,&fin,sizeof(fin),1,0);
/* release sem */
semreq.op=PATCH_SEM_RELEASE;
mcu_send(MCU_CMD_PATCH_SEM_CONTROL,0,&semreq,sizeof(semreq),1,&sem);
return ret;
}
static int load_ram(const u8 *fw,u64 fwsz){
if(fwsz<sizeof(struct fw_trailer)){ mt_panic("MT7920-E22 RAM FW TOO SMALL"); return -1; }
const struct fw_trailer *tr=(const struct fw_trailer*)(fw+fwsz-sizeof(struct fw_trailer));
u8 nreg=tr->n_region;
rhex("[mt7920] ram regions=",nreg);
u32 offset=0, override=0, option=0;
for(u8 i=0;i<nreg;i++){
const struct fw_region *rg=(const struct fw_region*)((const u8*)tr-((u32)(nreg-i))*sizeof(struct fw_region));
u32 len=le32((const u8*)&rg->len);
u32 addr=le32((const u8*)&rg->addr);
u32 mode=DL_MODE_NEED_RSP;
if(rg->feature_set&FW_FEATURE_NON_DL){ offset+=len; continue; }
if(rg->feature_set&FW_FEATURE_OVERRIDE_ADDR) override=addr;
rhex("[mt7920] ram addr=",addr);
rhex("[mt7920] ram len=",len);
if(mcu_init_download(addr,len,mode)!=0){ rlog("[mt7920] ram init_download fail\n"); return -1; }
if(fw_scatter(fw+offset,len)!=0){ rlog("[mt7920] ram scatter fail\n"); return -1; }
offset+=len;
}
if(override) option|=FW_START_OVERRIDE;
struct { u32 option,addr; } sreq={option,override};
mcu_send(MCU_CMD_FW_START_REQ,0,&sreq,sizeof(sreq),1,0);
return 0;
}
static int load_firmware(void){
const u8 *patch,*ram; u64 psz,rsz;
if(fw_find("fw:mt7920_patch",&patch,&psz)!=0){ mt_panic("MT7920-E23 PATCH FW MODULE MISSING"); return -1; }
if(fw_find("fw:mt7920_ram",&ram,&rsz)!=0){ mt_panic("MT7920-E24 RAM FW MODULE MISSING"); return -1; }
rhex("[mt7920] patch fw size=",psz);
rhex("[mt7920] ram fw size=",rsz);
/* NIC_POWER_CTRL restart + 等 FW_PWR_ON */
struct { u8 pm; u8 rsv[3]; } pr; memset_(&pr,0,sizeof(pr)); pr.pm=1;
mcu_send(MCU_CMD_NIC_POWER_CTRL,0,&pr,sizeof(pr),0,0);
if(poll_reg(MT_CONN_ON_MISC,MT_TOP_MISC_FW_STATE,MT_TOP_MISC2_FW_PWR_ON,1000)!=0)
rlog("[mt7920] WARN MCU not ready for fw dl\n");
if(load_patch(patch,psz)!=0){ mt_panic("MT7920-E25 PATCH DOWNLOAD FAILED"); return -1; }
rlog("[mt7920] patch downloaded\n");
if(load_ram(ram,rsz)!=0){ mt_panic("MT7920-E26 RAM DOWNLOAD FAILED"); return -1; }
rlog("[mt7920] ram downloaded\n");
if(poll_reg(MT_CONN_ON_MISC,MT_TOP_MISC2_FW_N9_RDY,MT_TOP_MISC2_FW_N9_RDY,1500)!=0){
mt_panic("MT7920-E27 FIRMWARE N9 NOT READY");
return -1;
}
rlog("[mt7920] N9 firmware ready\n");
return 0;
}
/* ===== netdev 回调(扫描/关联未实现,显式 panic) ===== */
static int mt_scan_start(void *c){(void)c;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return -1;}
static int mt_scan_count(void *c){(void)c;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return 0;}
static int mt_scan_result(void *c,u32 n,struct dkm_net_scan_result *o){(void)c;(void)n;(void)o;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return -1;}
static int mt_connect(void *c,const char *s,const char *p){(void)c;(void)s;(void)p;mt_panic("MT7920-E11 WLAN CONNECT NOT IMPLEMENTED");return -1;}
static int mt_disconnect(void *c){(void)c;mt_panic("MT7920-E12 WLAN DISCONNECT NOT IMPLEMENTED");return -1;}
static int mt_is_wireless(void *c){(void)c;return 1;}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,struct dkm_driver_handle *handle){
(void)handle;
if(!api||!api->log) return -1;
dkm_tsc_calibrate();
g_api=api; g_log=(struct dkm_log_api*)api->log;
dkm_instr_init(api);
rlog("[mt7920] driver_init entered\n");
if(mt_find()!=0){ rlog("[mt7920] 14c3:7920 not found\n"); return 0; }
rhex("[mt7920] b:d:f=",((u64)g_bus<<16)|((u64)g_dev<<8)|g_func);
u32 cmd=pci_rd(g_bus,g_dev,g_func,0x04);
cmd|=(1u<<1)|(1u<<2);
pci_wr(g_bus,g_dev,g_func,0x04,cmd);
u32 bar0=pci_rd(g_bus,g_dev,g_func,0x10);
if(bar0&1u){ mt_panic("MT7920-E02 BAR0 IS IO"); return -1; }
u64 phys=(u64)(bar0&0xFFFFFFF0u);
if((bar0&0x6u)==0x4u) phys|=((u64)pci_rd(g_bus,g_dev,g_func,0x14)<<32);
rhex("[mt7920] BAR0 phys=",phys);
if(!phys){ mt_panic("MT7920-E02 BAR0 MISSING"); return -1; }
if(!api->mm_map_mmio){ mt_panic("MT7920-E02 MMIO MAP API MISSING"); return -1; }
void *va=api->mm_map_mmio(phys,0x200000); /* 2MB BAR0 窗口 */
if(!va){ mt_panic("MT7920-E02 MMIO MAP FAILED"); return -1; }
g_bar=(volatile u8*)va;
/* 先让固件 own,再取 driver own(对拍 probe 顺序) */
fw_own();
if(drv_own()!=0){ mt_panic("MT7920-E03 DRIVER OWN FAILED"); g_bar=0; return -1; }
rlog("[mt7920] driver own ok\n");
u32 chip=reg_rr(MT_HW_CHIPID);
u32 rev=reg_rr(MT_HW_REV);
rhex("[mt7920] chipid=",chip);
rhex("[mt7920] rev=",rev);
if(wfsys_reset()!=0){ rlog("[mt7920] WARN wfsys reset timeout\n"); }
else rlog("[mt7920] wfsys reset done\n");
bar_wr(MT_WFDMA0_HOST_INT_ENA,0);
bar_wr(MT_PCIE_MAC_INT_ENABLE,0xff);
if(dma_init()!=0){ mt_panic("MT7920-E04 DMA INIT FAILED"); g_bar=0; return -1; }
rlog("[mt7920] dma init ok\n");
/* TX scratch */
if(api->dma->alloc_pages((TX_SCRATCH_SZ+4095)/4096,4096,0xFFFFFFFFull,&g_tx_scratch)!=0){
mt_panic("MT7920-E05 TX SCRATCH ALLOC FAILED");
g_bar=0; return -1;
}
/* MCU own via TOP LPCR,再下载固件 */
if(top_drv_own()!=0){ rlog("[mt7920] WARN top drv own timeout\n"); }
if(load_firmware()!=0){ mt_panic("MT7920-E06 FIRMWARE LOAD FAILED"); g_bar=0; return -1; }
/* 注册 netdev(link 需后续 phy/连接阶段才 up) */
if(api->net&&api->net->register_device){
struct dkm_net_device_desc nd; memset_(&nd,0,sizeof(nd));
nd.name="wlan0";
nd.flags=(1u<<3);
nd.scan_start=mt_scan_start;
nd.scan_count=mt_scan_count;
nd.scan_result=mt_scan_result;
nd.is_wireless=mt_is_wireless;
nd.connect=mt_connect;
nd.disconnect=mt_disconnect;
int idx=api->net->register_device(&nd);
rhex("[mt7920] netdev index=",(u64)(i32)idx);
}
rlog("[mt7920] init done (firmware running; scan/connect pending)\n");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle){ (void)handle; g_bar=0; return 0; }
-1641
View File
@@ -1,1641 +0,0 @@
/* DKM NVMe Driver — 高位 BAR MMIO 映射 + admin/IO queue + block provider
*
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
*
* 实现要点:
* 1. 64-bit BAR 常位于 4G 以上(QEMU/OVMF 把 NVMe BAR0 放到 ~48GiB),
* HHDM 直映射不保证覆盖。访问前先用 kernel_api.mmio->is_mapped 做
* 四级页表 walk 核实;未映射则 map_mmio 原地补建 4KiB UC 页。
* 2. 完成模型: 单 outstanding 命令 + CQE phase bit。B7 阶段3 起 IO 队列
* 默认尝试 MSI/MSI-X 中断驱动(FUCK [drivers] nvme_msi, 默认 1 自动):
* 先 MSI cap 编程, 缺则 MSI-X (QEMU nvme 只暴露 MSI-X) — 表项
* addr=0xFEE00000|dest<<12, data=动态向量, entry unmask + cap Enable;
* Create IOCQ IEN=1/IV=0 + INTMC 解除 IV0; handler 摘 CQE 进完成槽、
* 推进 head、写 CQ doorbell, 自写 LAPIC EOI 并返回 1 (MSI 直投 LAPIC,
* 与 apic_route 开关无关, 不依赖阶段2 的 EOI 钩子注册状态)。
* 提交侧统一等待: 先查完成槽(中断路径), 再在 irqsave 临界区内手动
* 摘取(自适应轮询路径) — IF=0 (DSK 阶段) 或 MSI 未投递时零感知降级,
* 行为与旧版纯轮询一致。计数: g_msi_irq_count / g_msi_slot_hits /
* g_msi_poll_harvests / g_msi_timeouts, init 末尾与压测后打点。
* 3. 数据面: 32 页 (128KiB) 物理连续 DMA bounce buffer + 单页 PRP list。
* PRP 布局按传输页数分档: 1 页单 PRP1; 2 页 PRP2 直连第二页;
* >=3 页 PRP2 指向 PRP list 页 (512 项 u64, 上限 513 页 = 2052KiB)。
* chunk 上限 = min(buffer 页数, 2^MDTS 页, NLB 16-bit)。
* 读写一律 bounce: 调用方缓冲是任意虚拟地址 (DSK BSS / vmm dbuf),
* 物理底账未知, 统一经 DMA 缓冲中转, 一次命令读满 chunk 再 memcpy。
* WRITE 路径 memcpy 后 wbinvd (沿用 ahci BUG-P0-2 防陈旧 cache 先例),
* 每次写调用末尾随一个 FLUSH。admin/IO queue 各 64 项, 均每队列一页。
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(nvm0_pci_found);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "nvme",
.version = "0.4.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
.flags = 0, /* optional */
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---- NVMe 控制器寄存器 ---- */
#define NVME_CAP 0x00 /* u64 capabilities */
#define NVME_VS 0x08 /* u32 version */
#define NVME_INTMS 0x0C /* interrupt mask set */
#define NVME_INTMC 0x10 /* interrupt mask clear */
#define NVME_CC 0x14 /* controller configuration */
#define NVME_CSTS 0x1C /* controller status */
#define NVME_AQA 0x24 /* admin queue attributes */
#define NVME_ASQ 0x28 /* u64 admin SQ base */
#define NVME_ACQ 0x30 /* u64 admin CQ base */
#define NVME_DB_BASE 0x1000 /* doorbell 起始偏移 */
#define NVME_CLASS_STORAGE 0x01
#define NVME_SUBCLASS_NVM 0x08
#define NVME_PROGIF_NVME 0x02
/* admin 命令 opcode */
#define NVME_ADM_DELETE_IOSQ 0x00
#define NVME_ADM_CREATE_IOSQ 0x01
#define NVME_ADM_DELETE_IOCQ 0x04
#define NVME_ADM_CREATE_IOCQ 0x05
#define NVME_ADM_IDENTIFY 0x06
/* IO 命令 opcode(NVM 命令集: 00h=Flush, 01h=Write, 02h=Read) */
#define NVME_IO_FLUSH 0x00
#define NVME_IO_READ 0x02
#define NVME_IO_WRITE 0x01
#define ADMIN_QSIZE 64 /* entry 数(SQ 64*64B=4K 一页, CQ 64*16B=1K) */
#define IO_QSIZE 64
#define NVME_IDENT_BYTES 4096 /* identify 数据页 */
#define NVME_DATA_PAGES 32u /* IO bounce: 128KiB = 512B×256 扇区,
* 正好覆盖 DSK FAT32 的 256 扇区批量读 */
#define NVME_PRP_LIST_ENTRIES 512u /* 单 list 页 4KiB / 8B = 512 项 */
static const struct dkm_log_api *g_log;
static volatile u8 *g_regs; /* MMIO 基址(虚拟) */
static u32 g_db_stride; /* doorbell 步长 = 4 << CAP.DSTRD */
static u64 g_timeout_ms; /* CAP.TO * 500ms */
static struct dkm_dma_buffer g_asq; /* admin SQ */
static struct dkm_dma_buffer g_acq; /* admin CQ */
static struct dkm_dma_buffer g_ident; /* identify 数据页 */
static struct dkm_dma_buffer g_data; /* IO 读写 bounce (NVME_DATA_PAGES 页连续) */
static struct dkm_dma_buffer g_prp; /* PRP list 页 */
static struct dkm_dma_buffer g_iosq; /* IO SQ (qid=1) */
static struct dkm_dma_buffer g_iocq; /* IO CQ (qid=1) */
static u32 g_asq_tail;
static u32 g_acq_head;
static u32 g_acq_phase;
static u32 g_iosq_tail;
static u32 g_iocq_head;
static u32 g_iocq_phase;
static u16 g_cid;
static u64 g_sector_size;
static u64 g_sector_count;
static u32 g_mdts; /* Identify Controller byte 77 */
static u32 g_chunk_max_sectors; /* 单命令扇区上限 (buffer/MDTS/NLB 取小) */
static int g_io_ready;
static int g_test_disk; /* LBA0 带 DESHABNVME0 签名的测试盘 */
/* ---- B7 阶段3: MSI 状态 ---- */
static int g_cfg_nvme_msi = 1; /* FUCK [drivers] nvme_msi, 默认 1 自动尝试 */
static int g_msi_enabled; /* MSI/MSI-X 编程成功且 handler 已注册 */
static int g_msix_mode; /* 1=MSI-X 表项模式, 0=MSI cap 模式 */
static int g_msi_vector = -1; /* 动态分配的 IDT 向量 (0x40-0xDF 池) */
/* 完成槽: 单 outstanding 前提下, handler 是唯一写者、提交侧是唯一读者,
* 且下一命令提交前不可能再有新 CQE, 故读-清无需互斥; valid 最后写/最先读,
* x86 TSO store-store / load-load 天然有序, nvme_mb 只挡编译器重排。 */
static volatile u32 g_slot_valid;
static volatile u32 g_slot_status; /* sc | (sct << 8), 0 = 成功 */
static volatile u32 g_slot_dw0; /* CQE dw0 (命令特定返回) */
static volatile u32 g_slot_cid; /* CQE dw3 低 16 位 CID, 提交侧比对防错位 */
static u64 g_msi_irq_count; /* MSI handler 进入次数 (投递证明) */
static u64 g_msi_slot_hits; /* 提交侧经完成槽完成的命令数 (中断路径) */
static u64 g_msi_poll_harvests; /* 提交侧 irqsave 手动摘取数 (轮询路径) */
static u64 g_msi_timeouts; /* 完成超时计数 (真故障, 非降级) */
static u64 g_msi_cid_mismatch; /* 摘到 CID 与提交 cid 不符的 CQE 计数 (队列错位警报) */
/* ---- 基础工具 ---- */
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* 打印有界 ASCII 串(identify 数据中的 SN/MN 等定长字段) */
static void log_str(const char *prefix, const u8 *src, u32 len) {
char buf[48];
if (len > 40) len = 40;
for (u32 i = 0; i < len; i++) {
char c = (char)src[i];
buf[i] = (c >= 32 && c < 127) ? c : ' ';
}
buf[len] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void nvme_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static __inline__ void nvme_mb(void) {
/* x86 TSO 保证 store-store 顺序, 这里只需阻断编译器重排 */
__asm__ volatile("" ::: "memory");
}
/* 十进制打印 (MSI 计数器打点用, 比 hex 直观) */
static void log_dec(const char *prefix, u64 val) {
char buf[24];
u32 pos = 0;
if (!val) {
buf[pos++] = '0';
} else {
char tmp[20];
u32 n = 0;
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
while (n) buf[pos++] = tmp[--n];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static int memeq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
if (a[i] != b[i]) return 0;
}
return 1;
}
static u32 str_len(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 在 NUL 结尾字符串中查找子串 (boot module cmdline 匹配用) */
static int str_contains(const char *hay, const char *needle) {
u32 nlen = str_len(needle);
if (!nlen) return 1;
for (u32 i = 0; hay[i]; i++) {
u32 j = 0;
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
if (j == nlen) return 1;
}
return 0;
}
static void nvme_cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
__asm__ volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(leaf), "c"(0));
}
/* irqsave 临界区: 提交侧手动摘取 CQE 时屏蔽中断, 与 MSI handler 互斥。
* pushf/popf 配对恢复 IF — DSK 阶段 IF=0, 绝不能无条件 sti。 */
static __inline__ u64 nvme_irqsave(void) {
u64 flags;
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
return flags;
}
static __inline__ void nvme_irqrestore(u64 flags) {
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory", "cc");
}
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_regs + off);
}
static void mmio_write32(u32 off, u32 value) {
*(volatile u32 *)(void *)(g_regs + off) = value;
}
static u64 mmio_read64(u32 off) {
u32 lo = mmio_read32(off);
u32 hi = mmio_read32(off + 4);
return ((u64)hi << 32) | lo;
}
static void mmio_write64(u32 off, u64 value) {
mmio_write32(off, (u32)(value & 0xffffffffu));
mmio_write32(off + 4, (u32)(value >> 32));
}
static u32 sq_tail_db(u16 qid) { return NVME_DB_BASE + (2u * qid) * g_db_stride; }
static u32 cq_head_db(u16 qid) { return NVME_DB_BASE + (2u * qid + 1u) * g_db_stride; }
/* ---- PCI ---- */
static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read(bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == NVME_CLASS_STORAGE &&
subclass == NVME_SUBCLASS_NVM &&
prog_if == NVME_PROGIF_NVME) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
return 0;
}
}
}
}
return -1;
}
/* ---- FUCK 配置自解析 (与 apic.c 同一范式, 不依赖 UTSM ini_parser) ----
* UTSM 经 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
* 此处只找 [drivers] 分区下的 nvme_msi 键; 缺失文件/键一律默认 1
* (自动尝试 MSI, 失败回轮询), 与无配置时行为一致。
*/
/* Limine boot module 最小类型 (与 apic.c / bootfs.c / UTSM limine.h 布局一致) */
struct limine_file_min {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response_min {
u64 revision;
u64 module_count;
struct limine_file_min **modules;
};
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
u64 i = start;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
u64 tok_start = i;
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
u64 tok_len = i - tok_start;
const char *tok = line + tok_start;
if (!tok_len) return defval;
if (tok_len == 1 && tok[0] == '1') return 1;
if (tok_len == 1 && tok[0] == '0') return 0;
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
int neg = 0;
u64 j = 0;
if (tok[0] == '-') { neg = 1; j = 1; }
int val = 0;
int any = 0;
for (; j < tok_len; j++) {
if (tok[j] < '0' || tok[j] > '9') return defval;
val = val * 10 + (tok[j] - '0');
any = 1;
}
if (!any) return defval;
return neg ? -val : val;
}
static int fuck_get_int(const char *text, u64 size,
const char *section, const char *key, int defval) {
u64 pos = 0;
int in_section = 0;
u32 slen = str_len(section);
u32 klen = str_len(key);
while (pos < size) {
u64 end = pos;
while (end < size && text[end] != '\n') end++;
u64 line_len = end - pos;
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
const char *line = text + pos;
u64 i = 0;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
if (i < line_len) {
char c = line[i];
if (c == '#' || c == ';') {
/* 注释行, 跳过 */
} else if (c == '[') {
in_section = 0;
if (line_len >= (u64)(i + 1 + slen + 1) &&
memeq(line + i + 1, section, slen) &&
line[i + 1 + slen] == ']') {
in_section = 1;
}
} else if (in_section) {
if (line_len >= (u64)(i + klen) &&
memeq(line + i, key, klen)) {
u64 j = i + klen;
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
if (j < line_len && line[j] == '=') {
return parse_scalar(line, j + 1, line_len, defval);
}
}
}
}
pos = end + 1;
}
return defval;
}
static void nvme_load_config(const struct dkm_kernel_api *api) {
/* 编译期默认: 自动尝试 MSI (任何前提缺失都会回退纯轮询, 零回归) */
g_cfg_nvme_msi = 1;
const struct limine_module_response_min *resp =
(const struct limine_module_response_min *)api->boot_modules_response;
if (!resp || !resp->modules) {
g_log->warn("[nvme] no boot modules; FUCK defaults in effect");
return;
}
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file_min *f = resp->modules[i];
if (!f || !f->cmdline || !f->address || !f->size) continue;
if (!str_contains(f->cmdline, "fuck:config")) continue;
g_cfg_nvme_msi = fuck_get_int((const char *)f->address, f->size,
"drivers", "nvme_msi", 1);
g_log->info("[nvme] FUCK config module found");
log_dec("[nvme] cfg nvme_msi=", (u64)g_cfg_nvme_msi);
return;
}
g_log->warn("[nvme] FUCK module (fuck:config) not found; defaults in effect");
}
/* ---- CSTS 等待 ---- */
static int nvme_wait_csts(u32 mask, u32 expect, u64 timeout_ms) {
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
for (;;) {
u32 csts = mmio_read32(NVME_CSTS);
if (csts & (1u << 1)) { /* CFS: controller fatal status */
g_log->error("[nvme] CSTS.CFS set");
log_hex("[nvme] CSTS=", csts);
return -2;
}
if ((csts & mask) == expect) return 0;
if (dkm_rdtsc() > deadline) {
g_log->error("[nvme] CSTS wait timeout");
log_hex("[nvme] CSTS=", csts);
log_hex("[nvme] wait mask=", mask);
return -1;
}
__asm__ volatile("pause");
}
}
/* 前向声明: nvme_dump_regs 在 nvme_submit 中使用 */
static void nvme_dump_regs(const char *ctx);
/* ---- B7 阶段3: IO CQ 完成摘取 (MSI handler 与提交侧轮询共用) ----
* 返回 1 = 摘到一枚 CQE; 0 = 无新完成。
* out_cid 返回 CQE dw3 低 16 位的命令 ID, 供调用方与提交 cid 比对 —
* 单 outstanding 模型下 CQ 任何时刻只应有当前命令的一枚 CQE, CID 不匹配
* 即队列状态错位的硬证据, 必须显式报错而非静默当作当前命令完成
* (route=0 曾观测 stress readback MISMATCH @0x9000, 疑 CQE 提前消费)。
* 并发模型: 单生产者(控制器 DMA 写 CQ) + 单逻辑消费者, 消费者有两个入口 ——
* 1. MSI handler (IRQ 上下文): 同一 vector 在 EOI 前不会被 LAPIC 再次
* 投递, handler 对同向量天然不可重入, 无需自锁;
* 2. 提交侧统一等待 (主上下文): 必须在 nvme_irqsave 临界区内调用,
* 屏蔽中断期间 handler 不可能插入, 消除 "phase 判定后、head 推进前
* 被 handler 抢摘同一 CQE" 的双消费竞态。
* head 推进 + CQ doorbell 只在此函数内发生, 是两个入口的唯一汇合点。
*/
static int nvme_iocq_harvest(u32 *out_status, u32 *out_dw0, u32 *out_cid) {
volatile u32 *cq = (volatile u32 *)g_iocq.virt;
u32 dw3 = cq[g_iocq_head * 4 + 3];
if (((dw3 >> 16) & 1u) != g_iocq_phase) return 0;
if (out_dw0) *out_dw0 = cq[g_iocq_head * 4 + 0];
if (out_cid) *out_cid = dw3 & 0xffffu;
u32 sc = (dw3 >> 17) & 0xffu;
u32 sct = (dw3 >> 25) & 0x7u;
/* SC 与 SCT 联合编码 (与 nvme_submit 经典路径同一约定):
* SCT!=0 且 SC==0 时只返回 sc 会被误判成功。 */
*out_status = sc | (sct << 8);
g_iocq_head = (g_iocq_head + 1) % IO_QSIZE;
if (g_iocq_head == 0) g_iocq_phase ^= 1u;
nvme_mb(); /* head 先于 doorbell 可见 */
mmio_write32(cq_head_db(1), g_iocq_head);
return 1;
}
/* MSI 中断 handler: 摘 CQE 进完成槽。
* 返回 1 = 自负 LAPIC EOI (抑制 idt_handler 的钩子补 EOI)。
* 为何自写 EOI 而不依赖钩子: MSI 是设备向 LAPIC 直投的消息中断,
* 投递路径不经过 IOAPIC/PIC, 与 apic_route 开关无关; 但 EOI 钩子只在
* route=1 时注册, route=0 下钩子为 NULL 会漏写 EOI — LAPIC ISR 位
* 残留将永久阻塞同优先级后续中断, MSI 静默退化为 one-shot。
* handler 内直写 LAPIC EOI 寄存器后返回 1, 两种路由模式下行为一致。
* LAPIC 未软件使能时该写为无害 no-op (此时 MSI 本就不会投递)。
* 地址取 x86 默认 LAPIC base 0xFEE00000 + EOI 偏移 0xB0 (QEMU/MADT
* 一致; SAS-R0 恒等映射覆盖 4G 以下, apic.drv 已实证可直访)。
* 时序说明: 即使提交侧轮询抢先摘走 CQE, 控制器中断信号早已发出,
* 本次 handler 仍会执行并计入 g_msi_irq_count — 投递证据不受竞态影响。 */
static int nvme_msi_handler(u8 vector) {
(void)vector;
g_msi_irq_count++;
u32 st = 0, dw0 = 0, cid = 0;
if (nvme_iocq_harvest(&st, &dw0, &cid)) {
g_slot_status = st;
g_slot_dw0 = dw0;
g_slot_cid = cid;
nvme_mb(); /* payload 先于 valid 对提交侧可见 */
g_slot_valid = 1;
}
*(volatile u32 *)(uintptr_t)0xFEE000B0u = 0; /* LAPIC EOI */
return 1;
}
/* IO 命令统一完成等待 (MSI 模式): 完成槽(中断路径)优先, irqsave 手动摘取
* (自适应轮询路径)兜底。MSI 健康时 handler 在 CQE 落盘瞬间填槽; MSI 未投递
* (LAPIC 未启用/IF=0/信号丢失) 时手动摘取沿原 phase 语义直接完成, 调用方
* 零感知。超时语义与经典路径一致 (CAP.TO*500ms*10, 下限 5s)。 */
static int nvme_io_wait_completion(u16 cid, u32 opcode, u32 *out_cqe_dw0) {
u64 submit_timeout_ms = g_timeout_ms * 10;
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
for (;;) {
/* 中断路径: handler 已摘取 CQE 进完成槽 */
if (g_slot_valid) {
u32 st = g_slot_status;
u32 dw0 = g_slot_dw0;
u32 scid = g_slot_cid;
nvme_mb();
g_slot_valid = 0;
g_msi_slot_hits++;
if (scid != (u32)cid) {
/* CID 错位: 槽内 CQE 不属于本命令 — 队列状态已损,
* 显式报错并继续等真 CQE (超时兜底), 绝不静默当成功 */
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (slot)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", scid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed (msi slot)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
/* 自适应轮询路径: irqsave 临界区内手动摘取 (与 handler 互斥) */
u32 st = 0, dw0 = 0, hcid = 0;
u64 flags = nvme_irqsave();
int got = nvme_iocq_harvest(&st, &dw0, &hcid);
nvme_irqrestore(flags);
if (got) {
g_msi_poll_harvests++;
if (hcid != (u32)cid) {
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (poll)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", hcid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_msi_timeouts++;
g_log->error("[nvme] completion timeout (msi mode)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
nvme_dump_regs("completion timeout");
return -100;
}
__asm__ volatile("pause");
}
}
/* ---- 命令提交(同步轮询, 单 outstanding) ----
* dw[16] 为完整 SQE(dw0 低 16 位不含 CID, 由本函数填入)。
* 返回 0 成功;>0 = NVMe status code;<0 超时/内部错误。
*/
static int nvme_submit(volatile u32 *sq, u32 *sq_tail, u16 sq_qid,
volatile u32 *cq, u32 *cq_head, u32 *cq_phase,
u16 cq_qid, u32 qsize, u32 *dw, u32 *out_cqe_dw0) {
dw[0] = (dw[0] & 0xffffu) | ((u32)g_cid << 16);
u32 cid = g_cid;
g_cid++;
/* CID=0 保留不用: QEMU 在 MSI-X unmask 边界会向 IO CQ 补投一枚
* CID=0/phase 正确/status 成功的幽灵 CQE (实测稳定复现), g_cid 回绕
* 跳过 0 可保证该幽灵 CQE 永远被 CID 校验拒绝, 绝无误判窗口。 */
if (g_cid == 0) g_cid = 1;
/* 逐 dword 写入 SQE(64B),避免 freestanding 下编译器生成 memcpy 调用 */
u32 pos = *sq_tail;
for (u32 i = 0; i < 16; i++) sq[pos * 16 + i] = dw[i];
*sq_tail = (pos + 1) % qsize;
nvme_mb(); /* SQ 内容先于 doorbell 可见 */
mmio_write32(sq_tail_db(sq_qid), *sq_tail);
/* B7 阶段3: IO 队列 MSI 模式 → 完成槽 + irqsave 摘取统一等待。
* admin 队列保持纯轮询 (仅 init 期使用, 且 INTMC 解除前 IV0 处于
* 屏蔽态, admin 完成不会触发 MSI)。 */
if (cq_qid == 1 && g_msi_enabled) {
return nvme_io_wait_completion(cid, dw[0] & 0xffu, out_cqe_dw0);
}
/* 真机超时: 使用 CAP.TO * 500ms * 10 (留 10 倍余量), 下限 5s。
* 真机 NVMe 控制器在重负载下可能需要更长的完成时间。 */
u64 submit_timeout_ms = g_timeout_ms * 10;
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
for (;;) {
u32 dw3 = cq[*cq_head * 4 + 3];
if (((dw3 >> 16) & 1u) == *cq_phase) {
u32 sc = (dw3 >> 17) & 0xffu;
u32 sct = (dw3 >> 25) & 0x7u;
if (out_cqe_dw0) *out_cqe_dw0 = cq[*cq_head * 4 + 0];
*cq_head = (*cq_head + 1) % qsize;
if (*cq_head == 0) *cq_phase ^= 1u;
nvme_mb();
mmio_write32(cq_head_db(cq_qid), *cq_head);
if (sc || sct) {
g_log->error("[nvme] command failed");
log_hex("[nvme] opcode=", dw[0] & 0xffu);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] SC=", sc);
log_hex("[nvme] SCT=", sct);
/* SC 与 SCT 必须联合编码: SCT!=0 且 SC==0 时只返回 sc
* 会返回 0 被调用方误判为成功(CQID_INVALID 正是此类)。 */
return (int)(sc | (sct << 8));
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_log->error("[nvme] completion timeout");
log_hex("[nvme] opcode=", dw[0] & 0xffu);
nvme_dump_regs("completion timeout");
return -100;
}
__asm__ volatile("pause");
}
}
static int nvme_admin_submit(u32 *dw, u32 *out_cqe_dw0) {
return nvme_submit((volatile u32 *)g_asq.virt, &g_asq_tail, 0,
(volatile u32 *)g_acq.virt, &g_acq_head, &g_acq_phase,
0, ADMIN_QSIZE, dw, out_cqe_dw0);
}
static int nvme_io_submit(u32 *dw, u32 *out_cqe_dw0) {
return nvme_submit((volatile u32 *)g_iosq.virt, &g_iosq_tail, 1,
(volatile u32 *)g_iocq.virt, &g_iocq_head, &g_iocq_phase,
1, IO_QSIZE, dw, out_cqe_dw0);
}
/* ---- Identify ---- */
static int nvme_identify_controller(u32 *out_nn) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
dw[0] = NVME_ADM_IDENTIFY;
dw[1] = 0; /* NSID=0 for controller */
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
dw[7] = (u32)(g_ident.phys >> 32);
dw[10] = 1; /* CNS=1: identify controller */
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) return rc;
u8 *id = (u8 *)g_ident.virt;
log_str("[nvme] ctrl SN=", id + 4, 20);
log_str("[nvme] ctrl MN=", id + 24, 40);
log_str("[nvme] ctrl FR=", id + 64, 8);
/* Identify Controller 布局锚点核对(NVMe 1.4, CNS=1 数据结构):
* byte 77 MDTS u8 最大传输 = 2^MDTS × CAP.MPSMIN 页, 0=无限制
* byte 512 SQES/CQES/MAXCMD —— QEMU 实测 0x00004466, 证明偏移基正确
* byte 516 NN u32 Number of Namespaces
* NN 核实结论 (QEMU 11.0 串口实证): NN=0x100 不是解析错位。NN 的 spec
* 语义是"控制器支持的最大合法 NSID 数"而非已挂载数量; QEMU hw/nvme
* 填的是 NVME_MAX_NAMESPACES=256 (0x100)。实际挂载的 NSID=1 由后续
* Identify Namespace(CNS=0) 成功返回确认。解析偏移 516 与 spec 一致,
* 无需修正。 */
u32 anchor512 = (u32)id[512] | ((u32)id[513] << 8) |
((u32)id[514] << 16) | ((u32)id[515] << 24);
log_hex("[nvme] ID dword@512(SQES/CQES/MAXCMD)=", anchor512);
u32 nn = (u32)id[516] | ((u32)id[517] << 8) | ((u32)id[518] << 16) | ((u32)id[519] << 24);
log_hex("[nvme] NN(max valid NSID)=", nn);
log_hex("[nvme] MDTS=", id[77]);
g_mdts = id[77];
*out_nn = nn;
return 0;
}
static int nvme_identify_namespace(u32 nsid, u64 *out_nsze, u32 *out_lbads) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
dw[0] = NVME_ADM_IDENTIFY;
dw[1] = nsid;
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
dw[7] = (u32)(g_ident.phys >> 32);
dw[10] = 0; /* CNS=0: identify namespace */
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) return rc;
u8 *id = (u8 *)g_ident.virt;
u64 nsze = 0;
for (u32 i = 0; i < 8; i++) nsze |= ((u64)id[i]) << (i * 8);
u8 flbas = id[26] & 0x0f;
u32 lbaf_off = 128 + (u32)flbas * 4;
u32 lbaf = (u32)id[lbaf_off] | ((u32)id[lbaf_off + 1] << 8) |
((u32)id[lbaf_off + 2] << 16) | ((u32)id[lbaf_off + 3] << 24);
u32 lbads = (lbaf >> 16) & 0xff;
log_hex("[nvme] NSZE=", nsze);
log_hex("[nvme] FLBAS=", flbas);
log_hex("[nvme] LBADS=", lbads);
*out_nsze = nsze;
*out_lbads = lbads;
return 0;
}
/* ---- B7 阶段3: PCI MSI 能力编程 ----
* MSI cap (ID 0x05) 布局:
* +0x00 u8 Cap ID (=0x05) +0x01 u8 Next Ptr
* +0x02 u16 Message Control: bit0 Enable, bit[3:1] MMC(可申请向量数-1),
* bit[6:4] MME(已分配向量数-1), bit7 64-bit Capable, bit8 PVM
* +0x04 u32 Message Address
* 64-bit 时: +0x08 u32 Message Upper Address, +0x0C u16 Message Data
* 32-bit 时: +0x08 u16 Message Data
* PVM(bit8)=1 时另有 Mask/Pending 寄存器, 复位值 0 (全部 unmask)。
* x86 LAPIC 投递公式 (physical destination, fixed delivery):
* Address = 0xFEE00000 | (dest_apic_id << 12) (RH=0, DM=0)
* Data = vector | (0<<8 delivery=fixed) (level/trigger 对 MSI 无效)
*/
#define PCI_CAP_ID_MSI 0x05
#define PCI_CAP_ID_MSIX 0x11
#define PCI_STATUS_CAPLIST 0x10u
static int nvme_find_pci_cap(u8 bus, u8 dev, u8 func, u8 cap_id) {
u32 reg04 = dkm_pci_read(bus, dev, func, PCI_COMMAND);
if (!((reg04 >> 16) & PCI_STATUS_CAPLIST)) return -1; /* Status.CapList=0 */
u8 ptr = (u8)(dkm_pci_read(bus, dev, func, PCI_CAP_PTR) & 0xFC);
int guard = 0;
while (ptr && guard++ < 64) { /* guard 防配置空间坏链死循环 */
u32 dw = dkm_pci_read(bus, dev, func, ptr);
u8 id = (u8)(dw & 0xff);
u8 next = (u8)((dw >> 8) & 0xff);
log_hex("[nvme] pci cap id=", id);
log_hex("[nvme] pci cap at=", ptr);
if (id == cap_id) return (int)ptr;
ptr = next & 0xFC;
}
return -1;
}
/* MSI cap (0x05) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* 失败路径完整回滚 (恢复 msg_ctl + 释放向量), 不留半编程状态。 */
static int nvme_msi_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSI);
if (cap < 0) {
g_log->info("[nvme] MSI capability not present");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
int cap64 = (int)((msg_ctl >> 7) & 1u);
int pvm = (int)((msg_ctl >> 8) & 1u);
log_hex("[nvme] MSI msg_ctl=", msg_ctl);
log_hex("[nvme] MSI 64bit=", (u64)cap64);
log_hex("[nvme] MSI pvm=", (u64)pvm);
/* 动态向量: UTSM 位图池 0x40-0xDF, 与静态向量 (tick 0xE0/spurious 0xFF)
* 及 legacy PIC 段 (0x20-0x2F) 无交叠。 */
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
g_log->warn("[nvme] MSI: vector alloc failed; polling");
return 0;
}
log_dec("[nvme] MSI vector=", (u64)vector);
/* 目的 APIC ID: CPUID.1:EBX[31:24] = BSP initial APIC ID (QEMU=0) */
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
log_hex("[nvme] MSI dest apic id=", dest);
u32 addr = 0xFEE00000u | (dest << 12);
u32 data = (u32)vector & 0xffu; /* fixed delivery, edge */
/* 先写 Address/Data, 最后置 Enable — 避免半成品消息被发出 */
dkm_pci_write(bus, dev, func, (u8)(cap + 4), addr);
if (cap64) {
dkm_pci_write(bus, dev, func, (u8)(cap + 8), 0); /* upper addr */
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 12));
dw = (dw & 0xffff0000u) | data; /* data 为低 u16 */
dkm_pci_write(bus, dev, func, (u8)(cap + 12), dw);
} else {
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 8));
dw = (dw & 0xffff0000u) | data;
dkm_pci_write(bus, dev, func, (u8)(cap + 8), dw);
}
/* Message Control: MME=0 (单向量), Enable=1; 低 u16 (id/next) 回写原值 */
u32 mc = msg_ctl;
mc &= ~(0x7u << 4); /* MME=0 → 1 vector */
mc |= 1u; /* MSI Enable */
cdw = (cdw & 0x0000ffffu) | (mc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* PVM: 显式清 mask bit0 (复位值即为 0, 此处防御性 RMW) */
if (pvm) {
int mask_off = cap64 ? cap + 16 : cap + 12;
u32 mask = dkm_pci_read(bus, dev, func, (u8)mask_off);
mask &= ~1u;
dkm_pci_write(bus, dev, func, (u8)mask_off, mask);
log_hex("[nvme] MSI mask cleared=", mask);
}
/* 回读验证: Enable 置位 + Address/Data 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_addr = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 rb_data = dkm_pci_read(bus, dev, func,
(u8)(cap64 ? cap + 12 : cap + 8)) & 0xffffu;
log_hex("[nvme] MSI readback ctl=", rb_ctl);
log_hex("[nvme] MSI readback addr=", rb_addr);
log_hex("[nvme] MSI readback data=", rb_data);
if (!(rb_ctl & 1u) || rb_addr != addr || (rb_data & 0xffu) != data) {
g_log->warn("[nvme] MSI: readback mismatch; rolling back");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
api->irq_vector_free(vector);
return 0;
}
/* 注册 handler (irq_register 已接受向量命名空间, >0x2F 直挂分发表) */
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI enabled (IO CQ IEN=1, IV=0)");
return 1;
}
/* 按 BIR 读取 BAR 物理基址 (处理 64-bit BAR; IO BAR 返回 0) */
static u64 nvme_bar_phys(u8 bus, u8 dev, u8 func, u32 bir) {
u8 reg = (u8)(PCI_BAR0 + bir * 4u);
u32 lo = dkm_pci_read(bus, dev, func, reg);
if (lo & 1u) return 0; /* IO BAR */
if ((lo & 0x6u) == 0x4u) { /* 64-bit MMIO BAR */
u32 hi = dkm_pci_read(bus, dev, func, (u8)(reg + 4));
return ((u64)hi << 32) | (u64)(lo & 0xFFFFFFF0u);
}
return (u64)(lo & 0xFFFFFFF0u);
}
/* MSI-X cap (0x11) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* QEMU nvme 只暴露 MSI-X (无 0x05), 本路径是 QEMU 下中断驱动化的唯一通道。
* 与 MSI 的差异仅在消息存储位置: MSI-X 的 addr/data 存放在 BAR 内的
* 表项 (16B/项), 需先把表区 MMIO 映射进 CPU 地址空间再编程 entry 0。
* 失败路径完整回滚 (清 Enable + 表项重新 mask + 释放向量)。 */
static int nvme_msix_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSIX);
if (cap < 0) {
g_log->warn("[nvme] MSI-X capability not present; polling");
return 0;
}
if (!api->mm_map_mmio) {
g_log->warn("[nvme] MSI-X: mm_map_mmio unavailable; polling");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
u32 table_size = (msg_ctl & 0x7ffu) + 1u; /* N-1 encoded */
u32 tbl_dw = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 bir = tbl_dw & 7u;
u32 tbl_off = tbl_dw & ~7u;
log_hex("[nvme] MSI-X msg_ctl=", msg_ctl);
log_dec("[nvme] MSI-X table size=", table_size);
log_hex("[nvme] MSI-X table BIR=", bir);
log_hex("[nvme] MSI-X table offset=", tbl_off);
if (bir > 5u) {
g_log->warn("[nvme] MSI-X: invalid BIR; polling");
return 0;
}
u64 bar_phys = nvme_bar_phys(bus, dev, func, bir);
if (!bar_phys) {
g_log->warn("[nvme] MSI-X: table BAR invalid; polling");
return 0;
}
u64 tbl_phys = bar_phys + tbl_off;
log_hex("[nvme] MSI-X table BAR phys=", bar_phys);
log_hex("[nvme] MSI-X table phys=", tbl_phys);
/* 映射表区首页 (含 entry0 的 16B)。mm_map_mmio 返回含页内偏移的指针,
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
if (!tbl) {
/* 严格错误策略插桩:MSI-X 表映射失败不允许静默转轮询(NVME-E01) */
g_log->error("[nvme] MSI-X: table MMIO map failed");
g_log->panic("NVME-E01 MSIX TABLE MAP FAILED");
return 0;
}
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
/* 严格错误策略插桩:向量分配失败不允许静默转轮询(NVME-E02) */
g_log->error("[nvme] MSI-X: vector alloc failed");
g_log->panic("NVME-E02 VECTOR ALLOC FAILED");
return 0;
}
log_dec("[nvme] MSI-X vector=", (u64)vector);
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
u32 addr = 0xFEE00000u | (dest << 12);
/* 先写表项 (addr/data/mask=0), 再置 cap Enable — 避免半成品消息 */
volatile u32 *e = tbl; /* entry 0 */
e[0] = addr; /* Msg Addr Lo */
e[1] = 0; /* Msg Addr Hi */
e[2] = (u32)vector & 0xffu; /* Msg Data = 向量 */
e[3] = 0; /* Vector Control: unmask */
nvme_mb();
u32 nc = (msg_ctl | 0x8000u) & ~(1u << 14); /* Enable=1, FunctionMask=0 */
cdw = (cdw & 0x0000ffffu) | (nc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* 回读验证: cap Enable + 表项 addr/data/mask 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_a0 = e[0], rb_a1 = e[1], rb_d = e[2], rb_vc = e[3];
log_hex("[nvme] MSI-X readback ctl=", rb_ctl);
log_hex("[nvme] MSI-X readback entry addr=", ((u64)rb_a1 << 32) | rb_a0);
log_hex("[nvme] MSI-X readback entry data=", rb_d);
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
/* 严格错误策略插桩:MSI-X 编程回读不符 = 硬件/虚拟机拒绝消息中断,
* 不允许静默回滚转轮询(NVME-E03)。 */
g_log->error("[nvme] MSI-X: readback mismatch");
g_log->panic("NVME-E03 MSIX READBACK MISMATCH");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
e[3] = 1; /* 表项重新 mask */
api->irq_vector_free(vector);
return 0;
}
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msix_mode = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI-X enabled (table entry0, IO CQ IEN=1, IV=0)");
return 1;
}
/* 中断驱动化入口: 先 MSI (0x05), 缺则 MSI-X (0x11), 皆不可则纯轮询。
* 前提检查集中在此; 两条编程路径各自保证失败完整回滚。 */
static int nvme_msi_setup(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
if (!g_cfg_nvme_msi) {
g_log->info("[nvme] MSI disabled by FUCK (nvme_msi=0); polling mode");
return 0;
}
if (!api->irq_vector_alloc || !api->irq_vector_free || !api->irq_register) {
g_log->warn("[nvme] MSI: kernel vector allocator unavailable; polling");
return 0;
}
if (nvme_msi_program(api, bus, dev, func)) return 1;
return nvme_msix_program(api, bus, dev, func);
}
/* ---- IO queue 创建 ---- */
static int nvme_create_io_queues(void) {
u32 dw[16];
/* Create IO CQ (qid=1): PC=1; MSI 就绪时 IEN=1 + IV=0
* CDW10 = QSIZE[31:16](0's based) | QID[15:0] —— QID 必须在低半字
* CDW11 = IV[31:16] | IEN[1] | PC[0] —— IV=0 与 INTMS/INTMC bit0 对应 */
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_ADM_CREATE_IOCQ;
dw[6] = (u32)(g_iocq.phys & 0xffffffffu);
dw[7] = (u32)(g_iocq.phys >> 32);
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
dw[11] = 1u;
if (g_msi_enabled) dw[11] = 1u | (1u << 1); /* PC=1, IEN=1, IV=0 */
log_hex("[nvme] create IOCQ CDW11=", dw[11]);
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) {
g_log->error("[nvme] create IO CQ failed");
return rc;
}
/* Create IO SQ (qid=1, cqid=1): PC=1
* CDW10 同 Create IOCQ: QSIZE[31:16] | QID[15:0]; CDW11 = CQID[31:16] | PC */
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_ADM_CREATE_IOSQ;
dw[6] = (u32)(g_iosq.phys & 0xffffffffu);
dw[7] = (u32)(g_iosq.phys >> 32);
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
dw[11] = (1u << 16) | 1;
rc = nvme_admin_submit(dw, 0);
if (rc != 0) {
g_log->error("[nvme] create IO SQ failed");
return rc;
}
g_iosq_tail = 0;
g_iocq_head = 0;
g_iocq_phase = 1;
return 0;
}
/* ---- IO Read/Write(PRP1 + PRP2 直连 / PRP list 分档) ----
* buf_phys 必须 4KiB 对齐且物理连续(本驱动只传 g_data.phys)。
* 传输页数由 nlb × sector_size 推出,决定 PRP 布局:
* 1 页 : PRP2 = 0(单 PRP1,等价旧 bounce 路径)
* 2 页 : PRP2 = 第二页物理地址(spec 规定的两页直连形式)
* >=3 页: PRP2 = g_prp list 页物理地址, list[i] = 第 i+2 页物理地址
* 单 list 页 512 项, 上限 513 页 (2052KiB), 远大于 chunk 上限 32 页,
* 无需 list 链页。PRP list 由 CPU 写、控制器 DMA 读: x86 TSO + 编译屏障
* 保证 list 先于 doorbell 可见(与 SQE 提交路径同一约定)。
*/
static int nvme_io_rw(u8 opcode, u64 slba, u32 nlb, u64 buf_phys) {
u32 bytes = nlb * (u32)g_sector_size;
u32 pages = (bytes + 4095u) >> 12;
if (pages == 0 || (pages - 1u) > NVME_PRP_LIST_ENTRIES) return -3;
u32 dw[16];
nvme_zero(dw, sizeof(dw));
dw[0] = opcode;
dw[1] = 1; /* nsid=1 */
dw[6] = (u32)(buf_phys & 0xffffffffu);
dw[7] = (u32)(buf_phys >> 32);
if (pages == 2) {
u64 p2 = buf_phys + 4096u;
dw[8] = (u32)(p2 & 0xffffffffu);
dw[9] = (u32)(p2 >> 32);
} else if (pages >= 3) {
volatile u64 *list = (volatile u64 *)g_prp.virt;
for (u32 i = 0; i < pages - 1u; i++)
list[i] = buf_phys + 4096ULL * (u64)(i + 1u);
nvme_mb(); /* list 内容先于 doorbell 可见 */
dw[8] = (u32)(g_prp.phys & 0xffffffffu);
dw[9] = (u32)(g_prp.phys >> 32);
}
dw[10] = (u32)(slba & 0xffffffffu);
dw[11] = (u32)(slba >> 32);
dw[12] = nlb - 1u; /* NLB 0-based */
return nvme_io_submit(dw, 0);
}
/* NVM Flush (opcode 0x00): 无数据字段, 把 volatile write cache 刷到介质 */
static int nvme_io_flush(void) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_IO_FLUSH;
dw[1] = 1; /* nsid=1 */
return nvme_io_submit(dw, 0);
}
/* ---- block provider ----
* 调用方缓冲为任意虚拟地址(DSK 静态 BSS、vmm virtio_blk dbuf 等),
* 物理底账未知且不一定连续/对齐,不能直接进 PRP。一律 bounce 经
* g_data(物理连续 DMA 区): 一次命令读/写满 chunk,再 memcpy 中转。
*/
static int nvme_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (!g_io_ready || !buffer || count == 0) return -1;
u8 *out = (u8 *)buffer;
while (count > 0) {
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
int rc = nvme_io_rw(NVME_IO_READ, lba, n, g_data.phys);
if (rc != 0) return rc;
u32 bytes = n * (u32)g_sector_size;
u8 *src = (u8 *)g_data.virt;
for (u32 i = 0; i < bytes; i++) out[i] = src[i];
out += bytes;
lba += n;
count -= n;
}
return 0;
}
static int nvme_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (!g_io_ready || !buffer || count == 0) return -1;
const u8 *in = (const u8 *)buffer;
while (count > 0) {
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
u32 bytes = n * (u32)g_sector_size;
u8 *dst = (u8 *)g_data.virt;
for (u32 i = 0; i < bytes; i++) dst[i] = in[i];
/* 沿用 ahci BUG-P0-2 先例: 写前 flush CPU cache, 防 DMA 读到陈旧数据 */
__asm__ volatile("wbinvd" ::: "memory");
int rc = nvme_io_rw(NVME_IO_WRITE, lba, n, g_data.phys);
if (rc != 0) return rc;
in += bytes;
lba += n;
count -= n;
}
/* 每次写调用末尾 FLUSH 一次,保证 volatile write cache 落盘 */
return nvme_io_flush();
}
/* ---- 数据面自测(仅签名测试盘运行写测试,防实机盘数据损坏) ---- */
/* 读回 LBA0 并核对 DESHABNVME0 签名(写测试后复查无串扰用) */
static int nvme_lba0_sig_ok(void) {
static const char sig[] = "DESHABNVME0";
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
if (rc != 0) return 0;
u8 *d = (u8 *)g_data.virt;
for (u32 i = 0; i < 11; i++) {
if (d[i] != (u8)sig[i]) return 0;
}
return 1;
}
/* 多页单命令读: LBA0 起 32 扇区 = 16KiB = 4 页 → PRP list 路径。
* 测试盘内容已知: LBA0 为 "DESHABNVME0" 重复填满 512B, LBA1..31 全零。 */
static void nvme_selftest_read_multipage(void) {
static u8 rbuf[16384];
static const char sig[] = "DESHABNVME0";
int rc = nvme_block_read(0, 0, 32, rbuf);
log_hex("[nvme] PRPLIST read 32sec rc=", (u64)(i64)rc);
if (rc != 0) return;
u32 bad = 0;
for (u32 i = 0; i < 512; i++) {
if (rbuf[i] != (u8)sig[i % 11]) {
bad = 1;
log_hex("[nvme] sig mismatch idx=", i);
break;
}
}
if (!bad) {
for (u32 i = 512; i < 16384; i++) {
if (rbuf[i] != 0) {
bad = 1;
log_hex("[nvme] nonzero idx=", i);
break;
}
}
}
if (bad) {
g_log->error("[nvme] PRPLIST read verify FAILED");
} else {
g_log->info("[nvme] PRPLIST read verify OK (LBA0 sig + LBA1-31 zero)");
}
}
/* 写 → FLUSH → 读回逐字节校验。sectors=16 走 PRP2 直连(2页),
* sectors=32 走 PRP list(4页)。pattern 为首 8 字节 "NVMEWRIT" + 全缓冲
* 确定式伪随机,重跑幂等(与上次写入内容一致)。 */
static void nvme_selftest_rw(u64 lba, u32 sectors) {
static u8 wpat[16384];
static u8 rbuf[16384];
u32 bytes = sectors * (u32)g_sector_size;
if (bytes > sizeof(wpat)) return;
for (u32 i = 0; i < bytes; i++) wpat[i] = (u8)(i * 31u + 17u);
wpat[0] = 'N'; wpat[1] = 'V'; wpat[2] = 'M'; wpat[3] = 'E';
wpat[4] = 'W'; wpat[5] = 'R'; wpat[6] = 'I'; wpat[7] = 'T';
log_hex("[nvme] selftest rw lba=", lba);
log_hex("[nvme] selftest rw sectors=", sectors);
int rc = nvme_block_write(0, lba, sectors, wpat);
log_hex("[nvme] write+flush rc=", (u64)(i64)rc);
if (rc != 0) return;
for (u32 i = 0; i < bytes; i++) rbuf[i] = 0;
rc = nvme_block_read(0, lba, sectors, rbuf);
log_hex("[nvme] readback rc=", (u64)(i64)rc);
if (rc != 0) return;
for (u32 i = 0; i < bytes; i++) {
if (rbuf[i] != wpat[i]) {
g_log->error("[nvme] readback MISMATCH");
log_hex("[nvme] first bad idx=", i);
log_hex("[nvme] expect=", wpat[i]);
log_hex("[nvme] actual=", rbuf[i]);
return;
}
}
g_log->info("[nvme] write+flush+readback verify OK");
}
/* B7 阶段3 压测: 128 扇区 (64KiB, PRP list 路径) × 50 次 写+读。
* 统计总耗时与 MSI 计数器增量, 供中断/轮询两模式粗粒度对比。
* 仅签名测试盘运行 (写 LBA3000 起的 50 个块, 不碰 LBA0 签名区)。 */
static void nvme_selftest_stress(void) {
static u8 wbuf[65536];
static u8 rbuf[65536];
for (u32 i = 0; i < sizeof(wbuf); i++) wbuf[i] = (u8)(i * 17u + 3u);
u64 irq0 = g_msi_irq_count, slot0 = g_msi_slot_hits, poll0 = g_msi_poll_harvests;
u64 t0 = dkm_rdtsc();
u32 fails = 0;
for (u32 i = 0; i < 50; i++) {
u64 lba = 3000 + (u64)i * 128;
if (nvme_block_write(0, lba, 128, wbuf) != 0) { fails++; break; }
if (nvme_block_read(0, lba, 128, rbuf) != 0) { fails++; break; }
}
u64 elapsed_ms = dkm_tsc_per_ms ? (dkm_rdtsc() - t0) / dkm_tsc_per_ms : 0;
/* 末次读回全量校验 (数据通路在 MSI 模式下完整性证明) */
if (!fails) {
for (u32 i = 0; i < sizeof(wbuf); i++) {
if (rbuf[i] != wbuf[i]) {
g_log->error("[nvme] stress readback MISMATCH");
log_hex("[nvme] first bad idx=", i);
log_hex("[nvme] expect=", wbuf[i]);
log_hex("[nvme] actual=", rbuf[i]);
log_hex("[nvme] cq head=", g_iocq_head);
log_hex("[nvme] cq phase=", g_iocq_phase);
log_hex("[nvme] slot valid=", g_slot_valid);
fails++;
break;
}
}
}
log_dec("[nvme] stress 50x(128sec W+R) ms=", elapsed_ms);
log_dec("[nvme] stress fails=", fails);
log_dec("[nvme] stress irq delta=", g_msi_irq_count - irq0);
log_dec("[nvme] stress slot delta=", g_msi_slot_hits - slot0);
log_dec("[nvme] stress poll delta=", g_msi_poll_harvests - poll0);
if (!fails) g_log->info("[nvme] stress verify OK");
}
static void nvme_msi_stats_dump(const char *tag) {
g_log->info(tag);
log_dec("[nvme] msi enabled=", (u64)g_msi_enabled);
log_dec("[nvme] msix mode=", (u64)g_msix_mode);
log_dec("[nvme] msi vector=", (u64)(g_msi_vector < 0 ? 0 : g_msi_vector));
log_dec("[nvme] msi irq count=", g_msi_irq_count);
log_dec("[nvme] msi slot hits=", g_msi_slot_hits);
log_dec("[nvme] msi poll harvests=", g_msi_poll_harvests);
log_dec("[nvme] msi timeouts=", g_msi_timeouts);
log_dec("[nvme] msi cid mismatch=", g_msi_cid_mismatch);
}
/* ---- 真机诊断: dump 关键寄存器 ---- */
static void nvme_dump_regs(const char *ctx) {
g_log->info("[nvme] --- register dump: ");
g_log->info(ctx);
log_hex("[nvme] CAP =", mmio_read64(NVME_CAP));
log_hex("[nvme] VS =", mmio_read32(NVME_VS));
log_hex("[nvme] CC =", mmio_read32(NVME_CC));
log_hex("[nvme] CSTS =", mmio_read32(NVME_CSTS));
log_hex("[nvme] AQA =", mmio_read32(NVME_AQA));
log_hex("[nvme] ASQ =", mmio_read64(NVME_ASQ));
log_hex("[nvme] ACQ =", mmio_read64(NVME_ACQ));
/* doorbell 区域首个 entry 诊断 */
log_hex("[nvme] SQ0DB=", mmio_read32(NVME_DB_BASE));
log_hex("[nvme] CQ0DB=", mmio_read32(NVME_DB_BASE + g_db_stride));
}
/* ---- 高位 BAR MMIO 接入 ---- */
/* 真机 NVMe 控制器寄存器 + doorbell 空间可能远大于 8K。
* 典型实机: CAP.MQES 决定队列深度,doorbell 偏移 = DB_BASE + 2*QID*DSTRD*4,
* 需要映射足够大的范围覆盖至少 admin + 1 个 IO queue 对。
* 安全映射大小: 至少 64K(覆盖 QID 0-7 的 SQ/CQ doorbell)。 */
#define NVME_MAP_SIZE 0x10000
static volatile u8 *nvme_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
u64 bar_virt = api->hhdm_offset + bar_phys;
/* 优先使用 mm_map_mmio(独立窗口映射, PCD|PWT, 不与 HHDM 空洞竞争) */
if (api->mm_map_mmio) {
void *mapped = api->mm_map_mmio(bar_phys, NVME_MAP_SIZE);
if (mapped) {
g_log->info("[nvme] BAR mapped via mm_map_mmio window");
return (volatile u8 *)mapped;
}
g_log->warn("[nvme] mm_map_mmio failed; trying HHDM fallback");
}
if (!api->mmio || !api->mmio->is_mapped) {
/* 内核无页表服务: 仅当 BAR 位于低 4G(HHDM 经验上覆盖)时冒险直用 */
if (bar_phys < 0x100000000ULL) {
g_log->warn("[nvme] no mmio api; assume HHDM covers low BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
g_log->warn("[nvme] no mmio api and BAR above 4G; cannot access");
return (volatile u8 *)0;
}
log_hex("[nvme] CR3=", api->mmio->cr3 ? api->mmio->cr3() : 0);
/* 核实: HHDM 是否直接覆盖该 BAR(四级页表只读 walk, 无 #PF 风险) */
int level = api->mmio->is_mapped(bar_virt);
log_hex("[nvme] HHDM is_mapped(BAR)=", (u64)(i64)level);
if (level == 0) {
g_log->info("[nvme] HHDM does NOT cover BAR; mapping via page tables");
if (!api->mmio->map_mmio) return (volatile u8 *)0;
int rc = api->mmio->map_mmio(bar_phys, NVME_MAP_SIZE);
log_hex("[nvme] map_mmio rc=", (u64)(i64)rc);
if (rc != 0) return (volatile u8 *)0;
level = api->mmio->is_mapped(bar_virt);
log_hex("[nvme] post-map is_mapped=", (u64)(i64)level);
if (level == 0) {
g_log->error("[nvme] BAR still unmapped after map_mmio");
return (volatile u8 *)0;
}
} else {
g_log->info("[nvme] HHDM already covers BAR; direct access");
}
return (volatile u8 *)(uintptr_t)bar_virt;
}
/* ---- driver entry ---- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[nvme] init begin");
nvme_load_config(api);
u8 bus = 0, dev = 0, func = 0;
if (nvme_find(&bus, &dev, &func) != 0) {
g_log->warn("[nvme] NVMe controller not found");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] NVMe controller found");
DKM_STAT_INC(nvm0_pci_found);
log_hex("[nvme] bus=", bus);
log_hex("[nvme] dev=", dev);
log_hex("[nvme] func=", func);
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[nvme] vendor=", vd & 0xffff);
log_hex("[nvme] device=", vd >> 16);
/* 启用 MEM + BUSM, 并禁用 INTx(轮询模式不需要中断) */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[nvme] PCI command=", command);
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
u64 bar_phys;
if (bar0_lo & 1u) {
g_log->warn("[nvme] BAR0 is IO space; unsupported");
g_log->info("[nvme] driver ready");
return 0;
}
if ((bar0_lo & 0x6u) == 0x4u) {
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
} else {
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
}
u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[nvme] BAR0 phys=", bar_phys);
log_hex("[nvme] PCI IRQ line=", irq_line);
log_hex("[nvme] HHDM offset=", api->hhdm_offset);
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[nvme] missing HHDM or BAR0; skip");
g_log->info("[nvme] driver ready");
return 0;
}
g_regs = nvme_map_bar(api, bar_phys);
if (!g_regs) {
g_log->error("[nvme] BAR MMIO unavailable; driver inactive");
g_log->info("[nvme] driver ready");
return 0;
}
/* ---- 寄存器枚举(验证 MMIO 可读) ---- */
u64 cap = mmio_read64(NVME_CAP);
u32 vs = mmio_read32(NVME_VS);
u32 cc = mmio_read32(NVME_CC);
u32 csts = mmio_read32(NVME_CSTS);
log_hex("[nvme] CAP=", cap);
log_hex("[nvme] VS=", vs);
log_hex("[nvme] CC=", cc);
log_hex("[nvme] CSTS=", csts);
log_hex("[nvme] CAP.MQES=", (cap & 0xffff) + 1);
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
if (cap == 0 || cap == 0xFFFFFFFFFFFFFFFFULL) {
g_log->error("[nvme] CAP invalid; MMIO read broken");
g_log->info("[nvme] driver ready");
return 0;
}
if (!((cap >> 37) & 1u)) {
g_log->error("[nvme] NVM command set not supported");
g_log->info("[nvme] driver ready");
return 0;
}
g_db_stride = 4u << ((cap >> 32) & 0xf);
g_timeout_ms = ((cap >> 24) & 0xff) * 500;
if (!g_timeout_ms) g_timeout_ms = 500;
log_hex("[nvme] doorbell stride=", g_db_stride);
log_hex("[nvme] timeout ms=", g_timeout_ms);
if (!api->dma) {
g_log->warn("[nvme] DMA API unavailable; queues not configured");
g_log->info("[nvme] driver ready");
return 0;
}
/* ---- 分配队列与数据缓冲(低 4G, 4K 对齐, 清零, 物理连续) ---- */
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_asq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_acq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_ident) != 0) goto dma_fail;
if (api->dma->alloc_pages(NVME_DATA_PAGES, 4096, 0x100000000ULL, &g_data) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_prp) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iosq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iocq) != 0) goto dma_fail;
goto dma_ok;
dma_fail:
g_log->error("[nvme] DMA alloc failed");
g_log->info("[nvme] driver ready");
return 0;
dma_ok:
log_hex("[nvme] ASQ phys=", g_asq.phys);
log_hex("[nvme] ACQ phys=", g_acq.phys);
log_hex("[nvme] DATA phys=", g_data.phys);
log_hex("[nvme] PRP list phys=", g_prp.phys);
log_hex("[nvme] IOSQ phys=", g_iosq.phys);
log_hex("[nvme] IOCQ phys=", g_iocq.phys);
/* ---- 控制器复位: EN=0 等 RDY=0 ---- */
mmio_write32(NVME_INTMS, 0xFFFFFFFFu); /* 屏蔽全部中断向量 */
cc = mmio_read32(NVME_CC);
if (cc & 1u) {
mmio_write32(NVME_CC, cc & ~1u);
}
if (nvme_wait_csts(1u, 0u, g_timeout_ms) != 0) {
g_log->error("[nvme] disable failed");
nvme_dump_regs("controller disable timeout");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] controller disabled");
/* ---- 配置 admin queue ---- */
mmio_write32(NVME_AQA, ((u32)(ADMIN_QSIZE - 1) << 16) | (u32)(ADMIN_QSIZE - 1));
mmio_write64(NVME_ASQ, g_asq.phys);
mmio_write64(NVME_ACQ, g_acq.phys);
g_asq_tail = 0;
g_acq_head = 0;
g_acq_phase = 1;
g_cid = 1;
/* ---- 使能: EN=1, CSS=NVM, MPS=4K, AMS=RR, IOSQES=6, IOCQES=4 ---- */
mmio_write32(NVME_CC, 0x00460001u);
if (nvme_wait_csts(1u, 1u, g_timeout_ms) != 0) {
g_log->error("[nvme] enable failed");
nvme_dump_regs("controller enable timeout");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] controller enabled, admin queue live");
/* ---- Identify ---- */
u32 nn = 0;
if (nvme_identify_controller(&nn) != 0) {
g_log->error("[nvme] identify controller failed");
g_log->info("[nvme] driver ready");
return 0;
}
if (nn == 0) {
g_log->warn("[nvme] no namespace");
g_log->info("[nvme] driver ready");
return 0;
}
u64 nsze = 0;
u32 lbads = 0;
if (nvme_identify_namespace(1, &nsze, &lbads) != 0) {
g_log->error("[nvme] identify namespace failed");
g_log->info("[nvme] driver ready");
return 0;
}
if (lbads < 9 || lbads > 12 || nsze == 0) {
g_log->warn("[nvme] unsupported LBA format or empty NS");
log_hex("[nvme] LBADS=", lbads);
g_log->info("[nvme] driver ready");
return 0;
}
g_sector_size = 1ULL << lbads;
g_sector_count = nsze;
log_hex("[nvme] sector size=", g_sector_size);
log_hex("[nvme] sector count=", g_sector_count);
/* ---- 单命令扇区上限: min(bounce 缓冲, 2^MDTS 页, NLB 16-bit) ----
* QEMU MDTS=7 → 512KiB, bounce 32 页=128KiB 更紧, 故 chunk=256 扇区,
* 恰好覆盖 DSK FAT32 的 256 扇区批量读(一次命令读满)。 */
{
u64 buf_bytes = (u64)NVME_DATA_PAGES * 4096u;
u64 mdts_bytes = g_mdts ? ((1ULL << g_mdts) * 4096u) : buf_bytes;
u64 cap = buf_bytes < mdts_bytes ? buf_bytes : mdts_bytes;
g_chunk_max_sectors = (u32)(cap / g_sector_size);
if (g_chunk_max_sectors > 65536u) g_chunk_max_sectors = 65536u;
if (g_chunk_max_sectors == 0) g_chunk_max_sectors = 1;
log_hex("[nvme] chunk max sectors=", g_chunk_max_sectors);
}
/* ---- B7 阶段3: IO queue 创建前尝试 MSI (任何前提缺失回退纯轮询) ---- */
nvme_msi_setup(api, bus, dev, func);
/* ---- IO queue ---- */
if (nvme_create_io_queues() != 0) {
g_log->error("[nvme] IO queue creation failed");
g_log->info("[nvme] driver ready");
return 0;
}
g_io_ready = 1;
g_log->info("[nvme] IO queue live");
/* MSI 就绪: INTMC 解除 IV0 屏蔽 (此前 IV0 保持屏蔽, admin 完成不产生
* MSI; 此后 IO CQ 每次完成经 MSI 投递到 g_msi_vector) */
if (g_msi_enabled) {
mmio_write32(NVME_INTMC, 1u);
log_hex("[nvme] INTMC unmask IV0, INTMS=", mmio_read32(NVME_INTMS));
}
/* ---- LBA0 读取验证 ---- */
{
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
log_hex("[nvme] READ LBA0 rc=", (u64)(i64)rc);
if (rc == 0) {
u8 *d = (u8 *)g_data.virt;
u32 *w = (u32 *)g_data.virt;
log_hex("[nvme] LBA0 dword0=", w[0]);
log_hex("[nvme] LBA0 dword1=", w[1]);
log_hex("[nvme] LBA0 dword2=", w[2]);
log_hex("[nvme] LBA0 dword3=", w[3]);
static const char sig[] = "DESHABNVME0";
u32 match = 1;
for (u32 i = 0; i < 11; i++) {
if (d[i] != (u8)sig[i]) { match = 0; break; }
}
if (match) {
g_log->info("[nvme] LBA0 signature OK (DESHABNVME0)");
g_test_disk = 1;
} else {
g_log->info("[nvme] LBA0 data read (no test signature)");
}
}
}
/* ---- 注册 block provider(读 + 写) ---- */
if (api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "nvme0";
desc.sector_size = g_sector_size;
desc.sector_count = g_sector_count;
desc.ctx = 0;
desc.read = nvme_block_read;
desc.write = nvme_block_write;
int index = api->block->register_device(&desc);
log_hex("[nvme] block provider index=", (u64)(i64)index);
} else {
g_log->warn("[nvme] block API unavailable; provider not registered");
}
/* ---- 数据面自测: 写测试仅在签名测试盘上运行(防实机盘数据损坏) ---- */
if (g_test_disk) {
nvme_selftest_read_multipage(); /* 32 扇区 16KiB: PRP list 读 */
nvme_selftest_rw(1000, 16); /* 8KiB: PRP2 直连 (2 页) 写+读回 */
nvme_selftest_rw(2000, 32); /* 16KiB: PRP list (4 页) 写+读回 */
nvme_selftest_stress(); /* B7: 128 扇区×50 压测 + MSI 计数 */
if (nvme_lba0_sig_ok()) {
g_log->info("[nvme] LBA0 sig intact after write tests");
} else {
g_log->error("[nvme] LBA0 sig CORRUPTED after write tests");
}
} else {
g_log->info("[nvme] not a test disk; write self-test skipped");
}
nvme_msi_stats_dump("[nvme] --- MSI stats ---");
g_log->info("[nvme] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-427
View File
@@ -1,427 +0,0 @@
/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC)
* Stage 0, required, depends on "irq", provides "pci".
* No IRQ needed for enumeration. Scans all buses/devices/functions.
*
* 真机增强:
* - 多总线扫描(桥接设备下游 bus > 0)
* - 网卡型号识别(常见 Intel/Realtek 网卡芯片自动标注)
* - PCI 桥检测和副总线号读取
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci_api;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
/* ---------------------------------------------------------------
* PCI constants
* --------------------------------------------------------------- */
#define PCI_CFG_ADDR 0xCF8
#define PCI_CFG_DATA 0xCFC
#define PCI_VENDOR_ID 0x00
#define PCI_DEVICE_ID 0x02
#define PCI_COMMAND 0x04
#define PCI_STATUS 0x06
#define PCI_CLASS 0x0B /* class code */
#define PCI_SUBCLASS 0x0A /* subclass */
#define PCI_PROG_IF 0x09 /* prog IF */
#define PCI_HEADER_TYPE 0x0E
#define PCI_BAR0 0x10
/* PCI 桥配置 (header type 1) */
#define PCI_PRI_BUS 0x18
#define PCI_SEC_BUS 0x19
#define PCI_SUB_BUS 0x1A
#define PCI_CMD_IO (1u << 0)
#define PCI_CMD_MEM (1u << 1)
#define PCI_CMD_BUSM (1u << 2)
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_depends[] = { "irq" };
static const char *const g_provides[] = { "pci" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "pci",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 2, /* DKM_CLASS_BUS */
.stage = 0,
.flags = 1, /* DKM_F_REQUIRED */
.priority = 0,
.depends = g_depends,
.depends_count = 1,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* I/O helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static __inline__ void outl(u16 port, u32 value) {
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u32 inl(u16 port) {
u32 value;
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
/* ---------------------------------------------------------------
* PCI config access
* --------------------------------------------------------------- */
static u32 pci_cfg_read(u8 bus, u8 dev, u8 func, u8 reg) {
u32 addr = (u32)((1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC));
outl(PCI_CFG_ADDR, addr);
return inl(PCI_CFG_DATA);
}
static void pci_cfg_write(u8 bus, u8 dev, u8 func, u8 reg, u32 val) {
u32 addr = (u32)((1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC));
outl(PCI_CFG_ADDR, addr);
outl(PCI_CFG_DATA, val);
}
/* ---------------------------------------------------------------
* Log helpers
* --------------------------------------------------------------- */
static void log_hex(const char *prefix, u64 val) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0'; buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(val >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ---- 网卡型号识别 ---- */
struct netchip_id {
u16 vendor;
u16 device;
const char *name;
u8 driver_hint; /* 0=e1000, 1=e1000e, 2=r8169, 3=igb, 4=ath9k */
};
static const struct netchip_id g_netchips[] = {
/* Intel e1000 经典 (8254x) */
{0x8086, 0x100E, "82540EM", 0},
{0x8086, 0x1004, "82543GC", 0},
{0x8086, 0x100F, "82545EM", 0},
{0x8086, 0x1010, "82546EB", 0},
{0x8086, 0x1013, "82541EI", 0},
{0x8086, 0x1019, "82547EI", 0},
{0x8086, 0x1026, "82545GM", 0},
{0x8086, 0x1075, "82547GI", 0},
{0x8086, 0x1076, "82541GI", 0},
{0x8086, 0x1079, "82546GB", 0},
{0x8086, 0x108B, "82573E", 0},
{0x8086, 0x109A, "82573L", 0},
/* Intel e1000e (8257x/I217/I218/I219) */
{0x8086, 0x10D3, "82574L", 1},
{0x8086, 0x10F5, "82578DM", 1},
{0x8086, 0x10F6, "82578DC", 1},
{0x8086, 0x1502, "82579V", 1},
{0x8086, 0x1533, "I217-V", 1},
{0x8086, 0x1534, "I217-LM", 1},
{0x8086, 0x153A, "I218-V", 1},
{0x8086, 0x153B, "I218-LM", 1},
{0x8086, 0x156F, "I219-V", 1},
{0x8086, 0x1570, "I219-LM", 1},
{0x8086, 0x157B, "I219-V-2", 1},
{0x8086, 0x15D3, "I219-V-3", 1},
{0x8086, 0x15D8, "I219-LM-3", 1},
{0x8086, 0x15BD, "I219-V-6", 1},
/* Intel I210/I211 (igb) */
{0x8086, 0x1537, "I210", 3},
{0x8086, 0x1536, "I211-AT", 3},
/* Intel 82575/82576/82580 (igb) */
{0x8086, 0x10A7, "82575EB", 3},
{0x8086, 0x10C9, "82576", 3},
{0x8086, 0x150D, "82580", 3},
{0x8086, 0x1526, "82576-2", 3},
/* Realtek RTL8111/8168/8125 (r8169) — 常见桌面/笔记本有线网卡 */
{0x10EC, 0x8167, "RTL8111/8168B", 2},
{0x10EC, 0x8168, "RTL8111/8168", 2},
{0x10EC, 0x8169, "RTL8169", 2},
{0x10EC, 0x8125, "RTL8125", 2},
{0x10EC, 0x3000, "RTL8169S", 2},
{0x10EC, 0x8136, "RTL8101E", 2},
/* Qualcomm Atheros WiFi (ath9k) */
{0x168C, 0x0023, "AR5416", 4},
{0x168C, 0x0024, "AR5418", 4},
{0x168C, 0x0027, "AR9160", 4},
{0x168C, 0x0029, "AR9280", 4},
{0x168C, 0x002A, "AR9285", 4},
{0x168C, 0x002B, "AR9287", 4},
{0x168C, 0x002D, "AR9287-2", 4},
{0x168C, 0x002E, "AR9287-3", 4},
{0x168C, 0x0030, "AR9300", 4},
{0x168C, 0x0032, "AR9485", 4},
{0x168C, 0x0034, "AR9462", 4},
{0x168C, 0x0036, "AR9485-2", 4},
{0x168C, 0x0037, "AR1111", 4},
/* Broadcom WiFi — 目前无驱动,标注识别 */
{0x14E4, 0x4315, "BCM4312-WiFi", 0xFF},
{0x14E4, 0x4365, "BCM4365-WiFi", 0xFF},
{0x14E4, 0x43A0, "BCM4360-WiFi", 0xFF},
{0x14E4, 0x43C3, "BCM4366-WiFi", 0xFF},
/* Qualcomm WiFi — 无驱动 */
{0x168C, 0x003E, "QCA6174-WiFi", 0xFF},
{0x168C, 0x0042, "QCA6390-WiFi", 0xFF},
/* Intel WiFi — 无驱动 */
{0x8086, 0x24FD, "Intel-8265-WiFi", 0xFF},
{0x8086, 0x2526, "Intel-AX200-WiFi",0xFF},
{0x8086, 0x2725, "Intel-AX210-WiFi",0xFF},
{0x8086, 0x51F0, "Intel-AX211-WiFi",0xFF},
{0x8086, 0x7A70, "Intel-BE200-WiFi",0xFF},
{0, 0, 0, 0} /* 终止符 */
};
static const char *netchip_name(u16 vendor, u16 device) {
for (int i = 0; g_netchips[i].name != 0; i++) {
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
return g_netchips[i].name;
}
return 0;
}
static u8 netchip_driver(u16 vendor, u16 device) {
for (int i = 0; g_netchips[i].name != 0; i++) {
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
return g_netchips[i].driver_hint;
}
return 0xFF;
}
static const char *pci_class_name(u8 base, u8 sub) {
switch (base) {
case 0x00: return "Legacy";
case 0x01: return "Storage";
case 0x02: return "Network";
case 0x03: return "Display";
case 0x04: return "Multimedia";
case 0x05: return "Memory";
case 0x06: return "Bridge";
case 0x07: return "SimpleComm";
case 0x08: return "BasePeriph";
case 0x09: return "Input";
case 0x0A: return "Docking";
case 0x0B: return "CPU";
case 0x0C: return "SerialBus";
case 0x0D: return "Wireless";
case 0x0E: return "IntelligentIO";
case 0x0F: return "Satellite";
case 0x10: return "Crypto";
case 0x11: return "SignalProc";
case 0x12: return "Accelerator";
default: return "Unknown";
}
(void)sub;
}
/* PCI subclass names for Network class */
static const char *net_subclass_name(u8 sub) {
switch (sub) {
case 0x00: return "Ethernet";
case 0x01: return "TokenRing";
case 0x02: return "FDDI";
case 0x03: return "ATM";
case 0x04: return "ISDN";
case 0x05: return "WorldFIP";
case 0x06: return "PICMG";
case 0x80: return "Other";
default: return "Net-?";
}
}
/* ---------------------------------------------------------------
* Enumeration
* --------------------------------------------------------------- */
static u32 g_device_count;
static void pci_scan_bus(u8 bus) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID);
u16 vendor = (u16)(vid_dev & 0xffff);
if (vendor == 0xffff) continue;
u8 header_type = (u8)(pci_cfg_read(bus, dev, 0, PCI_HEADER_TYPE) >> 16);
int multi_func = (header_type & 0x80) != 0;
u8 func_count = multi_func ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vid_dev2 = pci_cfg_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor2 = (u16)(vid_dev2 & 0xffff);
if (vendor2 == 0xffff) continue;
u16 device = (u16)(vid_dev2 >> 16);
u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS);
u8 class_code = (u8)(cc >> 24);
u8 subclass = (u8)((cc >> 16) & 0xff);
u8 prog_if = (u8)((cc >> 8) & 0xff);
g_device_count++;
g_log->info("[pci]");
log_hex("[pci] bus=", bus);
log_hex("[pci] dev=", dev);
log_hex("[pci] func=", func);
log_hex("[pci] vendor=", vendor2);
log_hex("[pci] device=", device);
g_log->info("[pci] class");
g_log->info(pci_class_name(class_code, subclass));
log_hex("[pci] class_hex=", class_code);
log_hex("[pci] subclass=", subclass);
/* 网卡自动识别与驱动匹配提示 */
if (class_code == 0x02) {
const char *chip_name = netchip_name(vendor2, device);
u8 drv = netchip_driver(vendor2, device);
g_log->info("[pci] NetChip");
if (chip_name) {
g_log->info(chip_name);
} else {
g_log->info("unknown");
}
g_log->info("[pci] NetSub");
g_log->info(net_subclass_name(subclass));
switch (drv) {
case 0: g_log->info("[pci] -> driver: e1000"); break;
case 1: g_log->info("[pci] -> driver: e1000 (e1000e compat)"); break;
case 2: g_log->info("[pci] -> driver: r8169 (NOT YET IMPLEMENTED)"); break;
case 3: g_log->info("[pci] -> driver: igb (e1000 compat)"); break;
case 4: g_log->info("[pci] -> driver: ath9k"); break;
case 0xFF: g_log->info("[pci] -> driver: NONE (unsupported)"); break;
default: break;
}
}
/* PCI-PCI 桥检测: 递归扫描下游总线 */
if (class_code == 0x06 && (subclass == 0x04 || subclass == 0x09)) {
u8 sec_bus = (u8)pci_cfg_read(bus, dev, func, PCI_SEC_BUS);
if (sec_bus > bus && sec_bus < 255) {
g_log->info("[pci] bridge -> secondary bus");
log_hex("[pci] sec_bus=", sec_bus);
pci_scan_bus(sec_bus);
}
}
}
}
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
g_device_count = 0;
g_log->info("[pci] scanning PCI bus");
pci_scan_bus(0);
log_hex("[pci] total devices=", g_device_count);
g_log->info("[pci] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-108
View File
@@ -1,108 +0,0 @@
/* DKM PS/2 Keyboard Driver — IRQ1 scan code → ASCII
* Stage 3, optional, depends on "irq", provides "keyboard".
* Registers IRQ1 handler, translates scan codes via lookup table.
*/
#include <stdint.h>
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(ps2kbd_irq_count);
/* Driver descriptor */
static const char *const g_depends[] = {"irq"};
static const char *const g_provides[] = {"keyboard"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="ps2kbd", .version="0.1.0", .vendor="Deshab",
.driver_class=10, .stage=3, .flags=0, .priority=0,
.depends=g_depends, .depends_count=1,
.provides=g_provides, .provides_count=1,
.min_kernel_abi=1,
};
/* Scan code set 1 → ASCII (lowercase) */
static const char sc_ascii[] = {
0,0,'1','2','3','4','5','6','7','8','9','0','-','=',0,
0,'q','w','e','r','t','y','u','i','o','p','[',']',0,
0,'a','s','d','f','g','h','j','k','l',';','\'','`',0,
'\\','z','x','c','v','b','n','m',',','.','/',0,
'*',0,' ',0
};
static const struct dkm_log_api *g_log;
static volatile int g_has_key;
static volatile u8 g_last_sc;
/* inline I/O */
static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; }
static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); }
static void log_hex(const char *prefix, u64 v) {
static const char h[]="0123456789abcdef";
char b[19]; u32 i=0; b[i++]='0'; b[i++]='x';
for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf];
b[i]=0; g_log->info(prefix); g_log->info(b);
}
static int kbd_handler(u8 irq) {
(void)irq;
g_last_sc = inb(0x60);
g_has_key = 1;
DKM_STAT_INC(ps2kbd_irq_count);
return 0; /* let kernel send EOI */
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
if (!api->irq_register) {
g_log->warn("[kbd] irq_register not available");
return 0;
}
if (api->irq_register(1, (void*)(uintptr_t)kbd_handler) != 0) {
g_log->error("[kbd] failed to register IRQ1");
return -1;
}
g_log->info("[kbd] IRQ1 registered, waiting for keypress");
g_log->info("[kbd] (boot will continue, key echo in ISR)");
/* spin briefly to show a key echo demo */
int ticks = 0;
while (ticks < 200000) {
if (g_has_key) {
u8 sc = g_last_sc;
g_has_key = 0;
if (sc < 0x80) {
/* make code */
log_hex("[kbd] sc=", sc);
if (sc < sizeof(sc_ascii) && sc_ascii[sc]) {
char buf[2] = {sc_ascii[sc], 0};
g_log->info("[kbd] key");
g_log->info(buf);
}
}
}
for (volatile int z=0;z<100;z++) {}
ticks++;
}
g_log->info("[kbd] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle; return 0;
}
-105
View File
@@ -1,105 +0,0 @@
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$Root = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $ScriptDir))
$Source = Join-Path $Root 'CODE\DKM\stub_driver\stub_driver.c'
$Manifest = Join-Path $Root 'SYSTEM\driver\manifest.json'
$SystemDriver = Join-Path $Root 'SYSTEM\driver'
$BuildDir = Join-Path $Root '.build_tmp\drivers'
function Find-Tool($Name, $ExtraNames = @()) {
$names = @($Name) + $ExtraNames
foreach ($n in $names) {
$cmd = Get-Command $n -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
}
$dirs = @(
'C:\Program Files\LLVM\bin',
'C:\Program Files (x86)\LLVM\bin',
'C:\LLVM\bin',
'D:\LLVM\bin',
'D:\Program Files\LLVM\bin',
'D:\Code\LLVM\bin',
'C:\msys64\clang64\bin',
'C:\msys64\mingw64\bin',
'C:\mingw64\bin'
)
foreach ($dir in $dirs) {
foreach ($n in $names) {
$candidate = Join-Path $dir $n
if (Test-Path $candidate) { return $candidate }
if (-not $n.EndsWith('.exe')) {
$candidateExe = Join-Path $dir "$n.exe"
if (Test-Path $candidateExe) { return $candidateExe }
}
}
}
return $null
}
function Get-DriverClassValue([string]$Class) {
switch ($Class) {
'platform' { return 1 }
'bus' { return 2 }
'interrupt' { return 3 }
'timer' { return 4 }
'console' { return 5 }
'storage' { return 6 }
'fs' { return 7 }
'fs-core' { return 8 }
'net' { return 9 }
'input' { return 10 }
'gpu' { return 11 }
default { return 0 }
}
}
$clang = Find-Tool 'clang'
$lld = Find-Tool 'ld.lld'
if (-not $clang) {
throw 'clang was not found. Install LLVM/Clang.'
}
if (-not $lld) {
throw 'ld.lld was not found. Install LLVM lld.'
}
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
$manifestData = Get-Content -Raw -LiteralPath $Manifest | ConvertFrom-Json
$count = 0
foreach ($stage in $manifestData.stages) {
foreach ($driver in $stage.drivers) {
$relative = [string]$driver.path
if (-not $relative.StartsWith('driver/')) {
throw "Unexpected driver path: $relative"
}
$destRelative = $relative.Substring('driver/'.Length).Replace('/', '\')
$dest = Join-Path $SystemDriver $destRelative
$destDir = Split-Path -Parent $dest
New-Item -ItemType Directory -Force -Path $destDir | Out-Null
$name = [string]$driver.name
$classValue = Get-DriverClassValue ([string]$driver.class)
$stageValue = [int]$stage.id
$flags = 0
if ([bool]$driver.required) { $flags = $flags -bor 1 }
if ($stageValue -le 1) { $flags = $flags -bor 2 }
$obj = Join-Path $BuildDir "$name.o"
$drv = Join-Path $BuildDir "$name.drv"
$driverNameDefine = 'DRIVER_NAME=' + $name
& $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-stack-protector -fno-pic -fno-pie -mno-red-zone -Wall -Wextra -O2 -c $Source -o $obj "-D$driverNameDefine" "-DDRIVER_CLASS=$classValue" "-DDRIVER_STAGE=$stageValue" "-DDRIVER_FLAGS=$flags"
if ($LASTEXITCODE -ne 0) { throw "failed to compile stub driver: $name" }
& $lld -r -o $drv $obj
if ($LASTEXITCODE -ne 0) { throw "failed to link stub driver: $name" }
Copy-Item -LiteralPath $drv -Destination $dest -Force
Write-Host "[drivers] installed $relative"
$count++
}
}
Write-Host "[drivers] installed $count DKM stub drivers"
-93
View File
@@ -1,93 +0,0 @@
#include <stdint.h>
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1
#ifndef DRIVER_NAME
#define DRIVER_NAME stub
#endif
#define DKM_STRINGIFY_INNER(x) #x
#define DKM_STRINGIFY(x) DKM_STRINGIFY_INNER(x)
#define DRIVER_NAME_STRING DKM_STRINGIFY(DRIVER_NAME)
#ifndef DRIVER_CLASS
#define DRIVER_CLASS 0
#endif
#ifndef DRIVER_STAGE
#define DRIVER_STAGE 0
#endif
#ifndef DRIVER_FLAGS
#define DRIVER_FLAGS 0
#endif
#ifndef DRIVER_PRIORITY
#define DRIVER_PRIORITY 0
#endif
struct dkm_driver_desc {
uint32_t magic;
uint16_t abi_version;
uint16_t desc_size;
const char *name;
const char *version;
const char *vendor;
uint32_t driver_class;
uint32_t stage;
uint32_t flags;
uint32_t priority;
const char *const *depends;
uint32_t depends_count;
const char *const *provides;
uint32_t provides_count;
uint64_t min_kernel_abi;
uint64_t feature_bits;
uint64_t reserved0;
uint64_t reserved1;
};
struct dkm_kernel_api;
struct dkm_driver_handle;
__attribute__((visibility("default")))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = DRIVER_NAME_STRING,
.version = "0.1.0-stub",
.vendor = "Deshab",
.driver_class = DRIVER_CLASS,
.stage = DRIVER_STAGE,
.flags = DRIVER_FLAGS,
.priority = DRIVER_PRIORITY,
.depends = 0,
.depends_count = 0,
.provides = 0,
.provides_count = 0,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) {
(void)api;
(void)handle;
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-192
View File
@@ -1,192 +0,0 @@
/* DKM Timer Driver - PIT (Programmable Interval Timer)
* Stage 0 platform driver, provides "timer".
* No IDT/IRQ required - uses PIT counter polling for busy-wait delay.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types (standalone, no kernel headers)
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
/* ---------------------------------------------------------------
* PIT I/O ports
* --------------------------------------------------------------- */
#define PIT_CH0_DATA 0x40
#define PIT_CH1_DATA 0x41
#define PIT_CH2_DATA 0x42
#define PIT_CMD 0x43
/* PIT frequency: 1.193182 MHz */
#define PIT_BASE_FREQ 1193182ULL
static const char *const g_provides[] = { "timer" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "timer",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 4, /* DKM_CLASS_TIMER */
.stage = 0,
.flags = 1, /* DKM_F_REQUIRED */
.priority = 0,
.depends = NULL,
.depends_count = 0,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* Inline assembly for port I/O (works in Ring0 SAS-R0)
* --------------------------------------------------------------- */
static __inline__ void outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u8 inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
/* ---------------------------------------------------------------
* PIT operations
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static void pit_set_channel(u8 channel, u8 mode, u16 count) {
u8 cmd = (u8)((channel << 6) | (3 << 4) | (mode << 1));
outb(PIT_CMD, cmd);
outb((u16)(PIT_CH0_DATA + channel), (u8)(count & 0xff));
outb((u16)(PIT_CH0_DATA + channel), (u8)((count >> 8) & 0xff));
}
/* Read back current counter value via latch command */
static u16 pit_read_counter(void) {
u8 cmd = 0xD0; /* read-back: channel 0, latch count, no status */
outb(PIT_CMD, cmd);
u8 lo = inb(PIT_CH0_DATA);
u8 hi = inb(PIT_CH0_DATA);
return (u16)(((u16)hi << 8) | lo);
}
/* Busy-wait delay using PIT channel 0 counter.
* count: PIT divisor (1193182 = ~1 second)
* The counter counts DOWN from 'count' to 0.
* We set it to 'count', then poll until it reaches near 0.
*/
static void pit_delay(u16 count) {
pit_set_channel(0, 0, count); /* mode 0: interrupt on terminal count */
u16 prev = pit_read_counter();
u64 loops = 0;
while (1) {
u16 cur = pit_read_counter();
if (cur > prev && loops > 10) {
break; /* counter wrapped → terminal count reached */
}
prev = cur;
loops++;
}
}
/* ---------------------------------------------------------------
* Driver entry points
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) {
return -1;
}
g_log = api->log;
g_log->info("[timer] PIT driver initializing");
/* Quick calibration: set PIT to max count, measure how many
* iterations of read_counter() happen before terminal count.
*/
pit_set_channel(0, 0, 0xFFFF); /* 0xFFFF = max, ~55ms at 1.193 MHz */
u16 prev = pit_read_counter();
u64 cal_loops = 0;
while (1) {
u16 cur = pit_read_counter();
cal_loops++;
if (cur > prev && cal_loops > 5) break;
prev = cur;
}
/* Expected: PIT_BASE_FREQ / 0xFFFF ≈ 18.2 Hz, period ≈ 55 ms */
g_log->info("[timer] PIT calibration complete");
g_log->info("[timer] PIT channel 0 operational");
/* Do a quick ~100ms delay demo */
g_log->info("[timer] running 100ms busy-wait demo");
pit_delay((u16)(PIT_BASE_FREQ / 10)); /* ~0.1 sec = 119318 counts */
g_log->info("[timer] 100ms delay done");
g_log->info("[timer] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-222
View File
@@ -1,222 +0,0 @@
/* DKM VFS Driver — Virtual File System layer
* Stage 2, required, depends on "bootfs", provides "vfs".
* Mounts bootfs modules as files, implements basic open/read.
* Demo: reads and displays first 64 bytes of manifest.json.
*/
#include <stdint.h>
/* ---------------------------------------------------------------
* DKM ABI types
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci_api;
const void *dma;
const void *vfs_api;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u8 _pad[56];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
static const char *const g_depends[] = { "bootfs" };
static const char *const g_provides[] = { "vfs" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "vfs",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 8, /* DKM_CLASS_FS_CORE */
.stage = 2,
.flags = 1,
.priority = 0,
.depends = g_depends,
.depends_count = 1,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ---------------------------------------------------------------
* Helpers
* --------------------------------------------------------------- */
static const struct dkm_log_api *g_log;
static void log_hex(const char *prefix, u64 val) {
static const char h[] = "0123456789abcdef";
char buf[19];
u32 p = 0;
buf[p++] = '0'; buf[p++] = 'x';
for (int i = 15; i >= 0; i--) buf[p++] = h[(val >> (i*4)) & 0xf];
buf[p] = 0;
g_log->info(prefix); g_log->info(buf);
}
static u64 min64(u64 a, u64 b) { return a < b ? a : b; }
static int path_match(const char *bp, const char *mp) {
/* strip leading '/' from bootloader path */
while (*bp == '/') bp++;
u64 i = 0;
while (bp[i] && mp[i] && bp[i] == mp[i]) i++;
return bp[i] == 0 && mp[i] == 0;
}
static int str_len(const char *s) {
int l = 0; while (s[l]) l++; return l;
}
/* ---------------------------------------------------------------
* Driver entry
* --------------------------------------------------------------- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
if (!rsp) { g_log->error("[vfs] no modules"); return -1; }
g_log->info("[vfs] mounting bootfs as root");
log_hex("[vfs] files=", rsp->module_count);
/* list all files */
for (u64 i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (!f || !f->path) continue;
g_log->info("[vfs] /");
g_log->info(f->path);
}
/* ---- demo: open & read /driver/manifest.json ---- */
g_log->info("[vfs] demo: open //driver/manifest.json");
struct limine_file *target = NULL;
for (u64 i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (f && f->path && path_match(f->path, "driver/manifest.json")) {
target = f; break;
}
}
if (!target) {
g_log->warn("[vfs] manifest.json not found");
} else {
log_hex("[vfs] size=", target->size);
g_log->info("[vfs] content (first 64 bytes):");
/* dump first 64 bytes as hex + ascii */
u64 n = min64(64, target->size);
const u8 *data = (const u8 *)target->address;
char line[80];
for (u64 off = 0; off < n; off += 16) {
u32 pos = 0;
for (u32 j = 0; j < 16 && off + j < n; j++) {
u8 b = data[off + j];
line[pos++] = "0123456789abcdef"[b >> 4];
line[pos++] = "0123456789abcdef"[b & 0xf];
line[pos++] = ' ';
}
/* ascii */
line[pos++] = ' ';
for (u32 j = 0; j < 16 && off + j < n; j++) {
u8 c = data[off + j];
line[pos++] = (c >= 32 && c < 127) ? (char)c : '.';
}
line[pos] = 0;
g_log->info(line);
}
}
g_log->info("[vfs] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}
-951
View File
@@ -1,951 +0,0 @@
/* DKM virtio-net Driver — virtio network device (legacy/transitional PCI)
* Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev".
*
* 数据路径实现(virtio spec 1.0 legacy interface,transitional device 1AF4:1001):
* - PCI 探测 → BAR0 legacy I/O 窗口
* - reset → ACKNOWLEDGE → DRIVER → feature negotiation(MAC/STATUS 子集)
* → FEATURES_OK → virtqueue 建立(RXQ0/TXQ1, legacy split-ring 布局)
* → DRIVER_OK
* - RX: 预填 32×2048B buffer 到 RXQ avail;rx_poll 收割 used ring,
* 剥 10B virtio_net_hdr 后把以太网帧交给上层协议栈,buffer 回收重填
* - TX: 8×2048B buffer 池(desc 索引与 buffer 1:1 映射),hdr+帧单描述符,
* QueueNotify kick,与 e1000 一致的同步等待完成(TSC 超时兜底)
* - boot 自测: 经真实 QEMU user-net 后端向 10.0.2.2 DHCP server 发 DISCOVER、
* 收 OFFER,一次跑通证明 TX/RX 全链路(DMA/notify/used ring)
* - IRQ 按 IDT vector(irq_line+0x20)注册做 ISR 统计;PIC 未解屏蔽时
* 自动退化为纯轮询,不依赖中断正确性
*/
#include "../dkm_shared.h"
/* virtio-specific PCI IDs and capability types (driver-local) */
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
#define PCI_VENDOR_VIRTIO 0x1AF4u
/* PCI device ID 约定见 virtio_is_net_device(): transitional net=0x1000, modern net=0x1041 */
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
#define VIRTIO_PCI_CAP_ISR_CFG 3u
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
#define VIRTIO_PCI_CAP_PCI_CFG 5u
/* ---- legacy I/O 寄存器偏移(virtio spec 1.0 §4.1.4, BAR0 I/O space) ---- */
#define VP_HOST_FEATURES 0x00u /* u32 RO: device feature bits 0..31 */
#define VP_GUEST_FEATURES 0x04u /* u32 WO: driver 接受的 feature 子集 */
#define VP_QUEUE_PFN 0x08u /* u32 WO: 队列物理地址 >> 12 */
#define VP_QUEUE_NUM 0x0Cu /* u16 RO: 队列容量(2 的幂) */
#define VP_QUEUE_SEL 0x0Eu /* u16 WO: 选择操作的队列 */
#define VP_QUEUE_NOTIFY 0x10u /* u16 WO: kick 指定队列 */
#define VP_STATUS 0x12u /* u8 RW: device status */
#define VP_ISR 0x13u /* u8 RO: ISR,读即清除 */
#define VP_NET_CFG_MAC 0x14u /* 6B RO: MAC(device config 区起始) */
/* ---- device status bits ---- */
#define VS_ACKNOWLEDGE 0x01u
#define VS_DRIVER 0x02u
#define VS_DRIVER_OK 0x04u
#define VS_FEATURES_OK 0x08u
/* ---- virtio-net feature bits(spec §5.1.6),只取最小子集 ---- */
#define VNET_F_MAC (1u << 5) /* device config 提供 MAC */
#define VNET_F_STATUS (1u << 16) /* device config 提供 link status */
/* ---- vring ---- */
#define VRING_DESC_F_WRITE 2u /* descriptor 由 device 写(RX) */
#define VNET_HDR_SIZE 10u /* 未协商 MRG_RXBUF → virtio_net_hdr 为 10 字节 */
#define RX_BUF_COUNT 32u /* RX 预填 buffer 数(不必填满整个队列) */
#define TX_BUF_COUNT 8u /* TX buffer 池大小 */
#define VNET_BUF_SIZE 2048u /* 10B hdr + 最大 1518B 以太网帧 */
/* split-ring 结构(legacy,x86 上字段为 guest 原生小端序) */
struct vring_desc {
u64 addr;
u32 len;
u16 flags;
u16 next;
};
struct vring_used_elem {
u32 id;
u32 len;
};
struct vring_avail {
u16 flags;
u16 idx;
u16 ring[];
};
struct vring_used {
u16 flags;
u16 idx;
struct vring_used_elem ring[];
};
/* 单个 virtqueue 的运行时视图。
* legacy 布局(spec §2.4.2):
* base+0 : descriptor table(16B × size)
* base+16*size : avail ring(4 + 2×size + 2B used_event)
* align4K(16*size + 6 + 2*size): used ring(4 + 8×size + 2B avail_event)
* 整段必须物理连续且 4KiB 对齐(QueuePFN 只接受页帧号)。 */
struct virtq {
u16 qsel; /* queue select 索引(0=RX, 1=TX) */
u16 size; /* device 报告的队列容量 */
u64 phys; /* 队列内存物理基址(页对齐) */
struct vring_desc *desc;
struct vring_avail *avail;
struct vring_used *used;
u16 last_used_idx; /* driver 侧 used ring 收割游标 */
};
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "netdev" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "virtio_net",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 9, /* DKM_CLASS_NET */
.stage = 3,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
static const struct dkm_log_api *g_log;
/* ---- 全局设备状态 ---- */
static u16 g_io_base; /* legacy I/O BAR 基址,0=未初始化 */
static struct virtq g_rxq;
static struct virtq g_txq;
static u8 *g_rx_buf[RX_BUF_COUNT];
static u64 g_rx_buf_phys[RX_BUF_COUNT];
static u8 *g_tx_buf[TX_BUF_COUNT];
static u64 g_tx_buf_phys[TX_BUF_COUNT];
static u8 g_tx_free[TX_BUF_COUNT]; /* 1=空闲可提交 */
static int g_ready; /* 数据路径就绪标志 */
static u8 g_mac[6];
/* ---- 统计计数器(串口可观测) ---- */
static u64 g_stat_tx_ok;
static u64 g_stat_tx_err;
static u64 g_stat_tx_timeout;
static u64 g_stat_rx_ok;
static u64 g_stat_rx_err;
static u64 g_stat_rx_overflow;
static u64 g_stat_irq_count;
/* ---- 自测缓冲区(静态 bss,避免内核栈压力) ---- */
static u8 g_st_frame[360]; /* DHCP DISCOVER 帧(342B) */
static u8 g_st_buf[2048]; /* RX 收割暂存 */
/* ---- 16-bit I/O port 访问(dkm_shared.h 只提供 8/32 位) ---- */
static __inline__ void vn_outw(u16 port, u16 value) {
__asm__ volatile ("outw %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u16 vn_inw(u16 port) {
u16 value;
__asm__ volatile ("inw %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
/* device DMA 内存屏障:描述符/ring 写入必须先于 idx 更新对设备可见。
* x86 store-store 有序,compiler fence + mfence 双保险(控制路径,开销可忽略)。 */
static __inline__ void vn_fence(void) {
__asm__ volatile("mfence" ::: "memory");
}
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
dkm_pci_write(bus, dev, func, reg, value);
}
static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read8(bus, dev, func, reg);
}
static void copy_bytes(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
static void zero_bytes(void *dst, u32 len) {
u8 *d = (u8 *)dst;
for (u32 i = 0; i < len; i++) d[i] = 0;
}
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static u32 dec_u32(u32 v, char *out) {
char tmp[10];
u32 n = 0;
if (v == 0) { out[0] = '0'; return 1; }
while (v && n < 10) { tmp[n++] = (char)('0' + (v % 10)); v /= 10; }
for (u32 i = 0; i < n; i++) out[i] = tmp[n - 1 - i];
return n;
}
/* 打印 IPv4 地址。ip 为主机序(高字节 = 第一个 octet,如 0x0A00020F = 10.0.2.15)。 */
static void log_ipv4(const char *prefix, u32 ip) {
char buf[24];
u32 p = 0;
for (u32 i = 0; i < 4; i++) {
u8 octet = (u8)(ip >> ((3 - i) * 8));
p += dec_u32(octet, buf + p);
if (i < 3) buf[p++] = '.';
}
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void log_mac(const char *prefix, const u8 mac[6]) {
static const char hex[] = "0123456789abcdef";
char buf[18];
u32 p = 0;
for (u32 i = 0; i < 6; i++) {
buf[p++] = hex[(mac[i] >> 4) & 0xf];
buf[p++] = hex[mac[i] & 0xf];
if (i < 5) buf[p++] = ':';
}
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* virtio-net PCI device ID 判定。
* QEMU 编号约定(virtio 1.0 spec §4.1.2.1 + QEMU hw/virtio/virtio-pci.c):
* - transitional/legacy: 0x1000=net, 0x1001=blk, 0x1002=balloon, ...
* (历史 table,不是 0x1000+virtio_id;virtio device id 由 PCI Subsystem ID 携带)
* - modern-only: 0x1040+virtio_id → net = 0x1041
* class code (0x02=Network) 作为第二判据,与 virtio-blk(0x1001/Storage) 区分。 */
static int virtio_is_net_device(u16 device_id) {
return (device_id == 0x1000u || device_id == 0x1041u) ? 1 : 0;
}
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor = (u16)(vd2 & 0xffff);
u16 device = (u16)(vd2 >> 16);
if (vendor != PCI_VENDOR_VIRTIO) continue;
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 class_code = (u8)(class_reg >> 24);
if (virtio_is_net_device(device) && class_code == 0x02) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
return 0;
}
}
}
}
return -1;
}
static const char *virtio_cap_name(u8 cfg_type) {
if (cfg_type == VIRTIO_PCI_CAP_COMMON_CFG) return "common_cfg";
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) return "notify_cfg";
if (cfg_type == VIRTIO_PCI_CAP_ISR_CFG) return "isr_cfg";
if (cfg_type == VIRTIO_PCI_CAP_DEVICE_CFG) return "device_cfg";
if (cfg_type == VIRTIO_PCI_CAP_PCI_CFG) return "pci_cfg";
return "unknown_cfg";
}
static u32 read_bar_raw(u8 bus, u8 dev, u8 func, u8 bar) {
return pci_read(bus, dev, func, (u8)(PCI_BAR0 + bar * 4));
}
static void log_bars(u8 bus, u8 dev, u8 func) {
for (u8 bar = 0; bar < 6; bar++) {
u32 raw = read_bar_raw(bus, dev, func, bar);
if (raw == 0 || raw == 0xffffffffu) continue;
log_hex("[virtio_net] BAR index=", bar);
log_hex("[virtio_net] raw=", raw);
if (raw & 1u) {
log_hex("[virtio_net] io base=", raw & 0xfffffffcU);
} else {
log_hex("[virtio_net] mmio base=", raw & 0xfffffff0U);
}
}
}
static void log_virtio_caps(u8 bus, u8 dev, u8 func) {
u8 cap = pci_read8(bus, dev, func, PCI_CAP_PTR) & 0xfc;
u32 guard = 0;
u32 found = 0;
while (cap && guard++ < 32) {
u8 cap_id = pci_read8(bus, dev, func, cap + 0);
u8 next = pci_read8(bus, dev, func, cap + 1) & 0xfc;
if (cap_id == 0x09) {
u8 cfg_type = pci_read8(bus, dev, func, cap + 3);
u8 bar = pci_read8(bus, dev, func, cap + 4);
u32 offset = pci_read(bus, dev, func, cap + 8);
u32 length = pci_read(bus, dev, func, cap + 12);
found++;
g_log->info("[virtio_net] virtio capability");
g_log->info(virtio_cap_name(cfg_type));
log_hex("[virtio_net] cap_offset=", cap);
log_hex("[virtio_net] cfg_type=", cfg_type);
log_hex("[virtio_net] bar=", bar);
log_hex("[virtio_net] offset=", offset);
log_hex("[virtio_net] length=", length);
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
u32 mult = pci_read(bus, dev, func, cap + 16);
log_hex("[virtio_net] notify_off_multiplier=", mult);
}
}
cap = next;
}
log_hex("[virtio_net] virtio cap count=", found);
if (!found) {
g_log->warn("[virtio_net] no modern virtio capabilities; transitional IO path expected");
}
}
/* ================= legacy device 初始化序列 ================= */
/* device status 寄存器读写(8 位) */
static void vdev_set_status(u8 status) {
dkm_outb((u16)(g_io_base + VP_STATUS), status);
}
static u8 vdev_get_status(void) {
return dkm_inb((u16)(g_io_base + VP_STATUS));
}
/* reset → ACKNOWLEDGE → DRIVER → feature 协商 → FEATURES_OK。
* 返回 0 成功;失败返回负数(调用方回退 stub 注册)。 */
static int vdev_negotiate(void) {
/* 1. reset: 写 0 并等待设备读回 0(spec §4.1.4.3 要求轮询确认) */
vdev_set_status(0);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (vdev_get_status() != 0) {
if (dkm_rdtsc() > deadline) {
g_log->error("[virtio_net] device reset timeout");
return -1;
}
__asm__ volatile("pause");
}
/* 2/3. ACKNOWLEDGE → DRIVER(status 位按序累积写入) */
vdev_set_status(VS_ACKNOWLEDGE);
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER);
/* 4. feature negotiation: 只取 MAC + STATUS 子集。
* 不接受 MRG_RXBUF/CTRL_VQ/CSUM/GSO 等复杂特性 →
* virtio_net_hdr 固定 10 字节、无控制队列、无卸载,数据路径最简。 */
u32 dev_feat = dkm_inl((u16)(g_io_base + VP_HOST_FEATURES));
log_hex("[virtio_net] device features=", dev_feat);
u32 subset = dev_feat & (VNET_F_MAC | VNET_F_STATUS);
dkm_outl((u16)(g_io_base + VP_GUEST_FEATURES), subset);
log_hex("[virtio_net] guest features=", subset);
/* 5. FEATURES_OK 并回读确认(spec §3.1.1)。
* 纯 legacy(pre-1.0)设备没有该位概念;QEMU transitional 实现 1.0 语义会置位。
* 未置位时按 legacy 兼容策略 warn 后继续(与 Linux legacy 驱动一致)。 */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK);
u8 st = vdev_get_status();
log_hex("[virtio_net] status after FEATURES_OK=", st);
if (!(st & VS_FEATURES_OK)) {
g_log->warn("[virtio_net] FEATURES_OK not echoed; continuing as pre-1.0 legacy device");
} else {
g_log->info("[virtio_net] FEATURES_OK confirmed");
}
return 0;
}
/* QueuePFN = 物理地址 >> 12(legacy 队列地址寄存器语义) */
static void vn_outl_pfn(const struct virtq *q) {
dkm_outl((u16)(g_io_base + VP_QUEUE_PFN), (u32)(q->phys >> 12));
}
/* 建立单个 virtqueue:读容量 → 计算 legacy 布局 → 分配连续页 → 写 QueuePFN。 */
static int vq_setup(const struct dkm_dma_api *dma, struct virtq *q, u16 qsel) {
q->qsel = qsel;
q->last_used_idx = 0;
vn_outw((u16)(g_io_base + VP_QUEUE_SEL), qsel);
q->size = vn_inw((u16)(g_io_base + VP_QUEUE_NUM));
if (q->size == 0) {
g_log->error("[virtio_net] queue size is 0");
return -1;
}
/* legacy 布局(spec §2.4.2):
* desc @ 0,avail @ 16*size,used @ 4KiB 对齐边界,整段物理连续。 */
u64 avail_off = 16ULL * q->size;
u64 used_off = (avail_off + 6ULL + 2ULL * q->size + 4095ULL) & ~4095ULL;
u64 total = used_off + 6ULL + 8ULL * q->size;
u64 pages = (total + 4095ULL) / 4096ULL;
struct dkm_dma_buffer buf;
/* 低 4G + 页对齐:QueuePFN 只接受 phys>>12,alloc_pages 返回已清零页 */
if (dma->alloc_pages(pages, 4096, 0x100000000ULL, &buf) != 0) {
g_log->error("[virtio_net] queue memory alloc failed");
return -2;
}
q->phys = buf.phys;
q->desc = (struct vring_desc *)buf.virt;
q->avail = (struct vring_avail *)((u8 *)buf.virt + avail_off);
q->used = (struct vring_used *)((u8 *)buf.virt + used_off);
vn_outl_pfn(q);
return 0;
}
/* kick 队列:写 queue index 到 QueueNotify */
static void vq_notify(const struct virtq *q) {
vn_outw((u16)(g_io_base + VP_QUEUE_NOTIFY), q->qsel);
}
/* 把描述符挂到 avail ring 并推进 idx。
* 顺序约束:desc 字段必须先写好(调用方责任),ring 写入 → fence → idx 推进。 */
static void vq_push_avail(struct virtq *q, u16 desc_idx) {
u16 slot = (u16)(q->avail->idx % q->size);
q->avail->ring[slot] = desc_idx;
vn_fence();
q->avail->idx = (u16)(q->avail->idx + 1u);
vn_fence();
}
/* 从 used ring 取一个完成项。返回 0=取到,1=空。 */
static int vq_pop_used(struct virtq *q, u16 *out_id, u32 *out_len) {
vn_fence();
if (q->used->idx == q->last_used_idx) return 1;
struct vring_used_elem *e = &q->used->ring[q->last_used_idx % q->size];
*out_id = (u16)e->id;
*out_len = e->len;
vn_fence();
q->last_used_idx = (u16)(q->last_used_idx + 1u);
return 0;
}
/* ================= RX 路径 ================= */
/* 回收 RX buffer:重置描述符并重新挂回 avail ring,保持 RX 水位。 */
static void rx_requeue(u16 id) {
g_rxq.desc[id].addr = g_rx_buf_phys[id];
g_rxq.desc[id].len = VNET_BUF_SIZE;
g_rxq.desc[id].flags = VRING_DESC_F_WRITE;
g_rxq.desc[id].next = 0;
vq_push_avail(&g_rxq, id);
vq_notify(&g_rxq);
}
/* 预填全部 RX buffer(单描述符覆盖 hdr+帧,device 可写)。 */
static int rx_prefill(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < RX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] rx buffer alloc failed");
log_hex("[virtio_net] filled=", i);
return -1;
}
g_rx_buf[i] = (u8 *)b.virt;
g_rx_buf_phys[i] = b.phys;
g_rxq.desc[i].addr = b.phys;
g_rxq.desc[i].len = VNET_BUF_SIZE;
g_rxq.desc[i].flags = VRING_DESC_F_WRITE;
g_rxq.desc[i].next = 0;
g_rxq.avail->ring[i] = (u16)i;
}
vn_fence();
g_rxq.avail->idx = (u16)RX_BUF_COUNT;
vn_fence();
log_hex("[virtio_net] rx buffers prefilled=", RX_BUF_COUNT);
return 0;
}
/* netdev rx_poll 回调(契约与 e1000 一致):
* 返回 0 且 *out_length>0 = 收到一帧;1 = 无数据;负数 = 错误。 */
static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx;
if (out_length) *out_length = 0;
if (!g_ready || !buffer || !out_length) return -1;
u16 id;
u32 total;
if (vq_pop_used(&g_rxq, &id, &total) != 0) return 1; /* 无完成项 */
/* 防御:id 越界或总长小于 hdr → 丢帧(id 无效时无法回收,只计数) */
if (id >= RX_BUF_COUNT) {
g_stat_rx_err++;
return -3;
}
if (total < VNET_HDR_SIZE) {
g_stat_rx_err++;
rx_requeue(id);
return -3;
}
u32 frame_len = total - VNET_HDR_SIZE; /* 剥掉 virtio_net_hdr */
if (frame_len > capacity) {
g_stat_rx_overflow++;
rx_requeue(id);
return -2;
}
copy_bytes(buffer, g_rx_buf[id] + VNET_HDR_SIZE, frame_len);
*out_length = frame_len;
rx_requeue(id);
g_stat_rx_ok++;
return 0;
}
/* ================= TX 路径 ================= */
/* 收割 TX used ring,把完成的 buffer 标回空闲。 */
static void tx_reclaim(void) {
u16 id;
u32 len;
while (vq_pop_used(&g_txq, &id, &len) == 0) {
(void)len; /* TX 的 used.len 无语义,仅 id 用于回收 */
if (id < TX_BUF_COUNT) g_tx_free[id] = 1;
}
}
static int tx_buffers_alloc(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] tx buffer alloc failed");
return -1;
}
g_tx_buf[i] = (u8 *)b.virt;
g_tx_buf_phys[i] = b.phys;
g_tx_free[i] = 1;
}
return 0;
}
/* netdev tx 回调:hdr(10B 全零,无卸载) + 帧单描述符提交,kick 后同步等待完成。
* 与 e1000 行为一致:TSC 1 秒超时兜底,避免上层协议栈无限阻塞。 */
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_ready || !packet || length == 0 || length + VNET_HDR_SIZE > VNET_BUF_SIZE) {
g_stat_tx_err++;
return -1;
}
tx_reclaim();
int slot = -1;
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
if (g_tx_free[i]) { slot = (int)i; break; }
}
if (slot < 0) {
g_stat_tx_err++;
g_log->warn("[virtio_net] tx ring full");
return -2;
}
u8 *buf = g_tx_buf[slot];
zero_bytes(buf, VNET_HDR_SIZE); /* virtio_net_hdr: flags/gso 全 0 */
copy_bytes(buf + VNET_HDR_SIZE, packet, length);
/* desc 索引与 buffer 1:1 映射,回收时 used.id 即 buffer 索引 */
g_txq.desc[slot].addr = g_tx_buf_phys[slot];
g_txq.desc[slot].len = length + VNET_HDR_SIZE;
g_txq.desc[slot].flags = 0; /* device 只读 */
g_txq.desc[slot].next = 0;
g_tx_free[slot] = 0;
vq_push_avail(&g_txq, (u16)slot);
vq_notify(&g_txq);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
tx_reclaim();
if (g_tx_free[slot]) {
g_stat_tx_ok++;
return 0;
}
__asm__ volatile("pause");
}
g_stat_tx_timeout++;
g_log->warn("[virtio_net] tx timeout");
log_hex("[virtio_net] txq used idx=", g_txq.used->idx);
log_hex("[virtio_net] txq last_used=", g_txq.last_used_idx);
return -3;
}
/* ================= IRQ(辅助统计,数据路径不依赖) ================= */
static int virtio_net_irq(u8 irq) {
(void)irq;
if (!g_io_base) return 0;
/* ISR 读即清除;bit0=queue used buffer 通知,bit1=config change */
u8 isr = dkm_inb((u16)(g_io_base + VP_ISR));
if (isr & 0x1u) g_stat_irq_count++;
return 0; /* 不 suppress EOI,由 IDT 发 PIC EOI */
}
/* ================= boot 自测:DHCP DISCOVER/OFFER ================= */
#define ST_XID 0x4B1D3A5Fu
#define ST_FRAME_LEN 342u /* 14 eth + 20 ip + 8 udp + 300 bootp */
/* 构造 DHCP DISCOVER(全手工大端字段,避免 packed struct 字节序歧义)。
* BOOTP 载荷补齐到 300 字节(RFC 1542 最小值,slirp DHCP server 容忍度最高)。 */
static void st_build_discover(const u8 mac[6]) {
u8 *f = g_st_frame;
zero_bytes(f, ST_FRAME_LEN);
/* Ethernet: 广播 DISCOVER */
for (u32 i = 0; i < 6; i++) f[i] = 0xff;
for (u32 i = 0; i < 6; i++) f[6 + i] = mac[i];
f[12] = 0x08; f[13] = 0x00; /* IPv4 */
/* IPv4 @14 */
u8 *ip = f + 14;
ip[0] = 0x45; /* v4, ihl=5 */
ip[1] = 0x00;
ip[2] = 0x01; ip[3] = 0x48; /* total len = 328 */
ip[4] = 0x00; ip[5] = 0x00; /* id */
ip[6] = 0x00; ip[7] = 0x00; /* flags/frag */
ip[8] = 64; /* ttl */
ip[9] = 17; /* UDP */
ip[10] = 0x00; ip[11] = 0x00; /* cksum 占位 */
/* src 0.0.0.0 @12..15 保持 0 */
ip[16] = 0xff; ip[17] = 0xff; ip[18] = 0xff; ip[19] = 0xff;
/* IP header checksum(标准 1's complement,大端语义直接按字节对累加) */
u32 sum = 0;
for (u32 i = 0; i < 20; i += 2) sum += ((u32)ip[i] << 8) | ip[i + 1];
while (sum >> 16) sum = (sum & 0xffffu) + (sum >> 16);
u16 ck = (u16)(~sum & 0xffffu);
ip[10] = (u8)(ck >> 8); ip[11] = (u8)ck;
/* UDP @34: 68 → 67,校验和置 0(IPv4 下合法) */
u8 *udp = f + 34;
udp[0] = 0x00; udp[1] = 68;
udp[2] = 0x00; udp[3] = 67;
udp[4] = 0x01; udp[5] = 0x34; /* len = 308 */
udp[6] = 0x00; udp[7] = 0x00;
/* BOOTP @42(固定 236B + magic 4B + options,pad 到 300B) */
u8 *bp = f + 42;
bp[0] = 1; /* op = BOOTREQUEST */
bp[1] = 1; /* htype = Ethernet */
bp[2] = 6; /* hlen */
bp[3] = 0; /* hops */
bp[4] = (u8)(ST_XID >> 24);
bp[5] = (u8)(ST_XID >> 16);
bp[6] = (u8)(ST_XID >> 8);
bp[7] = (u8)ST_XID;
/* secs @8..9 = 0 */
bp[10] = 0x80; bp[11] = 0x00; /* flags = broadcast */
/* ciaddr/yiaddr/siaddr/giaddr @12..27 = 0 */
for (u32 i = 0; i < 6; i++) bp[28 + i] = mac[i]; /* chaddr(16B,后 10B 为 0) */
/* sname @44..107, file @108..235 = 0 */
bp[236] = 0x63; bp[237] = 0x82; bp[238] = 0x53; bp[239] = 0x63; /* magic cookie */
u8 *opt = bp + 240;
opt[0] = 53; opt[1] = 1; opt[2] = 1; /* DHCP msg type = DISCOVER */
opt[3] = 55; opt[4] = 4; /* param request list */
opt[5] = 1; opt[6] = 3; opt[7] = 6; opt[8] = 15; /* subnet/router/dns/domain */
opt[9] = 255; /* end */
}
/* 解析帧是否为匹配 XID 的 DHCP OFFER。
* 返回 2=OFFER(*out_yiaddr 有效,主机序),5=ACK,0=无关帧。 */
static int st_parse_reply(const u8 *f, u32 len, u32 *out_yiaddr) {
if (len < 14 + 20 + 8 + 240) return 0;
if (f[12] != 0x08 || f[13] != 0x00) return 0; /* 非 IPv4 */
u32 ihl = (u32)(f[14] & 0x0f) * 4u;
if (ihl < 20) return 0;
if (f[14 + 9] != 17) return 0; /* 非 UDP */
u32 udp = 14 + ihl;
if (len < udp + 8 + 240) return 0;
u16 dport = (u16)(((u16)f[udp + 2] << 8) | f[udp + 3]);
if (dport != 68) return 0; /* 非 DHCP 回包 */
u32 bp = udp + 8;
if (f[bp] != 2) return 0; /* 非 BOOTREPLY */
u32 xid = ((u32)f[bp + 4] << 24) | ((u32)f[bp + 5] << 16) |
((u32)f[bp + 6] << 8) | f[bp + 7];
if (xid != ST_XID) return 0; /* XID 不匹配 */
if (f[bp + 236] != 0x63 || f[bp + 237] != 0x82 ||
f[bp + 238] != 0x53 || f[bp + 239] != 0x63) return 0;
u32 yiaddr = ((u32)f[bp + 16] << 24) | ((u32)f[bp + 17] << 16) |
((u32)f[bp + 18] << 8) | f[bp + 19];
/* options 区扫描 msg type(53) */
u32 i = bp + 240;
while (i < len) {
u8 o = f[i];
if (o == 255) break;
if (o == 0) { i++; continue; }
if (i + 1 >= len) break;
u8 olen = f[i + 1];
if (o == 53 && olen == 1 && i + 2 < len) {
u8 mtype = f[i + 2];
if (mtype == 2 || mtype == 5) {
*out_yiaddr = yiaddr;
return mtype;
}
return 0;
}
i += 2u + olen;
}
return 0;
}
/* boot 自测:经真实网络后端验证 TX(desc+notify+used 回收)与
* RX(avail 预填 + used 收割 + hdr 剥离)全链路。
* 任何失败只记日志,绝不阻塞/崩溃启动流程。 */
static void virtio_net_selftest(void) {
if (!g_ready) return;
g_log->info("[virtio_net] selftest begin: DHCP DISCOVER -> expect OFFER from backend");
st_build_discover(g_mac);
int txrc = virtio_net_tx(0, g_st_frame, ST_FRAME_LEN);
log_hex("[virtio_net] selftest tx rc=", (u64)(i64)txrc);
if (txrc != 0) {
g_log->warn("[virtio_net] selftest: TX path FAILED");
return;
}
g_log->info("[virtio_net] selftest: TX path ok (descriptor consumed by device)");
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
u32 frames = 0;
while (dkm_rdtsc() < deadline) {
u32 flen = 0;
int rc = virtio_net_rx_poll(0, g_st_buf, sizeof(g_st_buf), &flen);
if (rc == 0 && flen > 0) {
frames++;
u32 ethertype = ((u32)g_st_buf[12] << 8) | g_st_buf[13];
log_hex("[virtio_net] selftest rx frame len=", flen);
log_hex("[virtio_net] ethertype=", ethertype);
u32 yiaddr = 0;
int t = st_parse_reply(g_st_buf, flen, &yiaddr);
if (t == 2) {
log_ipv4("[virtio_net] selftest: DHCP OFFER yiaddr=", yiaddr);
g_log->info("[virtio_net] selftest PASS: RX path ok (frame from real backend)");
return;
}
}
__asm__ volatile("pause");
}
g_log->warn("[virtio_net] selftest: no DHCP OFFER within 2000ms");
log_hex("[virtio_net] selftest rx frames seen=", frames);
}
/* ================= stub(初始化失败时的安全占位) =================
* 严格错误策略插桩:占位不再静默丢包/静默无数据——一旦被上层调用
* (即有人真的把流量交给未协商 virtqueue 的设备),立即莲花 panic。
* 正常路径 netman 因 flags 缺 LINK_UP 不会选中本驱动,stub 不触发。 */
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
(void)ctx; (void)packet;
log_hex("[virtio_net] tx stub hit, len=", length);
g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)");
return -1;
}
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity;
g_log->panic("VNET-E02 RX VIRTQUEUE NOT IMPLEMENTED (stub rx called)");
if (out_length) *out_length = 0;
return 0;
}
/* ================= driver 入口 ================= */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
/* 实机要求: 在驱动初始化开头校准 TSC, 为后续时序提供准确计时 */
dkm_tsc_calibrate();
g_log->info("[virtio_net] init begin");
u8 bus = 0;
u8 dev = 0;
u8 func = 0;
if (virtio_net_find(&bus, &dev, &func) != 0) {
g_log->warn("[virtio_net] virtio-net device not found");
g_log->info("[virtio_net] driver ready");
return 0;
}
g_log->info("[virtio_net] virtio-net device found");
log_hex("[virtio_net] bus=", bus);
log_hex("[virtio_net] dev=", dev);
log_hex("[virtio_net] func=", func);
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor = (u16)(vd & 0xffff);
u16 device = (u16)(vd >> 16);
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 revision = (u8)(class_reg & 0xff);
log_hex("[virtio_net] vendor=", vendor);
log_hex("[virtio_net] device=", device);
log_hex("[virtio_net] revision=", revision);
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[virtio_net] PCI command=", command);
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[virtio_net] PCI IRQ line=", irq_line);
log_bars(bus, dev, func);
log_virtio_caps(bus, dev, func);
/* ---- legacy I/O BAR 检查:transitional 设备 BAR0 必须是 I/O space ---- */
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
int datapath_ok = 0;
if (!(bar0 & 1u)) {
g_log->warn("[virtio_net] BAR0 is not I/O space (modern-only device?); legacy path unavailable");
} else if (!api->dma || !api->dma->alloc_pages) {
g_log->warn("[virtio_net] dma API unavailable");
} else {
g_io_base = (u16)(bar0 & 0xfffcU);
log_hex("[virtio_net] legacy io_base=", g_io_base);
if (vdev_negotiate() != 0) {
g_log->warn("[virtio_net] feature negotiation failed");
} else if (vq_setup(api->dma, &g_rxq, 0) != 0) {
g_log->warn("[virtio_net] RXQ setup failed");
} else if (vq_setup(api->dma, &g_txq, 1) != 0) {
g_log->warn("[virtio_net] TXQ setup failed");
} else if (rx_prefill(api->dma) != 0) {
g_log->warn("[virtio_net] RX prefill failed");
} else if (tx_buffers_alloc(api->dma) != 0) {
g_log->warn("[virtio_net] TX buffer alloc failed");
} else {
log_hex("[virtio_net] rxq size=", g_rxq.size);
log_hex("[virtio_net] rxq phys=", g_rxq.phys);
log_hex("[virtio_net] txq size=", g_txq.size);
log_hex("[virtio_net] txq phys=", g_txq.phys);
/* MAC: device config @ io_base+0x14(协商 MAC feature 后有效) */
for (u32 i = 0; i < 6; i++) {
g_mac[i] = dkm_inb((u16)(g_io_base + VP_NET_CFG_MAC + i));
}
u8 mac_sum = 0;
for (u32 i = 0; i < 6; i++) mac_sum |= g_mac[i];
if (mac_sum == 0 || (g_mac[0] & 1u)) {
/* 全 0 或组播位异常 → fallback */
g_mac[0] = 0x52; g_mac[1] = 0x54; g_mac[2] = 0x00;
g_mac[3] = 0x12; g_mac[4] = 0x34; g_mac[5] = 0x57;
g_log->warn("[virtio_net] invalid device MAC, using fallback");
}
log_mac("[virtio_net] MAC=", g_mac);
/* DRIVER_OK:设备开始处理队列;随后 kick RXQ 让设备吃掉预填 buffer */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK | VS_DRIVER_OK);
vq_notify(&g_rxq);
g_ready = 1;
datapath_ok = 1;
g_log->info("[virtio_net] DRIVER_OK set; virtqueues live");
}
}
/* ---- IRQ 注册(辅助统计;数据路径为纯轮询,不依赖中断) ----
* 注意: kernel irq_register 按 IDT vector 索引(PIC IRQn → vector 0x20+n),
* 传裸 IRQ 号会落到异常向量槽位永不触发(e1000 当前如此)。
* PIC 未解屏蔽该线时自动退化为纯轮询。 */
if (datapath_ok && api->irq_register && irq_line > 0 && irq_line < 16) {
api->irq_register((u8)(irq_line + 0x20u), (void *)(uintptr_t)virtio_net_irq);
g_log->info("[virtio_net] IRQ handler registered (vector = irq + 0x20)");
} else {
g_log->info("[virtio_net] IRQ not registered (polling mode)");
}
/* ---- 注册 netdev ---- */
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "virtio-net";
for (u32 i = 0; i < 6; i++) netdev.mac[i] = g_mac[i];
netdev.ctx = 0;
if (datapath_ok) {
netdev.flags = DKM_NET_F_LINK_UP | DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
netdev.tx = virtio_net_tx;
netdev.rx_poll = virtio_net_rx_poll;
} else {
/* 安全占位:flags 仅 LINK_UP,netman 不会选它跑 DHCP(与历史行为一致) */
netdev.flags = DKM_NET_F_LINK_UP;
netdev.tx = virtio_net_tx_stub;
netdev.rx_poll = virtio_net_rx_poll_stub;
}
int net_index = api->net->register_device(&netdev);
log_hex("[virtio_net] netdev registered, index=", (u64)(i64)net_index);
} else {
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
}
/* ---- boot 自测(证明 TX/RX 全链路,失败仅记日志) ---- */
virtio_net_selftest();
log_hex("[virtio_net] stat tx_ok=", g_stat_tx_ok);
log_hex("[virtio_net] stat rx_ok=", g_stat_rx_ok);
g_log->info("[virtio_net] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* reset 设备:停止一切 DMA 与队列活动 */
if (g_io_base) {
vdev_set_status(0);
}
g_ready = 0;
g_io_base = 0;
return 0;
}
-247
View File
@@ -1,247 +0,0 @@
/* DKM WLAN Driver - wireless netdev framework (stage 3, optional)
*
* 阶段 1(本文件现状):
* - PCI 全总线扫描 class 02:80(无线网络控制器),识别常见芯片家族
* (Intel AX2xx/72xx、Realtek RTL8x、Atheros AR9x、Broadcom BCM43xx)。
* - 检测到真实硬件 → 注册 wireless netdev(attest net_wlan → NET-E01 通过)。
* mac 暂为全零:原生芯片后端(NVM/EEPROM 读取)未实现,且数据面回调
* 全部走 panic —— 零 mac 不会泄露到链路上。
* - scan/connect/disconnect 回调 = 严格错误策略 panic 骨架:
* 原生芯片驱动(ath9k/iwlwifi/rtw 路线)与 linuxguest 后端未落地,
* 调用即莲花 panic(NETWL-E10/E11),坐标清晰,不伪装成功。
* - QEMU(无 wlan 卡)不注册任何设备 → attest MISSING 由 FUCK
* [attest] require_net_wlan=0 白名单豁免(设计内,见 CLAUDE.md §3)。
*
* 阶段 2(路线图):芯片家族原生后端逐家实现;linuxguest 后端
* (Linux guest 加载发行驱动 → 扫描/关联状态经共享内存回传)作为
* 芯片覆盖面不足时的兜底数据源。
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
static const char *const g_depends[] = {"pci"};
static const char *const g_provides[] = {"wlan"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="wlan",.version="0.1.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=1,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
};
/* ---- 已知无线芯片 device ID 表(记录芯片名;原生后端按家族落地) ---- */
struct wlan_device_id {
u16 vendor;
u16 did;
const char *name;
const char *family; /* 目标原生后端:ath9k/iwlwifi/rtw/brcmfmac */
};
static const struct wlan_device_id g_wlan_ids[] = {
/* Intel (8086) — iwlwifi 家族 */
{0x8086, 0x2723, "AX200", "iwlwifi"},
{0x8086, 0x2725, "AX210", "iwlwifi"},
{0x8086, 0x51F1, "AX211", "iwlwifi"},
{0x8086, 0x02F0, "AX201", "iwlwifi"},
{0x8086, 0xA0F0, "AX201-LP", "iwlwifi"},
{0x8086, 0x24FD, "8265", "iwlwifi"},
{0x8086, 0x24F3, "8260", "iwlwifi"},
{0x8086, 0x095A, "7265", "iwlwifi"},
{0x8086, 0x24FB, "3168", "iwlwifi"},
/* Realtek (10EC) — rtw 家族 */
{0x10EC, 0x8852, "RTL8852AE","rtw89"},
{0x10EC, 0xC822, "RTL8822CE","rtw88"},
{0x10EC, 0xC82F, "RTL8822CE","rtw88"},
{0x10EC, 0x8821, "RTL8821AE","rtw88"},
{0x10EC, 0x818B, "RTL8192EE","rtw88"},
/* Atheros (168C) — ath9k 家族(无固件 blob,原生后端首选路线) */
{0x168C, 0x002B, "AR9285", "ath9k"},
{0x168C, 0x002D, "AR9287", "ath9k"},
{0x168C, 0x002E, "AR9287-2", "ath9k"},
{0x168C, 0x0034, "AR9462", "ath9k"},
{0x168C, 0x0036, "AR9565", "ath9k"},
{0x168C, 0x003C, "AR9462-2", "ath9k"},
/* MediaTek (14C3) — 由 mt7920.drv 独占,wlan 不注册 */
{0x14C3, 0x7920, "MT7920", "mt7920"},
/* Broadcom (14E4) — brcmfmac 家族 */
{0x14E4, 0x43A0, "BCM4360", "brcmfmac"},
{0x14E4, 0x4365, "BCM43142", "brcmfmac"},
{0x14E4, 0x43B1, "BCM4352", "brcmfmac"},
{0x14E4, 0x43B2, "BCM4352-2","brcmfmac"},
};
/* ---- PCI 访问(与 e1000 相同的 CF8/CFC 端口方式) ---- */
#define PCI_ADDR_PORT 0xCF8u
#define PCI_DATA_PORT 0xCFCu
static inline void outl(u16 port, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(port)); }
static inline u32 inl(u16 port) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(port)); return v; }
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
u32 addr = (1u << 31) | ((u32)bus << 16) | ((u32)dev << 11)
| ((u32)func << 8) | (reg & 0xFCu);
outl(PCI_ADDR_PORT, addr);
return inl(PCI_DATA_PORT);
}
/* ---- 扫描结果:阶段 1 无数据(scan 回调 panic),仅保留注册骨架 ---- */
static const struct wlan_device_id *g_found; /* 命中的芯片表项 */
static u8 g_found_bus, g_found_dev, g_found_func;
/* ---- 串口直出助手(e1000 同款) ---- */
#define COM1 0x3F8
static void raw_log(const char *s) {
while (*s) {
char c = *s++;
if (c == '\n') {
dkm_serial_wait_tx(COM1);
dkm_outb(COM1, '\r');
}
dkm_serial_wait_tx(COM1);
dkm_outb(COM1, (u8)c);
}
}
static void raw_hex(const char *prefix, u64 v) {
raw_log(prefix);
static const char hx[] = "0123456789abcdef";
char buf[19]; buf[0]='0'; buf[1]='x';
for (int i = 0; i < 16; i++) buf[2+i] = hx[(v >> ((15-i)*4)) & 0xf];
buf[18] = 0;
raw_log(buf);
raw_log("\n");
}
static void zero_bytes(void *p, u64 n) {
u8 *b = (u8 *)p;
while (n--) *b++ = 0;
}
/* ---- 严格错误策略骨架:原生/网守后端未落地,调用即 panic ---- */
static struct dkm_log_api *g_log;
/* strict 模式 g_log->panic 不返回;fallback 模式返回,回调随后报错返回 */
static void wlan_panic(const char *sym, const char *detail) {
g_log->panic(sym);
(void)detail;
}
static int wlan_scan_start(void *ctx) {
(void)ctx;
wlan_panic("NETWL-E10 WIFI SCAN NOT IMPLEMENTED",
"native chip backend pending (ath9k/iwlwifi/rtw) - linuxguest backend pending");
return -1;
}
static int wlan_scan_count(void *ctx) {
(void)ctx;
wlan_panic("NETWL-E10 WIFI SCAN NOT IMPLEMENTED",
"native chip backend pending");
return 0;
}
static int wlan_scan_result(void *ctx, u32 n, struct dkm_net_scan_result *out) {
(void)ctx; (void)n; (void)out;
wlan_panic("NETWL-E10 WIFI SCAN NOT IMPLEMENTED",
"native chip backend pending");
return -1;
}
static int wlan_connect(void *ctx, const char *ssid, const char *password) {
(void)ctx; (void)ssid; (void)password;
wlan_panic("NETWL-E11 WLAN CONNECT NOT IMPLEMENTED",
"802.11 auth/assoc + WPA handshake pending (native backend)");
return -1;
}
static int wlan_disconnect(void *ctx) {
(void)ctx;
wlan_panic("NETWL-E12 WLAN DISCONNECT NOT IMPLEMENTED",
"native chip backend pending");
return -1;
}
static int wlan_is_wireless(void *ctx) {
(void)ctx;
return 1;
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = (struct dkm_log_api *)api->log;
dkm_instr_init(api);
g_log->info("[wlan] driver_init entered");
/* PCI 全总线扫描:class 02 subclass 80 = 无线网络控制器。
* 匹配已知 device ID 表记录芯片家族;未匹配但 class 命中的也
* 登记(未知芯片 → 原生后端需新增设备表项)。 */
for (u32 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u8 func = 0;
u32 vd = pci_read((u8)bus, dev, func, 0x00);
if ((vd & 0xffff) == 0xffff) continue; /* 空设备 */
u32 class_reg = pci_read((u8)bus, dev, func, 0x08);
u8 class_code = (u8)(class_reg >> 24);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
if (class_code != 0x02 || subclass != 0x80) continue; /* 非无线控制器 */
u16 vendor = (u16)(vd & 0xffff);
u16 did = (u16)(vd >> 16);
if (vendor == 0x14C3 && did == 0x7920) {
g_log->info("[wlan] MT7920 left to mt7920.drv");
continue;
}
const struct wlan_device_id *hit = 0;
for (u32 i = 0; i < sizeof(g_wlan_ids)/sizeof(g_wlan_ids[0]); i++) {
if (g_wlan_ids[i].vendor == vendor && g_wlan_ids[i].did == did) {
hit = &g_wlan_ids[i]; break;
}
}
g_found_bus = (u8)bus; g_found_dev = dev; g_found_func = func;
g_log->info("[wlan] wireless controller detected:");
g_log->info(hit ? hit->name : "(unknown chip)");
g_log->info(hit ? hit->family : "unknown-family");
g_found = hit;
/* 多功能设备继续扫 func>0;命中即注册并返回(单 wlan 卡场景) */
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "wlan0";
/* mac 全零:原生芯片后端(NVM/OTP 读取)未实现。
* 数据面回调全 panic —— 零 mac 不会出现在链路上。 */
netdev.flags = 0; /* 无 LINK_UP/TX_READY:后端未落地 */
netdev.ctx = 0;
netdev.scan_start = wlan_scan_start;
netdev.scan_count = wlan_scan_count;
netdev.scan_result = wlan_scan_result;
netdev.is_wireless = wlan_is_wireless;
netdev.connect = wlan_connect;
netdev.disconnect = wlan_disconnect;
int net_index = api->net->register_device(&netdev);
raw_hex("[wlan] wireless netdev registered, index=", (u64)(i32)net_index);
} else {
g_log->warn("[wlan] net API unavailable");
}
return 0;
}
}
g_log->info("[wlan] no wireless controller found (attest: net_wlan exempted)");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
g_found = 0;
return 0;
}
-2158
View File
@@ -1,2158 +0,0 @@
/* DKM xHCI Driver — USB xHCI host controller + Mass Storage Class (BOT/SCSI)
*
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
*
* Architecture:
* 1. PCI discovery: class=0x0C/03/30 (Serial Bus / USB / xHCI)
* 2. MMIO mapping: 64-bit BAR0 via mm_map_mmio or HHDM fallback
* 3. Controller init: BIOS handoff, HC reset, parse capabilities
* 4. Ring setup: Command Ring, Event Ring, ERST, DCBAA, Scratchpad
* 5. Port scan: detect connected devices, port reset
* 6. USB enumeration: Enable Slot, Address Device, GET_DESCRIPTOR
* 7. MSC protocol: BOT (Bulk-Only Transport) + SCSI commands
* 8. Block provider: register usb0 via kernel_api.block
*
* Synchronous polling model (no IRQ/MSI in first version).
* All hardware waits use TSC deadline — no infinite loops.
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(xhci_pci_found);
DKM_STAT_DECL(xhci_port_reset_ok);
DKM_STAT_DECL(xhci_msc_found);
DKM_STAT_DECL(xhci_block_reads);
DKM_STAT_DECL(xhci_block_writes);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "xhci",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ================================================================
* Constants — PCI, xHCI registers, TRB types, USB
* ================================================================ */
#define XHCI_CLASS_SERIAL 0x0C
#define XHCI_SUBCLASS_USB 0x03
#define XHCI_PROGIF_XHCI 0x30
/* Capability Registers (MMIO + 0x00) */
#define XHCI_CAP_HCSPARAMS1 0x00
#define XHCI_CAP_HCSPARAMS2 0x04
#define XHCI_CAP_HCSPARAMS3 0x08
#define XHCI_CAP_HCCPARAMS1 0x0C
#define XHCI_CAP_DBOFF 0x14
#define XHCI_CAP_RTSOFF 0x18
/* Operational Registers (MMIO + cap_length) */
#define XHCI_OP_USBCMD 0x00
#define XHCI_OP_USBSTS 0x04
#define XHCI_OP_PAGESIZE 0x08
#define XHCI_OP_DNCTRL 0x14
#define XHCI_OP_CRCR_LO 0x18
#define XHCI_OP_CRCR_HI 0x1C
#define XHCI_OP_DCBAAP_LO 0x30
#define XHCI_OP_DCBAAP_HI 0x34
#define XHCI_OP_CONFIG 0x38
/* Port Registers (MMIO + 0x400 + port * 0x10) */
#define XHCI_PORT_BASE_OFF 0x400
#define XHCI_PORT_STRIDE 0x10
#define XHCI_PORT_SC 0x00
/* Runtime Registers (MMIO + rtsoff) */
#define XHCI_RT_MFINDEX 0x00
#define XHCI_RT_IR0_IMAN 0x20
#define XHCI_RT_IR0_IMOD 0x24
#define XHCI_RT_IR0_ERSTSZ 0x28
#define XHCI_RT_IR0_ERSTBA_LO 0x30
#define XHCI_RT_IR0_ERSTBA_HI 0x34
#define XHCI_RT_IR0_ERDP_LO 0x38
#define XHCI_RT_IR0_ERDP_HI 0x3C
/* ERDP bits: bit3 = Event Handler Busy (软件写 1 清挂起) */
#define ERDP_EHB (1u << 3)
/* USBCMD bits */
#define USBCMD_RS (1u << 0)
#define USBCMD_RST (1u << 1)
#define USBCMD_INTE (1u << 2)
/* USBSTS bits */
#define USBSTS_HCH (1u << 0)
#define USBSTS_EINT (1u << 3)
#define USBSTS_CNR (1u << 11)
/* PORTSC bits */
#define PORTSC_CCS (1u << 0)
#define PORTSC_PED (1u << 1)
#define PORTSC_OCA (1u << 3)
#define PORTSC_PR (1u << 4)
#define PORTSC_PLS_SHIFT 5
#define PORTSC_PLS_MASK 0xF
/* Port Speed 字段在 PORTSC bits[13:10](xHCI 1.2 §5.4.8)。
* BUG-FIX: 原实现 shift=17/mask=0x7 是错的——QEMU 用 10:13,
* 误读导致 SuperSpeed(4) 被当成 FULL(1),EP0 MPS 错误。 */
#define PORTSC_SPEED_SHIFT 10
#define PORTSC_SPEED_MASK 0xF
/* TRB types */
#define TRB_TYPE_NORMAL 0x01
#define TRB_TYPE_SETUP_STAGE 0x02
#define TRB_TYPE_DATA_STAGE 0x03
#define TRB_TYPE_STATUS_STAGE 0x04
#define TRB_TYPE_LINK 0x06
#define TRB_TYPE_ENABLE_SLOT 0x09
#define TRB_TYPE_DISABLE_SLOT 0x0A
#define TRB_TYPE_ADDRESS_DEV 0x0B
#define TRB_TYPE_CONFIGURE_EP 0x0C
#define TRB_TYPE_NOOP 0x17
/* Event TRB types — xHCI 1.2 spec Table 6-28 (TRB Type field [15:10]).
* 规范值:Transfer=0x20, Command Completion=0x21, Port Status Change=0x22。
* BUG-FIX: 原定义 0x01/0x02/0x21 与规范冲突——CMD_COMPLETE(0x21) 被
* 误判为 PORT_CHANGE 事件消费,导致 Enable Slot 等命令事件环被提前
* 清空,后续命令全部超时(QEMU hcd-xhci.c xhci_write_event 佐证)。 */
#define EVT_TYPE_TRANSFER 0x20 /* Transfer Event */
#define EVT_TYPE_CMD_COMPLETE 0x21 /* Command Completion Event */
#define EVT_TYPE_PORT_CHANGE 0x22 /* Port Status Change Event */
/* TRB control field helpers */
#define TRB_TYPE_SHIFT 10
#define TRB_TYPE_MASK 0x3F
#define TRB_IOC (1u << 5) /* Interrupt On Completion */
#define TRB_CH (1u << 6) /* Chain bit */
#define TRB_ENT (1u << 7) /* Evaluate Next TRB (for Link) */
#define TRB_ISP (1u << 8) /* Interrupt on Short Packet */
#define TRB_CYCLE (1u << 0)
#define TRB_TC (1u << 1) /* Toggle Cycle (for Link) */
/* Setup Stage TRB direction */
#define SETUP_DIR_OUT 0
#define SETUP_DIR_IN 1
/* USB speeds */
#define USB_SPEED_FULL 1
#define USB_SPEED_LOW 2
#define USB_SPEED_HIGH 3
#define USB_SPEED_SUPER 4
/* Endpoint types for EP Context */
#define EP_TYPE_CTRL_OUT 0
#define EP_TYPE_CTRL_IN 4
#define EP_TYPE_BULK_OUT 2
#define EP_TYPE_BULK_IN 6
/* Slot states */
#define SLOT_STATE_DISABLED 0
#define SLOT_STATE_ENABLED 1
#define SLOT_STATE_ADDRESSED 2
#define SLOT_STATE_CONFIGURED 3
/* Ring sizes */
#define CMD_RING_SIZE 256
#define EVT_RING_SIZE 256
#define XFER_RING_SIZE 256
/* xHCI MMIO mapping size (64 KB covers most controllers) */
#define XHCI_MAP_SIZE 0x10000
/* BOT (Bulk-Only Transport) signatures */
#define BOT_CBW_SIG 0x43425355u /* "USBC" */
#define BOT_CSW_SIG 0x53425355u /* "USBS" */
/* SCSI opcodes */
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
#define SCSI_READ_CAPACITY10 0x25
#define SCSI_READ10 0x28
#define SCSI_WRITE10 0x2A
/* ================================================================
* Data structures
* ================================================================ */
typedef struct {
u64 parameter; /* DW0-DW1 */
u32 status; /* DW2 */
u32 control; /* DW3 */
} xhci_trb;
/* TRB Ring */
typedef struct {
struct dkm_dma_buffer dma;
u32 size; /* TRB count */
u32 enqueue; /* producer index */
u32 ccs; /* producer Cycle State */
} xhci_ring;
/* ERST entry */
typedef struct {
u64 seg_addr;
u32 seg_size;
u32 rsvd;
} xhci_erst_entry;
/* MSC device state */
typedef struct {
u8 slot_id;
u8 speed;
u8 bulk_out_ep; /* EP number for Bulk OUT */
u8 bulk_in_ep; /* EP number for Bulk IN */
u16 bulk_out_mps; /* Max Packet Size */
u16 bulk_in_mps; /* Max Packet Size */
u32 cbw_tag; /* BOT tag counter */
u64 sector_count;
u32 sector_size;
int ready;
} xhci_msc_dev;
/* ================================================================
* Global state
* ================================================================ */
static const struct dkm_kernel_api *g_api;
static const struct dkm_log_api *g_log;
static volatile u8 *g_mmio; /* MMIO base virtual address */
static u32 g_cap_length; /* Capability register space length */
static u32 g_doorbell_off; /* Doorbell register offset */
static u32 g_runtime_off; /* Runtime register space offset */
static u32 g_max_slots;
static u32 g_max_ports;
static u32 g_max_intrs;
static u32 g_ctx_size; /* 32 or 64 bytes */
static u32 g_page_size; /* controller page size in bytes */
static u32 g_spb_count; /* scratchpad buffer count */
static u32 g_port_base; /* absolute MMIO offset of port register set (cap_length + 0x400) */
static u32 g_ppc; /* Per-Port Power Control (HCCPARAMS1 bit 3) */
static xhci_ring g_cmd_ring;
static xhci_ring g_evt_ring;
static u32 g_evt_ccs; /* consumer cycle state for event ring */
static struct dkm_dma_buffer g_erst; /* ERST DMA buffer */
static struct dkm_dma_buffer g_dcbaa; /* DCBAA DMA buffer */
static struct dkm_dma_buffer g_sp_bufs; /* Scratchpad buffer array */
/* Scratchpad individual pages — max 32 scratchpads for safety */
static struct dkm_dma_buffer g_sp_pages[32];
/* Per-device DMA buffers */
static struct dkm_dma_buffer g_input_ctx; /* Input Context */
static struct dkm_dma_buffer g_dev_ctx; /* Device Context (output) */
static xhci_ring g_ep0_ring; /* EP0 control transfer ring */
static xhci_ring g_bulk_out_ring;
static xhci_ring g_bulk_in_ring;
static struct dkm_dma_buffer g_xfer_buf; /* Data transfer bounce buffer */
static struct dkm_dma_buffer g_csw_buf; /* CSW receive buffer */
static xhci_msc_dev g_msc;
/* PCI device location */
static u8 g_pci_bus, g_pci_dev, g_pci_func;
/* ================================================================
* Utility functions
* ================================================================ */
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--)
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ================================================================
* Framebuffer 阶段诊断(实机无串口时屏幕直接显示 xhci 失败点)
* 失败时整屏填背景色 + 中央画大号两位十六进制状态码,然后 halt。
* 码表(调用点注释中注明):
* 红底 0x01..0x07 基础设施失败(PCI/MMIO/caps/reset/ring/start)
* 红底 0x11..0x14 PCI 自检细分(见 xhci_pci_diag_code)
* 黄底 0x08 所有端口无设备可枚举
* 黄底 0x0A..0x0F 枚举失败(slot/address/descriptor/configure/scsi)
* 依赖 dkm_kernel_api.fb_* 字段(32bpp,与 console_fb 一致)。
* ================================================================ */
static const u8 g_fb_font_hex[16][8] = {
{0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 0 */
{0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
{0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
{0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
{0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
{0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
{0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
{0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00}, /* 7 */
{0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
{0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
{0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
{0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
{0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
{0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
};
static void fb_draw_char(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, int ch) {
const u8 *g = g_fb_font_hex[ch & 0xF];
for (int row = 0; row < 8; row++) {
u8 bits = g[row];
for (int col = 0; col < 8; col++) {
if (!(bits & (0x80u >> col))) continue;
for (int sy = 0; sy < scale; sy++) {
u32 *line = (u32 *)((u8 *)fb + (u64)(y0 + row * scale + sy) * pitch);
for (int sx = 0; sx < scale; sx++)
line[x0 + col * scale + sx] = color;
}
}
}
}
static void fb_diag_show(const struct dkm_kernel_api *api, u32 bg, u32 code) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return; /* 无 framebuffer 时退化为仅串口日志 */
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
int scale = 8;
int cw = 8 * scale;
int cx = (int)(w / 2) - cw; /* 两位数字整体居中 */
int cy = (int)(h / 2) - (8 * scale) / 2;
u32 fg = 0xFFFFFFFFu;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, scale, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, scale, fg, (int)(code & 0xF));
/* 停住屏幕,等待实机拍照/观察 */
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 任意位数 hex(n=1..8),低位在前填充 */
static void fb_draw_hexn(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, u32 v, int n) {
for (int i = 0; i < n; i++) {
int nib = (int)((v >> (4 * (n - 1 - i))) & 0xF);
fb_draw_char(fb, pitch, x0 + i * 8 * scale, y0, scale, color, nib);
}
}
/* PCI 自检诊断清单项(bus/dev/device_id/class/progif) */
struct usb_ctl_info {
u32 bus;
u32 dev;
u32 devid; /* PCI device ID */
u32 cls; /* (class<<8)|subclass */
u32 progif;
};
/* 扩展诊断:大字标题码 + 下方逐行列出扫描到的 PCI 设备。
* 每行:bus(1) dev(1) deviceid(4) = 左 6 hex,class(2) progif(2) = 右 4 hex。
* scale=2 支持 16 行(640x480 完整容纳)。device ID 用于区分芯片型号:
* Intel 7系: xHCI=1e31, EHCI1=1e2d, EHCI2=1e26
* Intel 6系: EHCI1=1c2d, EHCI2=1c26 (H61 仅 1 个 EHCI)
* 第三方 USB3: ASMedia=1b21, Renesas=1033 */
/* 诊断用,保留 */
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) __attribute__((unused));
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return;
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
u32 fg = 0xFFFFFFFFu;
/* 标题大字(scale 6,两位居中,固定顶部) */
int s = 6, cw = 8 * s;
int cx = (int)(w / 2) - cw;
int cy = 8;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, s, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, s, fg, (int)(code & 0xF));
/* 信息行(scale 2,最多 16 行):
* 左:bus(1)+dev(1)+devid(4)=6 hex;右:class(2)+progif(2)=4 hex */
int s2 = 2, cw2 = 8 * s2;
int row_y = cy + 8 * s + 8;
int total = (6 + 1 + 4) * cw2; /* 176px */
int xl = (int)(w / 2) - total / 2;
int xr = xl + (6 + 1) * cw2;
u32 max_rows = usb_count < 16 ? usb_count : 16;
for (u32 i = 0; i < max_rows; i++) {
u32 bd = ((usb[i].bus & 0xFF) << 4) | (usb[i].dev & 0x1F);
fb_draw_hexn(fb, pitch, xl, row_y, s2, fg, bd, 2);
fb_draw_hexn(fb, pitch, xl + 2 * cw2, row_y, s2, fg, usb[i].devid & 0xFFFF, 4);
fb_draw_hexn(fb, pitch, xr, row_y, s2, fg, usb[i].cls & 0xFFFF, 2);
fb_draw_hexn(fb, pitch, xr + 2 * cw2, row_y, s2, fg, usb[i].progif & 0xFFFF, 2);
row_y += 8 * s2 + 4; /* 20px/行 */
}
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 红底 = 基础设施失败,halt(0xFFFF0000 在 BGRA 下是纯红;
* 原 0xFF0000FF 实际渲染为蓝——0x01 那次实机测试显示“蓝底”即此原因) */
#define FB_FATAL(api, code) fb_diag_show((api), 0xFFFF0000u, (code))
/* 红底 PCI 自检:标题码 + USB 控制器清单 */
#define FB_FATAL_show_usb(api, code) \
fb_diag_show_usb((api), 0xFFFF0000u, (code), g_diag_usb, g_diag_usb_count)
/* 黄底 = 枚举失败,halt */
#define FB_ENUM_FAIL(code) fb_diag_show(g_api, 0xFFFFC800u, (code))
static void xhci_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static void xhci_memcpy(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
/* MMIO read/write */
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_mmio + off);
}
static void mmio_write32(u32 off, u32 value) {
*(volatile u32 *)(void *)(g_mmio + off) = value;
}
static u64 mmio_read64(u32 off) {
u32 lo = mmio_read32(off);
u32 hi = mmio_read32(off + 4);
return ((u64)hi << 32) | lo;
}
static void mmio_write64(u32 off, u64 value) {
mmio_write32(off, (u32)(value & 0xffffffffu));
mmio_write32(off + 4, (u32)(value >> 32));
}
/* Memory barrier (compiler only — x86 TSO guarantees HW ordering) */
static __inline__ void xhci_mb(void) {
__asm__ volatile("" ::: "memory");
}
/* ================================================================
* Phase 1: PCI discovery + MMIO mapping
* ================================================================ */
/* PCI 自检:区分“传统 0xCF8/0xCFC 配置机制失效” vs “确实无 xHCI 控制器”。
* 实机现象:xhci_find 全扫描失败(诊断码 0x01)——USB3 主板必然有 xHCI,
* 扫不到只可能是 (a) 配置机制读不到任何设备 (b) 设备在异常位置 (c) 控制器
* 是 EHCI 等非 xHCI。返回细分诊断码:
* 0x11 bus0/dev0/func0 vendor=0xFFFF → 0xCF8/0xCFC 机制失效
* 0x12 机制可用但全扫描无任何 USB 控制器 (class 0x0C/03)
* 0x13 有 USB 控制器但无 prog_if=0x30(全部 EHCI/UHCI/OHCI)
* 0x14 存在 prog_if=0x30 的 xHCI 但 xhci_find 漏匹配(防御性,理论不可能)
* 同时把扫描到的设备填入 g_diag_usb 供屏幕显示——记录策略:
* 第一遍收集全部 USB 控制器(class 0C/03,含 bus1+ 第三方芯片),
* 第二遍收集其他设备(bus0 优先),总上限 16 行。
* 关键 device ID 判读:Intel 7系 xHCI=1e31 / EHCI1=1e2d / EHCI2=1e26;
* Intel 6系 EHCI1=1c2d / EHCI2=1c26(H61 仅 1 个);ASMedia=1b21;
* Renesas=1033。 */
static struct usb_ctl_info g_diag_usb[16];
static u32 g_diag_usb_count;
#define DIAG_MAX 16
static void diag_add(u32 bus, u32 dev, u32 devid, u32 cls, u32 progif) {
if (g_diag_usb_count >= DIAG_MAX) return;
g_diag_usb[g_diag_usb_count].bus = bus;
g_diag_usb[g_diag_usb_count].dev = dev;
g_diag_usb[g_diag_usb_count].devid = devid;
g_diag_usb[g_diag_usb_count].cls = cls;
g_diag_usb[g_diag_usb_count].progif = progif;
g_diag_usb_count++;
}
/* 诊断用,保留(无 xHCI 场景默认不调用,可通过临时改 driver_init 调试) */
static u32 xhci_pci_diag_code(void) __attribute__((unused));
static u32 xhci_pci_diag_code(void) {
u32 vd0 = dkm_pci_read(0, 0, 0, PCI_VENDOR_ID);
g_diag_usb_count = 0;
if ((vd0 & 0xffff) == 0xffff) return 0x11;
u32 usb_total = 0, xhci_total = 0;
/* 第一遍:USB 控制器(全 bus,含 bus1+ 第三方芯片) */
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) {
usb_total++;
if (prog_if == 0x30) xhci_total++;
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
/* 第二遍:其他设备补位(bus0 优先) */
if (g_diag_usb_count < DIAG_MAX) {
for (u16 bus = 0; bus < 256 && g_diag_usb_count < DIAG_MAX; bus++) {
for (u8 dev = 0; dev < 32 && g_diag_usb_count < DIAG_MAX; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count && g_diag_usb_count < DIAG_MAX; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) continue; /* 已收集 */
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
if (usb_total == 0) return 0x12;
if (xhci_total > 0) return 0x14;
return 0x13;
}
static int xhci_find_nth(int nth, u8 *out_bus, u8 *out_dev, u8 *out_func) {
int seen = 0;
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == XHCI_CLASS_SERIAL &&
subclass == XHCI_SUBCLASS_USB &&
prog_if == XHCI_PROGIF_XHCI) {
if (seen == nth) {
*out_bus = (u8)bus;
*out_dev = dev;
*out_func = func;
return 0;
}
seen++;
}
}
}
}
return -1;
}
static volatile u8 *xhci_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
u64 bar_virt = api->hhdm_offset + bar_phys;
/* Prefer mm_map_mmio independent window */
if (api->mm_map_mmio) {
void *mapped = api->mm_map_mmio(bar_phys, XHCI_MAP_SIZE);
if (mapped) {
g_log->info("[xhci] BAR mapped via mm_map_mmio");
return (volatile u8 *)mapped;
}
g_log->warn("[xhci] mm_map_mmio failed; trying HHDM fallback");
}
/* Check if HHDM covers this BAR */
if (api->mmio && api->mmio->is_mapped) {
int level = api->mmio->is_mapped(bar_virt);
if (level > 0) {
g_log->info("[xhci] HHDM covers BAR; direct access");
return (volatile u8 *)(uintptr_t)bar_virt;
}
/* Map via page tables */
if (api->mmio->map_mmio) {
int rc = api->mmio->map_mmio(bar_phys, XHCI_MAP_SIZE);
if (rc == 0 && api->mmio->is_mapped(bar_virt) > 0) {
g_log->info("[xhci] BAR mapped via page tables");
return (volatile u8 *)(uintptr_t)bar_virt;
}
}
}
/* Low BAR fallback */
if (bar_phys < 0x100000000ULL) {
g_log->warn("[xhci] assuming HHDM covers low BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
g_log->error("[xhci] BAR MMIO unavailable");
return (volatile u8 *)0;
}
/* ================================================================
* Phase 1: BIOS Handoff via Extended Capabilities
* ================================================================ */
static void xhci_bios_handoff(void) {
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
u32 ext_ptr = (hcc1 >> 16) & 0xFFFF;
if (!ext_ptr) return;
/* Walk extended capabilities (offsets in 32-bit words from MMIO base) */
u32 off = ext_ptr;
for (int i = 0; i < 16 && off != 0; i++) {
u32 dw0 = mmio_read32(off * 4);
u8 id = (u8)(dw0 & 0xFF);
u8 next = (u8)((dw0 >> 8) & 0xFF);
if (id == 1) {
/* USB Legacy Support Capability */
u32 legsup = mmio_read32(off * 4 + 4);
log_hex("[xhci] USBLEGSUP=", legsup);
if (legsup & 0x0000FFFFu) {
/* BIOS owns controller — request ownership */
mmio_write32(off * 4 + 4, legsup | (1u << 16));
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
u32 cur = mmio_read32(off * 4 + 4);
if (!(cur & 0x0000FFFFu)) {
g_log->info("[xhci] BIOS handoff complete");
/* Clear SMI triggers */
mmio_write32(off * 4 + 8, 0);
break;
}
__asm__ volatile("pause");
}
}
}
off = next ? (off + next) : 0;
}
}
/* ================================================================
* Phase 1: Controller reset and init
* ================================================================ */
static int xhci_controller_reset(void) {
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
u32 usbsts = mmio_read32(g_cap_length + XHCI_OP_USBSTS);
/* If controller is running, halt it first */
if (!(usbsts & USBSTS_HCH)) {
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_HCH) break;
__asm__ volatile("pause");
}
}
/* Wait for CNR to clear */
{
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (!(mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_CNR)) break;
__asm__ volatile("pause");
}
}
/* Assert HC reset */
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd | USBCMD_RST);
/* Wait for reset to complete (RST bit clears) */
{
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
if (!(usbcmd & USBCMD_RST)) {
g_log->info("[xhci] controller reset complete");
return 0;
}
__asm__ volatile("pause");
}
}
g_log->error("[xhci] controller reset timeout");
return -1;
}
static int xhci_parse_caps(void) {
/* Read CAPLENGTH (low byte of MMIO+0) */
g_cap_length = mmio_read32(0) & 0xFF;
u32 hcs1 = mmio_read32(XHCI_CAP_HCSPARAMS1);
u32 hcs2 = mmio_read32(XHCI_CAP_HCSPARAMS2);
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
g_max_slots = hcs1 & 0xFF;
g_max_intrs = (hcs1 >> 8) & 0x7FF;
g_max_ports = (hcs1 >> 24) & 0xFF;
/* Context size: CSZ bit4 of HCCPARAMS1 */
g_ctx_size = (hcc1 & (1u << 4)) ? 64 : 32;
/* Per-Port Power Control: PPC bit3 of HCCPARAMS1 */
g_ppc = (hcc1 >> 3) & 1;
/* Scratchpad buffer count from HCSPARAMS2 */
g_spb_count = ((hcs2 >> 21) & 0x1F) | ((hcs2 >> 27) & 0x1F);
/*
* Port Register Set offset: absolute from MMIO base.
* xHCI spec: "Port Register Sets are located at offset 0x400
* from the beginning of the Operational Register Space."
* Operational Register Space starts at offset CAPLENGTH.
* Therefore absolute port offset = CAPLENGTH + 0x400.
* Verified against QEMU source (XHCI_REGS_OFFSET_PORT =
* XHCI_REGS_OFFSET_OPER + XHCI_REGS_LENGTH_OPER) and Linux
* kernel (op_regs + PORTSC_OFFSET where op_regs = base + cap_length).
*/
g_port_base = g_cap_length + XHCI_PORT_BASE_OFF;
/* Doorbell and Runtime offsets — full 32-bit, clear reserved low bits */
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
/* Page size: PAGESIZE 寄存器 bit[n] 置位表示支持 2^(n+12) 字节页。
* 取置位的最低 bit(xHCI 规范要求软件使用最小支持页)。
* 原实现 (ps_reg & 0xF) 语义错误,实机多 bit 置位时会得到错误页大小。 */
u32 ps_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE);
u32 ps_bits = ps_reg & 0xFFFFu;
u32 ps_shift = 0;
if (ps_bits) {
while (!(ps_bits & 1u)) { ps_bits >>= 1; ps_shift++; }
g_page_size = 4096u << ps_shift;
} else {
g_page_size = 4096;
}
log_hex("[xhci] CAPLEN=", g_cap_length);
log_hex("[xhci] MaxSlots=", g_max_slots);
log_hex("[xhci] MaxPorts=", g_max_ports);
log_hex("[xhci] MaxIntrs=", g_max_intrs);
log_hex("[xhci] ContextSize=", g_ctx_size);
log_hex("[xhci] PPC=", g_ppc);
log_hex("[xhci] PortBaseOff=", g_port_base);
log_hex("[xhci] ScratchpadBufs=", g_spb_count);
log_hex("[xhci] DBOFF=", g_doorbell_off);
log_hex("[xhci] RTSOFF=", g_runtime_off);
log_hex("[xhci] PageSize=", g_page_size);
if (g_max_slots == 0 || g_max_ports == 0) {
g_log->error("[xhci] invalid capability parameters");
return -1;
}
if (g_max_intrs == 0) {
g_log->warn("[xhci] MaxIntrs=0 (no interrupt register sets); polling mode only");
}
return 0;
}
/* ================================================================
* Phase 2: TRB Ring management
* ================================================================ */
static int xhci_ring_init(xhci_ring *ring, u32 size) {
if (!g_api->dma) return -1;
/* Allocate one page (must be 4K aligned for DMA) */
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &ring->dma) != 0)
return -2;
xhci_zero(ring->dma.virt, ring->dma.size);
ring->size = size;
ring->enqueue = 0;
ring->ccs = 1; /* Producer Cycle State starts at 1 */
/* Append Link TRB at end of ring */
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
u32 link_idx = size - 1;
trbs[link_idx].parameter = ring->dma.phys;
trbs[link_idx].status = 0;
trbs[link_idx].control = (TRB_TYPE_LINK << TRB_TYPE_SHIFT) | TRB_TC | TRB_CYCLE;
return 0;
}
/* Enqueue a TRB onto a ring and return the enqueue index before advance */
static u32 xhci_ring_enqueue(xhci_ring *ring, u64 param, u32 status, u32 control) {
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
u32 idx = ring->enqueue;
/* Set cycle bit in control */
control &= ~TRB_CYCLE;
if (ring->ccs) control |= TRB_CYCLE;
trbs[idx].parameter = param;
trbs[idx].status = status;
trbs[idx].control = control;
/* Advance enqueue */
ring->enqueue = (idx + 1) % (ring->size - 1); /* -1 to skip Link TRB */
/* If we wrapped, toggle CCS */
if (idx + 1 >= ring->size - 1)
ring->ccs ^= 1;
return idx;
}
/* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell。
* Doorbell Registers 每个 4 字节(xHCI 1.2 §5.6):offset = slot_id * 4。
* BUG-FIX: 原实现 slot_id * 0x20 偏移错误——QEMU 门铃区 reg>>=2 后
* reg 即 slot_id,0x20 偏移会敲到 slot 8,该 slot 无 EP → 控制器不
* 处理 TRB → 无 Transfer Event → 轮询超时。 */
static void xhci_ring_doorbell(u32 slot_id, u32 target) {
mmio_write32(g_doorbell_off + slot_id * 4, target);
}
/* ================================================================
* Phase 2: Event Ring polling
* ================================================================ */
/* Poll event ring for a specific TRB type with TSC timeout.
* Returns 0 on success, fills out_param/out_status/out_control.
* Returns -1 on timeout, -2 on error completion code. */
static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
u64 *out_param, u32 *out_status, u32 *out_control) {
xhci_trb *trbs = (xhci_trb *)g_evt_ring.dma.virt;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
for (;;) {
xhci_trb *evt = &trbs[g_evt_ring.enqueue];
u32 ctrl = evt->control;
u8 cycle = (u8)(ctrl & TRB_CYCLE);
/* Check if this TRB is owned by us (consumer) */
if (cycle == (u8)g_evt_ccs) {
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
/* Process this event */
if (type == expected_type) {
if (out_param) *out_param = evt->parameter;
if (out_status) *out_status = evt->status;
if (out_control)*out_control= evt->control;
/* Advance dequeue */
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
/* Update ERDP: 地址 16 字节对齐,bit0 保持 0,bit3=EHB 清挂起 */
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Check completion code */
u32 cc = (evt->status >> 24) & 0xFF;
if (cc != 1) { /* 1 = Success */
if (cc != 0) {
log_hex("[xhci] event completion code=", cc);
return -2;
}
}
return 0;
}
/* Unexpected event type — handle or skip */
if (type == EVT_TYPE_PORT_CHANGE) {
/* Port Status Change Event: clear PORTSC change bits */
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
if (port_id > 0 && port_id <= g_max_ports) {
u32 port_off = g_port_base + (port_id - 1) * XHCI_PORT_STRIDE + XHCI_PORT_SC;
/* Read PORTSC and write back to clear change bits (W1C) */
u32 portsc = mmio_read32(port_off);
mmio_write32(port_off, portsc);
log_hex("[xhci] port change event, port=", port_id);
}
} else {
log_hex("[xhci] unexpected event type=", type);
}
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp2 = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | ERDP_EHB);
continue;
}
if (dkm_rdtsc() > deadline) {
g_log->warn("[xhci] event poll timeout");
return -1;
}
__asm__ volatile("pause");
}
}
/* ================================================================
* Phase 2: Command submission
* ================================================================ */
static int xhci_cmd_submit(u64 param, u32 status, u32 control,
u64 timeout_ms,
u64 *out_param, u32 *out_status, u32 *out_control) {
/* Clear Event Ring interrupt pending */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
/* Enqueue command TRB */
xhci_ring_enqueue(&g_cmd_ring, param, status, control);
xhci_mb();
/* Debug: verify CRCR and command ring state */
log_hex("[xhci] CRCR=", mmio_read64(g_cap_length + XHCI_OP_CRCR_LO));
log_hex("[xhci] cmd ring enqueue=", g_cmd_ring.enqueue);
log_hex("[xhci] cmd ring ccs=", g_cmd_ring.ccs);
xhci_ring_doorbell(0, 0); /* Command ring doorbell */
/* Debug: check USBSTS after doorbell */
log_hex("[xhci] USBSTS after db=", mmio_read32(g_cap_length + XHCI_OP_USBSTS));
/* Poll for Command Completion Event */
/* Debug: dump event ring head entry */
{
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
log_hex("[xhci] evt ring head=", g_evt_ring.enqueue);
log_hex("[xhci] evt[0] ctrl=", evts[g_evt_ring.enqueue].control);
}
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
out_param, out_status, out_control);
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return rc;
}
/* ================================================================
* Phase 2: Setup controller rings + DCBAA + Scratchpad
* ================================================================ */
static int xhci_setup_rings(void) {
if (!g_api->dma) {
g_log->warn("[xhci] DMA API unavailable");
return -1;
}
/* Command Ring */
if (xhci_ring_init(&g_cmd_ring, CMD_RING_SIZE) != 0) {
g_log->error("[xhci] cmd ring alloc failed");
return -2;
}
log_hex("[xhci] cmd ring phys=", g_cmd_ring.dma.phys);
/* Event Ring */
if (xhci_ring_init(&g_evt_ring, EVT_RING_SIZE) != 0) {
g_log->error("[xhci] evt ring alloc failed");
return -3;
}
g_evt_ccs = 1; /* Consumer Cycle State starts at 1 */
log_hex("[xhci] evt ring phys=", g_evt_ring.dma.phys);
/* ERST (Event Ring Segment Table) */
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_erst) != 0) {
g_log->error("[xhci] ERST alloc failed");
return -4;
}
xhci_erst_entry *erst = (xhci_erst_entry *)g_erst.virt;
xhci_zero(erst, sizeof(xhci_erst_entry));
erst->seg_addr = g_evt_ring.dma.phys;
erst->seg_size = EVT_RING_SIZE;
/* DCBAA (Device Context Base Address Array) */
u32 dcbaa_size = (g_max_slots + 1) * 8;
u32 dcbaa_pages = (dcbaa_size + 4095) / 4096;
if (dcbaa_pages == 0) dcbaa_pages = 1;
if (g_api->dma->alloc_pages(dcbaa_pages, 4096, 0x100000000ULL, &g_dcbaa) != 0) {
g_log->error("[xhci] DCBAA alloc failed");
return -5;
}
xhci_zero(g_dcbaa.virt, g_dcbaa.size);
log_hex("[xhci] DCBAA phys=", g_dcbaa.phys);
/* Scratchpad Buffer Array (if needed) */
if (g_spb_count > 0) {
u32 sp_arr_size = g_spb_count * 8;
u32 sp_arr_pages = (sp_arr_size + 4095) / 4096;
if (sp_arr_pages == 0) sp_arr_pages = 1;
if (g_api->dma->alloc_pages(sp_arr_pages, 4096, 0x100000000ULL, &g_sp_bufs) != 0) {
g_log->error("[xhci] scratchpad array alloc failed");
return -6;
}
xhci_zero(g_sp_bufs.virt, g_sp_bufs.size);
/* Allocate individual scratchpad buffer pages */
u32 sp_count = g_spb_count;
if (sp_count > 32) sp_count = 32; /* safety cap */
for (u32 i = 0; i < sp_count; i++) {
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_sp_pages[i]) != 0) {
g_log->error("[xhci] scratchpad page alloc failed");
return -7;
}
xhci_zero(g_sp_pages[i].virt, g_sp_pages[i].size);
/* Fill scratchpad buffer array with physical addresses */
u64 *sp_arr = (u64 *)g_sp_bufs.virt;
sp_arr[i] = g_sp_pages[i].phys;
}
/* DCBAA slot 0 points to scratchpad buffer array */
u64 *dcbaa_entries = (u64 *)g_dcbaa.virt;
dcbaa_entries[0] = g_sp_bufs.phys;
log_hex("[xhci] SPB array phys=", g_sp_bufs.phys);
}
return 0;
}
static int xhci_controller_start(void) {
u32 cap_off = g_cap_length;
/* Configure Command Ring */
u64 crcr = g_cmd_ring.dma.phys | (u64)g_cmd_ring.ccs;
mmio_write64(cap_off + XHCI_OP_CRCR_LO, crcr);
/* Configure DCBAAP */
mmio_write64(cap_off + XHCI_OP_DCBAAP_LO, g_dcbaa.phys);
/* Configure MaxSlotsEn */
mmio_write32(cap_off + XHCI_OP_CONFIG, g_max_slots);
/* Configure Event Ring */
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
u64 erdp = g_evt_ring.dma.phys;
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Enable interrupt (IMAN.IE=1) */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | (1u << 1));
/* Enable controller interrupts + start controller */
u32 usbcmd = mmio_read32(cap_off + XHCI_OP_USBCMD);
usbcmd |= USBCMD_INTE | USBCMD_RS;
mmio_write32(cap_off + XHCI_OP_USBCMD, usbcmd);
/* Wait for HCH to clear (controller running) */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
u32 usbsts = mmio_read32(cap_off + XHCI_OP_USBSTS);
if (!(usbsts & USBSTS_HCH)) {
g_log->info("[xhci] controller started");
return 0;
}
__asm__ volatile("pause");
}
g_log->error("[xhci] controller start timeout");
return -1;
}
/* ================================================================
* Phase 3: Port detection and reset
* ================================================================ */
static int xhci_reset_port(u32 port_idx) {
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
/* Check if device is connected */
if (!(portsc & PORTSC_CCS)) return -1;
/* Already enabled? */
if (portsc & PORTSC_PED) return 0;
/* 只置 PR,并清掉 RW1C 位。把读到的 PORTSC 原样或上 PR 会把
* PED=0 和已经为 1 的变化位写回去,实机上复位经常起不来。 */
u32 cmd = portsc & ~((1u << 1) | PORTSC_PR | 0x00FE0000u);
cmd |= PORTSC_PR;
if (portsc & (1u << 9)) cmd |= (1u << 9);
mmio_write32(port_off, cmd);
/* Wait for PED (Port Enabled) with 500ms timeout */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
while (dkm_rdtsc() < deadline) {
portsc = mmio_read32(port_off);
if (portsc & PORTSC_PED) {
/* Clear port change bits (W1C: write 1 to clear) */
mmio_write32(port_off, portsc);
DKM_STAT_INC(xhci_port_reset_ok);
/* Drain any Port Status Change Events from the event ring.
* Port reset generates a Port Status Change Event that must
* be consumed before submitting new commands, otherwise the
* event ring gets congested and command completion events
* may not be delivered. */
dkm_delay_ms(10); /* Allow controller to post the event */
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
for (int drain = 0; drain < 16; drain++) {
xhci_trb *evt = &evts[g_evt_ring.enqueue];
u32 ctrl = evt->control;
u8 cycle = (u8)(ctrl & TRB_CYCLE);
if (cycle != (u8)g_evt_ccs) break; /* No more events */
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
if (type == EVT_TYPE_PORT_CHANGE) {
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
log_hex("[xhci] drained port change event, port=", port_id);
}
/* Advance dequeue */
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
}
return 0;
}
/* Check if device disconnected during reset */
if (!(portsc & PORTSC_CCS)) return -2;
__asm__ volatile("pause");
}
g_log->warn("[xhci] port reset timeout");
return -3;
}
static u8 xhci_get_port_speed(u32 port_idx) {
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
return (u8)((portsc >> PORTSC_SPEED_SHIFT) & PORTSC_SPEED_MASK);
}
/* ================================================================
* Phase 3: Enable Slot
* ================================================================ */
static int xhci_enable_slot(u8 *out_slot_id) {
/* Submit Enable Slot Command TRB */
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Enable Slot failed");
return rc;
}
/* Slot ID 位于 Command Completion Event 的 TRB control[31:24]。
* (xHCI 1.2 §6.4.2.2:CMD_COMPLETE 事件 TRB parameter = 命令 TRB 地址,
* slot id 在 control 高字节 —— QEMU hcd-xhci.c xhci_write_event 佐证) */
*out_slot_id = (u8)((out_control >> 24) & 0xFF);
log_hex("[xhci] slot_id=", *out_slot_id);
return 0;
}
/* ================================================================
* Phase 3: Address Device
* ================================================================ */
static int xhci_address_device(u8 slot_id, int bsr) {
/* Input Context physical address */
u64 input_ctx_phys = g_input_ctx.phys;
u32 control = (TRB_TYPE_ADDRESS_DEV << TRB_TYPE_SHIFT);
if (bsr) control |= (1u << 9); /* BSR (Block Set Address) */
/* Slot ID goes in control bits [31:24] */
control |= ((u32)slot_id << 24);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Address Device failed");
return rc;
}
return 0;
}
/* ================================================================
* Phase 3: Configure Endpoint
* ================================================================ */
static int xhci_configure_endpoint(u8 slot_id) {
u64 input_ctx_phys = g_input_ctx.phys;
u32 control = (TRB_TYPE_CONFIGURE_EP << TRB_TYPE_SHIFT) | ((u32)slot_id << 24);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Configure Endpoint failed");
return rc;
}
return 0;
}
/* ================================================================
* Phase 3: Control Transfer (EP0)
* ================================================================ */
static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
u8 direction) {
/* Setup Stage TRB */
u64 setup_val = ((u64)setup_pkt[4] << 32) | ((u64)setup_pkt[5] << 40) |
((u64)setup_pkt[6] << 48) | ((u64)setup_pkt[7] << 56);
setup_val |= (u64)setup_pkt[0] | ((u64)setup_pkt[1] << 8) |
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
u32 setup_status = 8; /* Setup packet length */
/* Setup Stage TRB(不设 IOC:控制传输只需在最后一个 TRB 上报完成,
* 中间 TRB 设 IOC 会让 QEMU/实机产生多余 Transfer Event,残留事件
* 污染后续 CMD_COMPLETE 轮询——日志表现为 "unexpected event type") */
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT);
/* Setup Stage TRB 必须设置 IDT (Immediate Data, bit6):
* setup 包 8 字节直接内嵌在 TRB parameter 字段。
* BUG-FIX: 原实现缺 IDT,QEMU xhci_fire_ctl_transfer 校验
* `!(trb_setup->control & TRB_TR_IDT)` 直接返回 -1 → 无 Transfer
* Event → 轮询超时(GET_DESCRIPTOR 失败)。 */
setup_control |= (1u << 6); /* TRB_TR_IDT */
/* Set direction in TRB: bit 16 of Setup Stage TRB */
if (direction) setup_control |= (1u << 16);
xhci_ring_enqueue(&g_ep0_ring, setup_val, setup_status, setup_control);
/* Data Stage TRB (if data_len > 0) */
if (data_len > 0 && data) {
/* Copy data to transfer buffer for OUT direction */
if (direction == 0) {
xhci_memcpy(g_xfer_buf.virt, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
u32 data_status = data_len;
/* Data Stage 不设 IOC(避免多余事件残留) */
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT);
if (direction) data_control |= (1u << 16); /* DIR=IN */
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
}
/* Status Stage TRB */
u32 status_dir = direction ? 0 : 1; /* opposite of data direction */
u32 status_control = (TRB_TYPE_STATUS_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
if (status_dir) status_control |= (1u << 16);
xhci_ring_enqueue(&g_ep0_ring, 0, 0, status_control);
xhci_mb();
xhci_ring_doorbell(g_msc.slot_id, 1); /* EP1 = EP0+1 */
/* Poll for Transfer Event */
u64 timeout_ms = 5000;
u64 out_param = 0;
u32 out_status = 0;
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, timeout_ms,
&out_param, &out_status, 0);
if (rc != 0) {
g_log->warn("[xhci] control transfer failed");
return rc;
}
/* For IN direction, copy data from DMA buffer */
if (direction && data_len > 0 && data) {
xhci_memcpy(data, g_xfer_buf.virt, data_len);
}
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return 0;
}
/* ================================================================
* Phase 3: USB Descriptor reads
* ================================================================ */
static int xhci_get_device_descriptor(u8 *out_desc) {
u8 setup[8];
setup[0] = 0x80; /* bmRequestType: Device-to-Host, Standard, Device */
setup[1] = 0x06; /* bRequest: GET_DESCRIPTOR */
setup[2] = 0x00; /* wValue low: Descriptor Index */
setup[3] = 0x01; /* wValue high: Device Descriptor */
setup[4] = 0x00; /* wIndex low */
setup[5] = 0x00; /* wIndex high */
setup[6] = 0x12; /* wLength low: 18 bytes */
setup[7] = 0x00; /* wLength high */
xhci_zero(out_desc, 18);
return xhci_control_transfer(setup, out_desc, 18, 1);
}
static int xhci_get_config_descriptor(u8 *out_desc, u32 max_len) {
u8 setup[8];
setup[0] = 0x80; /* Device-to-Host, Standard, Device */
setup[1] = 0x06; /* GET_DESCRIPTOR */
setup[2] = 0x00; /* Index */
setup[3] = 0x02; /* Configuration Descriptor */
setup[4] = 0x00;
setup[5] = 0x00;
setup[6] = (u8)(max_len & 0xFF);
setup[7] = (u8)((max_len >> 8) & 0xFF);
xhci_zero(out_desc, max_len);
return xhci_control_transfer(setup, out_desc, max_len, 1);
}
/* ================================================================
* Phase 4: Bulk Transfer
* ================================================================ */
static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
u8 direction, /* 0=OUT, 1=IN */
const void *data, u32 data_len) {
if (data_len == 0) return -1;
/* For OUT, copy data to DMA buffer */
if (direction == 0) {
xhci_memcpy(g_xfer_buf.virt, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
/* Enqueue Normal TRB(s) — single TRB for now (up to 64KB) */
u32 control = (TRB_TYPE_NORMAL << TRB_TYPE_SHIFT) | TRB_IOC;
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
xhci_mb();
/* Doorbell target = Endpoint ID:EPn OUT→2n,EPn IN→2n+1。
* BUG-FIX: 原实现传 EP 号(2/1),QEMU xhci_kick_ep 按 Endpoint ID
* 索引 eps[epid-1] → 敲错端点 → 无 Transfer Event → 轮询超时。 */
u8 db_target = (direction ? (u8)(2u * ep_num + 1) : (u8)(2u * ep_num));
xhci_ring_doorbell(g_msc.slot_id, db_target);
/* Poll for Transfer Event */
u64 out_param = 0;
u32 out_status = 0;
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, 10000,
&out_param, &out_status, 0);
if (rc != 0) {
g_log->warn("[xhci] bulk transfer failed");
log_hex("[xhci] bulk rc=", (u64)(i64)rc);
return rc;
}
/* For IN, copy data from DMA buffer */
if (direction == 1) {
xhci_memcpy((void *)data, g_xfer_buf.virt, data_len);
}
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return 0;
}
/* ================================================================
* Phase 4: BOT (Bulk-Only Transport) + SCSI
* ================================================================ */
static int xhci_bot_send_cbw(u8 lun, u8 flags, u32 data_len,
const u8 *cb, u8 cb_len) {
u8 cbw[31];
xhci_zero(cbw, 31);
/* Signature */
cbw[0] = 0x55; cbw[1] = 0x53; cbw[2] = 0x42; cbw[3] = 0x43;
/* Tag (little-endian) */
u32 tag = g_msc.cbw_tag++;
cbw[4] = (u8)(tag & 0xFF);
cbw[5] = (u8)((tag >> 8) & 0xFF);
cbw[6] = (u8)((tag >> 16) & 0xFF);
cbw[7] = (u8)((tag >> 24) & 0xFF);
/* Data Transfer Length (little-endian) */
cbw[8] = (u8)(data_len & 0xFF);
cbw[9] = (u8)((data_len >> 8) & 0xFF);
cbw[10] = (u8)((data_len >> 16) & 0xFF);
cbw[11] = (u8)((data_len >> 24) & 0xFF);
/* Flags */
cbw[12] = flags;
/* LUN */
cbw[13] = lun;
/* CB Length */
cbw[14] = cb_len;
/* Command Block */
for (u8 i = 0; i < cb_len && i < 16; i++)
cbw[15 + i] = cb[i];
return xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
0, cbw, 31);
}
static int xhci_bot_receive_csw(void) {
int rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, g_csw_buf.virt, 13);
if (rc != 0) return rc;
/* Verify CSW signature */
u8 *csw = (u8 *)g_csw_buf.virt;
if (csw[0] != 0x55 || csw[1] != 0x53 || csw[2] != 0x42 || csw[3] != 0x53) {
g_log->error("[xhci] CSW signature mismatch");
return -2;
}
/* CSW status: byte 12, 0=OK */
u8 status = csw[12];
if (status != 0) {
log_hex("[xhci] CSW status=", status);
return -3;
}
return 0;
}
/* SCSI INQUIRY */
static int xhci_scsi_inquiry(void) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_INQUIRY;
cb[4] = 36; /* allocation length */
int rc = xhci_bot_send_cbw(0, 0x80, 36, cb, 6);
if (rc != 0) return rc;
/* Data phase: IN 36 bytes */
u8 inq_data[36];
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, inq_data, 36);
if (rc != 0) return rc;
/* CSW */
rc = xhci_bot_receive_csw();
if (rc != 0) return rc;
/* Parse INQUIRY data */
u8 dev_type = inq_data[0] & 0x1F;
log_hex("[xhci] INQ PeripheralType=", dev_type);
g_log->info("[xhci] INQ Vendor:");
g_log->info((const char *)(inq_data + 8)); /* Not null-terminated but safe for log */
return (dev_type == 0x00) ? 0 : -1; /* 0 = Direct Access Block */
}
/* SCSI READ CAPACITY(10) */
static int xhci_scsi_read_capacity(u64 *out_sectors, u32 *out_blk_size) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_READ_CAPACITY10;
int rc = xhci_bot_send_cbw(0, 0x80, 8, cb, 10);
if (rc != 0) return rc;
/* Data phase: IN 8 bytes */
u8 cap_data[8];
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, cap_data, 8);
if (rc != 0) return rc;
/* CSW */
rc = xhci_bot_receive_csw();
if (rc != 0) return rc;
/* Parse big-endian */
u32 max_lba = ((u32)cap_data[0] << 24) | ((u32)cap_data[1] << 16) |
((u32)cap_data[2] << 8) | (u32)cap_data[3];
u32 blk_size = ((u32)cap_data[4] << 24) | ((u32)cap_data[5] << 16) |
((u32)cap_data[6] << 8) | (u32)cap_data[7];
*out_sectors = (u64)max_lba + 1; /* READ CAPACITY returns max LBA */
*out_blk_size = blk_size;
log_hex("[xhci] capacity sectors=", *out_sectors);
log_hex("[xhci] block size=", blk_size);
return 0;
}
/* SCSI TEST UNIT READY */
static int xhci_scsi_test_unit_ready(void) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_TEST_UNIT_READY;
int rc = xhci_bot_send_cbw(0, 0x00, 0, cb, 6);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* SCSI READ(10) */
static int xhci_scsi_read10(u64 lba, u32 count, void *buffer) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_READ10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
int rc = xhci_bot_send_cbw(0, 0x80, data_len, cb, 10);
if (rc != 0) return rc;
/* Data phase: IN */
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, buffer, data_len);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* SCSI WRITE(10) */
static int xhci_scsi_write10(u64 lba, u32 count, const void *buffer) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_WRITE10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
int rc = xhci_bot_send_cbw(0, 0x00, data_len, cb, 10);
if (rc != 0) return rc;
/* Data phase: OUT */
rc = xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
0, buffer, data_len);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* ================================================================
* Phase 4+5: Block device interface
* ================================================================ */
static int xhci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(xhci_block_reads);
u8 *buf = (u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count; /* max 16 sectors (8KB) per SCSI cmd */
int rc = xhci_scsi_read10(lba, chunk, g_xfer_buf.virt);
if (rc != 0) {
log_hex("[xhci] READ10 failed lba=", lba);
return rc;
}
xhci_memcpy(buf, g_xfer_buf.virt, chunk * 512);
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
static int xhci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(xhci_block_writes);
const u8 *buf = (const u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count;
xhci_memcpy(g_xfer_buf.virt, buf, chunk * 512);
__asm__ volatile("wbinvd" ::: "memory"); /* Cache flush for DMA */
int rc = xhci_scsi_write10(lba, chunk, g_xfer_buf.virt);
if (rc != 0) {
log_hex("[xhci] WRITE10 failed lba=", lba);
return rc;
}
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
/* ================================================================
* Phase 3: Build Input Context for Address Device
* ================================================================ */
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0, u32 port_idx) {
(void)slot_id;
u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt;
xhci_zero(ic, g_input_ctx.size);
/* Input Control Context (xHCI 1.2 §6.4.3):
* DWORD0 = Drop Context Flags(本次全 0,不 drop 任何上下文)
* DWORD1 = Add Context Flags(bit0=Slot, bit1=EP0)
* BUG-FIX: 原实现把 Add flags 写进 DWORD0(Drop 槽),
* QEMU/实机校验 ictl_ctx[0]==0 && ictl_ctx[1]==0x3 直接失败 CC_TRB_ERROR。 */
u32 *icc = (u32 *)ic;
icc[0] = 0x0; /* Drop Context Flags */
icc[1] = (1u << 0) | (1u << 1); /* Add: Slot + EP0 */
/* Slot Context (§6.2.2):
* DWORD0: Route String [0:19] + Speed [20:22] + Context Entries [27:31]
* DWORD1: Max Exit Latency [0:7] + Root Hub Port Number [16:23]
* BUG-FIX: 原实现 speed<<29(占用 Context Entries 位域)、MPS0 写入
* DWORD1(规范 Slot Context 无 MPS0 字段,MPS0 在 EP0 Context)——
* QEMU xhci_lookup_uport 读 DWORD1[16:23] 找端口,原值 port=0 查不到
* 设备 → CC_TRB_ERROR。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 route_string = 0; /* direct-connect, no hub */
slot_ctx[0] = route_string | ((u32)speed << 20) | (1u << 27);
slot_ctx[1] = (port_idx + 1) << 16; /* Root Hub Port Number (1-based) */
/* EP0 Context starts at offset ctx_sz * 2
* Endpoint Context (§6.2.3) 布局:
* DWORD1: CErr[4:5] + EP Type[6:8] + Max Packet Size[16:31]
* DWORD2: Dequeue Pointer Low[4:31](bit0=DCS, bits1:3=reserved)
* DWORD3: Dequeue Pointer High
* DWORD4: Average TRB Length[0:15]
* BUG-FIX: 原实现把 dequeue 写到 DWORD6/7——QEMU xhci_init_epctx
* 从 DWORD2/3 读 dequeue(ctx[2]&~0xf, ctx[3]),读到 0 → ring 无
* dequeue → kick_epctx 直接返回,TRB 永不处理 → 无 Transfer Event。 */
u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2);
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
ep0_ctx[2] = (u32)(ep0_deq & 0xFFFFFFF0u) | (u32)g_ep0_ring.ccs;
ep0_ctx[3] = (u32)(ep0_deq >> 32);
ep0_ctx[4] = 8; /* Average TRB Length */
}
static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
u16 out_mps, u16 in_mps) {
u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt;
/* Input Control Context(Configure Endpoint 语义,xHCI 1.2 §6.4.3):
* DWORD0 = Drop(0),DWORD1 = Add。
* Configure 时 bit0=Slot 必须 Add,bit1=EP0 不可重复 Add
* (QEMU xhci_configure_slot 校验 (ictl[1]&0x3)==0x1)。
* 端点位用 Context Index:EPn OUT→2n,EPn IN→2n+1
* (QEMU 从 i=2 开始遍历 Add flags,按 ictx+32*i 读 EP Context)。 */
u32 *icc = (u32 *)ic;
u32 out_idx = 2u * bulk_out_ep;
u32 in_idx = 2u * bulk_in_ep + 1;
icc[0] = 0x0;
icc[1] = (1u << 0) | (1u << out_idx) | (1u << in_idx);
/* Update Slot Context: Context Entries = max Context Index
* (DWORD0 bits[27:31],保留 Speed/Route 原值)。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 max_idx = out_idx > in_idx ? out_idx : in_idx;
slot_ctx[0] &= ~(0x1Fu << 27);
slot_ctx[0] |= (max_idx << 27);
/* Bulk OUT Endpoint Context。
* 偏移 = ctx_sz * (out_idx + 1):input context 布局是
* [ICC][Slot][EP0][EP1]... 每个占 1 个 ctx_sz,Context Index i 的
* EP context 在 (i+1) 个槽处(QEMU xhci_configure_slot 用
* ictx+32+(32*i) 读取)。BUG-FIX: 原实现 ctx_sz*out_idx 少了 ICC
* 槽 → 全部错位 32 字节 → QEMU 读到零 EP context。 */
u32 *out_ctx = (u32 *)(ic + ctx_sz * (out_idx + 1));
out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16);
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
out_ctx[2] = (u32)(out_deq & 0xFFFFFFF0u) | (u32)g_bulk_out_ring.ccs;
out_ctx[3] = (u32)(out_deq >> 32);
out_ctx[4] = 512; /* Average TRB Length */
/* Bulk IN Endpoint Context (at offset ctx_sz * (in_idx + 1)) */
u32 *in_ctx = (u32 *)(ic + ctx_sz * (in_idx + 1));
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
in_ctx[2] = (u32)(in_deq & 0xFFFFFFF0u) | (u32)g_bulk_in_ring.ccs;
in_ctx[3] = (u32)(in_deq >> 32);
in_ctx[4] = 512; /* Average TRB Length */
}
/* ================================================================
* Phase 3: Full device enumeration
* ================================================================ */
static int xhci_enum_msc_device(u32 port_idx) {
u8 speed = xhci_get_port_speed(port_idx);
log_hex("[xhci] port speed=", speed);
/* Default MaxPacketSize for EP0 based on speed
* (xHCI 1.2 §4.3: Full=8? No — FS/HS EP0 通常 64,LS=8,SS=512) */
u16 mps0 = 64; /* Full/High-Speed default */
if (speed == USB_SPEED_LOW) mps0 = 8;
if (speed == USB_SPEED_SUPER) mps0 = 512;
/* Step 1: Enable Slot */
u8 slot_id = 0;
if (xhci_enable_slot(&slot_id) != 0) {
FB_ENUM_FAIL(0x0A); /* 黄: Enable Slot 失败 */
return -1;
}
g_msc.slot_id = slot_id;
g_msc.speed = speed;
/* Step 1.5: Allocate per-device DMA buffers(在 DCBAA 指向输出上下文之前) */
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_input_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_dev_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2;
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_csw_buf) != 0) return -2;
if (xhci_ring_init(&g_ep0_ring, XFER_RING_SIZE) != 0) return -2;
if (xhci_ring_init(&g_bulk_out_ring, XFER_RING_SIZE) != 0) return -2;
if (xhci_ring_init(&g_bulk_in_ring, XFER_RING_SIZE) != 0) return -2;
/* DCBAA[slot_id] 必须指向输出上下文(Device Context)。
* Address Device/Configure Endpoint 命令都会读取该地址;
* 缺失时 QEMU/实机将 octx=0 → DMA 写失败/CC_TRB_ERROR。 */
xhci_zero(g_dev_ctx.virt, g_dev_ctx.size);
u64 *dcbaa = (u64 *)g_dcbaa.virt;
dcbaa[slot_id] = g_dev_ctx.phys;
log_hex("[xhci] DCBAA[slot]=", dcbaa[slot_id]);
/* Step 3: Build Input Context and Address Device (BSR=1 first) */
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 1) != 0) {
/* BSR=1 failed, try without BSR */
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) {
FB_ENUM_FAIL(0x0B); /* 黄: Address Device 失败(BSR=1/0 均失败) */
return -3;
}
}
/* Step 4: Get Device Descriptor */
u8 dev_desc[18];
if (xhci_get_device_descriptor(dev_desc) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Device) 失败 */
return -4;
}
u8 dev_class = dev_desc[4];
u16 vid = (u16)dev_desc[8] | ((u16)dev_desc[9] << 8);
u16 pid = (u16)dev_desc[10] | ((u16)dev_desc[11] << 8);
u8 real_mps0 = dev_desc[7];
log_hex("[xhci] dev class=", dev_class);
log_hex("[xhci] VID=", vid);
log_hex("[xhci] PID=", pid);
log_hex("[xhci] bMaxPacketSize0=", real_mps0);
/* Step 5: Re-address with real MPS if different */
if (real_mps0 != mps0 && real_mps0 >= 8) {
mps0 = real_mps0;
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) {
g_log->warn("[xhci] re-address failed");
/* Continue anyway — device may still work */
}
}
/* Step 6: Get Configuration Descriptor */
u8 cfg_desc[255];
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Config) 失败 */
return -5;
}
/* Parse configuration descriptor to find MSC interface and endpoints */
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
if (total_len < 9) {
FB_ENUM_FAIL(0x0D); /* 黄: 配置描述符过短 */
return -5;
}
u8 bulk_out_ep = 0, bulk_in_ep = 0;
u16 bulk_out_mps = 512, bulk_in_mps = 512;
int msc_found = 0;
/* Walk through descriptors: config(9) + interface(9) + endpoints(7*N) */
u32 pos = 9; /* skip config descriptor */
while (pos + 2 <= (u32)total_len && pos + cfg_desc[pos] <= (u32)total_len) {
u8 desc_len = cfg_desc[pos];
u8 desc_type = cfg_desc[pos + 1];
if (desc_type == 4 && desc_len >= 9) {
/* Interface Descriptor */
u8 iface_class = cfg_desc[pos + 5];
u8 iface_subclass = cfg_desc[pos + 6];
u8 iface_protocol = cfg_desc[pos + 7];
if (iface_class == 0x08 && iface_subclass == 0x06 && iface_protocol == 0x50) {
msc_found = 1;
g_log->info("[xhci] MSC BOT interface found");
/* Scan following endpoint descriptors.
* SuperSpeed 设备在 endpoint 后紧跟 6 字节 SS Endpoint
* Companion descriptor (type 0x30)——必须跳过继续找下一
* 个 endpoint,不能因 type!=5 提前终止(Bulk OUT 常在
* IN 之后且被 companion 隔开)。 */
u32 ep_pos = pos + desc_len;
while (ep_pos + 2 <= (u32)total_len) {
u8 ep_len = cfg_desc[ep_pos];
if (ep_len == 0 || ep_pos + ep_len > (u32)total_len) break;
if (cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */
u8 ep_addr = cfg_desc[ep_pos + 2];
u8 ep_attr = cfg_desc[ep_pos + 3];
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
if ((ep_attr & 0x03) == 0x02) { /* Bulk */
if (ep_addr & 0x80) {
bulk_in_ep = ep_addr & 0x0F;
bulk_in_mps = ep_mps;
} else {
bulk_out_ep = ep_addr & 0x0F;
bulk_out_mps = ep_mps;
}
}
} else if (cfg_desc[ep_pos + 1] == 4) {
/* 下一个 interface — 停止扫描 */
break;
}
ep_pos += ep_len;
}
break;
}
}
pos += desc_len;
}
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
g_log->warn("[xhci] no MSC BOT interface found");
FB_ENUM_FAIL(0x0D); /* 黄: 未找到 MSC BOT 接口/端点 */
return -6;
}
log_hex("[xhci] Bulk OUT EP=", bulk_out_ep);
log_hex("[xhci] Bulk IN EP=", bulk_in_ep);
log_hex("[xhci] Bulk OUT MPS=", bulk_out_mps);
log_hex("[xhci] Bulk IN MPS=", bulk_in_mps);
g_msc.bulk_out_ep = bulk_out_ep;
g_msc.bulk_in_ep = bulk_in_ep;
g_msc.bulk_out_mps = bulk_out_mps;
g_msc.bulk_in_mps = bulk_in_mps;
/* Step 7: Configure Endpoint */
xhci_zero(g_input_ctx.virt, g_input_ctx.size);
xhci_build_input_ctx_bulk_eps(bulk_out_ep, bulk_in_ep,
bulk_out_mps, bulk_in_mps);
if (xhci_configure_endpoint(slot_id) != 0) {
g_log->warn("[xhci] Configure Endpoint failed");
FB_ENUM_FAIL(0x0E); /* 黄: Configure Endpoint 失败 */
return -7;
}
/* Step 8: SCSI INQUIRY to verify MSC device */
if (xhci_scsi_inquiry() != 0) {
g_log->warn("[xhci] INQUIRY failed or not block device");
/* Continue anyway — some devices return non-zero peripheral type */
}
/* Step 9: TEST UNIT READY (retry up to 5 times) */
for (int i = 0; i < 5; i++) {
if (xhci_scsi_test_unit_ready() == 0) break;
dkm_delay_ms(100);
}
/* Step 10: READ CAPACITY */
u64 sectors = 0;
u32 blk_size = 512;
if (xhci_scsi_read_capacity(&sectors, &blk_size) != 0) {
g_log->warn("[xhci] READ CAPACITY failed");
/* Use defaults */
sectors = 0;
blk_size = 512;
}
g_msc.sector_count = sectors;
g_msc.sector_size = blk_size;
g_msc.cbw_tag = 1;
g_msc.ready = 1;
DKM_STAT_INC(xhci_msc_found);
log_hex("[xhci] MSC device ready, sectors=", sectors);
log_hex("[xhci] MSC device block_size=", blk_size);
return 0;
}
/* ================================================================
* driver_init — main entry point
* ================================================================ */
#define XHCI_TRY_NEXT do { xhci_nth++; if (xhci_nth < 4) goto again_ctrl; return 0; } while (0)
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_api = api;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[xhci] init begin");
/* ---- Phase 1: PCI discovery ---- */
u8 bus = 0, dev = 0, func = 0;
/* 实机常有不止一只 xHCI。U 盘可能不在第一只上,找不到 MSC 就试下一只。 */
int xhci_nth = 0;
again_ctrl:
if (xhci_find_nth(xhci_nth, &bus, &dev, &func) != 0) {
g_log->info("[xhci] xHCI controller not found");
/* BUG-FIX: 不 halt。xhci 是可选驱动(manifest required=false),
* 无 xHCI 的平台(Intel 6 系/纯 EHCI)必须继续引导到 EHCI/DSK,
* 否则 `for(;;)hlt` 卡死整条启动链。仅记日志,如需诊断可开
* xhci_pci_diag_code() 打印控制器清单(见注释)。 */
return 0;
}
g_pci_bus = bus; g_pci_dev = dev; g_pci_func = func;
DKM_STAT_INC(xhci_pci_found);
g_log->info("[xhci] xHCI controller found");
log_hex("[xhci] bus=", bus);
log_hex("[xhci] dev=", dev);
log_hex("[xhci] func=", func);
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[xhci] vendor=", vd & 0xffff);
log_hex("[xhci] device=", vd >> 16);
/* Intel PCH 默认把 USB2 口留在 EHCI。不把路由切到 xHCI,
* U 盘在 xHCI 上永远没有 CCS,块设备就不会注册。 */
if ((vd & 0xffff) == 0x8086) {
u32 usb2_mask = dkm_pci_read(bus, dev, func, 0xD4);
u32 usb3_mask = dkm_pci_read(bus, dev, func, 0xDC);
if (usb2_mask) dkm_pci_write(bus, dev, func, 0xD0, usb2_mask);
if (usb3_mask) dkm_pci_write(bus, dev, func, 0xD8, usb3_mask);
log_hex("[xhci] intel usb2 route=", usb2_mask);
log_hex("[xhci] intel usb3 route=", usb3_mask);
dkm_delay_ms(200);
}
/* Enable PCI MEM + BUSM, disable INTx (polling mode) */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[xhci] PCI command=", command);
/* Read BAR0 (64-bit) */
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
u64 bar_phys;
if (bar0_lo & 1u) {
g_log->warn("[xhci] BAR0 is IO space; unsupported");
FB_FATAL(api, 0x02); /* 红: BAR0 是 IO 空间 */
XHCI_TRY_NEXT;
}
if ((bar0_lo & 0x6u) == 0x4u) {
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
} else {
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
}
u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[xhci] BAR0 phys=", bar_phys);
log_hex("[xhci] IRQ line=", irq_line);
log_hex("[xhci] HHDM offset=", api->hhdm_offset);
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[xhci] missing HHDM or BAR0; skip");
FB_FATAL(api, 0x02); /* 红: 缺 HHDM/BAR */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: MMIO mapping ---- */
g_mmio = xhci_map_bar(api, bar_phys);
if (!g_mmio) {
g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
FB_FATAL(api, 0x03); /* 红: BAR MMIO 映射失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: Parse capabilities ---- */
if (xhci_parse_caps() != 0) {
g_log->error("[xhci] capability parse failed");
FB_FATAL(api, 0x04); /* 红: capability 解析失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: BIOS Handoff ---- */
xhci_bios_handoff();
/* ---- Phase 1: Controller Reset ---- */
if (xhci_controller_reset() != 0) {
g_log->error("[xhci] controller reset failed");
FB_FATAL(api, 0x05); /* 红: 控制器 reset 失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
if (!api->dma) {
g_log->warn("[xhci] DMA API unavailable");
FB_FATAL(api, 0x06); /* 红: DMA API 缺失 */
XHCI_TRY_NEXT;
}
if (xhci_setup_rings() != 0) {
g_log->error("[xhci] ring setup failed");
FB_FATAL(api, 0x06); /* 红: ring 初始化失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 2: Start Controller ---- */
if (xhci_controller_start() != 0) {
g_log->error("[xhci] controller start failed");
FB_FATAL(api, 0x07); /* 红: 控制器启动失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 3: Port Scan + Device Enumeration ---- */
/*
* Wait 100ms after controller start for QEMU to complete USB device
* attachment and update port status. Without this delay, PORTSC.CCS
* may read 0 even though a device is physically connected.
*/
/* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。
* 20ms 就读 CCS 时,实机上的 U 盘还没出现,于是不会注册块设备。 */
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
if (g_ppc && !(portsc & (1u << 9))) {
mmio_write32(port_off, portsc | (1u << 9));
}
}
g_log->info("[xhci] waiting 1500ms for port settle...");
dkm_delay_ms(1500);
g_msc.ready = 0;
int saw_ccs = 0;
int saw_reset_fail = 0;
for (int pass = 0; pass < 2 && !g_msc.ready; pass++) {
if (pass == 1) {
g_log->info("[xhci] second port scan");
dkm_delay_ms(1500);
}
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
log_hex("[xhci] port=", port);
log_hex("[xhci] PORTSC=", portsc);
if (!(portsc & PORTSC_CCS)) continue;
saw_ccs = 1;
g_log->info("[xhci] device connected");
if (xhci_reset_port(port) != 0) {
g_log->warn("[xhci] port reset failed");
saw_reset_fail = 1;
continue;
}
g_log->info("[xhci] port reset OK");
if (xhci_enum_msc_device(port) == 0) {
g_log->info("[xhci] MSC enumeration successful");
break;
}
g_log->warn("[xhci] MSC enumeration failed; trying next port");
}
}
if (!g_msc.ready && saw_ccs && xhci_nth >= 3) {
g_log->panic(saw_reset_fail ? "XHC-E01 PORT RESET FAILED"
: "XHC-E02 MSC ENUM FAILED");
}
/* ---- Phase 5: Register Block Device ---- */
if (g_msc.ready) {
if (api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "usb0";
desc.sector_size = g_msc.sector_size;
desc.sector_count = g_msc.sector_count;
desc.ctx = 0;
desc.read = xhci_block_read;
desc.write = xhci_block_write;
int index = api->block->register_device(&desc);
log_hex("[xhci] block provider index=", (u64)(i64)index);
} else {
g_log->warn("[xhci] block API unavailable; provider not registered");
}
} else {
g_log->info("[xhci] no MSC device found; block provider not registered");
if (xhci_nth < 3) {
xhci_nth++;
g_log->info("[xhci] trying next controller");
goto again_ctrl;
}
FB_FATAL(api, 0x08); /* 黄: 所有端口无设备/枚举全部失败 */
}
g_log->info("[xhci] driver ready");
return 0;
}
#undef XHCI_TRY_NEXT
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* Halt controller if it was started */
if (g_mmio) {
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
}
return 0;
}
@@ -1,16 +0,0 @@
# Build for Windows PE64 without any MSVC toolchain:
# - lld-link (LLVM) links directly in MSVC flavor
# - all Win32 imports use raw-dylib (no import .lib files needed)
# - no CRT: we provide mainCRTStartup ourselves and link /NODEFAULTLIB
[build]
target = "x86_64-pc-windows-msvc"
[target.x86_64-pc-windows-msvc]
linker = "C:/Program Files/LLVM/bin/lld-link.exe"
rustflags = [
"-C", "link-arg=/NODEFAULTLIB",
"-C", "link-arg=/SUBSYSTEM:CONSOLE",
"-C", "link-arg=/ENTRY:mainCRTStartup",
"-C", "link-arg=/STACK:4194304",
"-C", "link-arg=/DEBUG:NONE",
]
-240
View File
@@ -1,240 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ab_glyph"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "deaicup-pe"
version = "0.1.0"
dependencies = [
"egui",
]
[[package]]
name = "ecolor"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
[[package]]
name = "egui"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
dependencies = [
"ahash",
"epaint",
"nohash-hasher",
]
[[package]]
name = "emath"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
[[package]]
name = "epaint"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
dependencies = [
"ab_glyph",
"ahash",
"ecolor",
"emath",
"nohash-hasher",
"parking_lot",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "owned_ttf_parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "zerocopy"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
-24
View File
@@ -1,24 +0,0 @@
[package]
name = "deaicup-pe"
version = "0.1.0"
edition = "2021"
# Deaicup Software — Windows PE64 target for Deshab UTSM PE compat layer.
# egui UI logic is kept; the eframe runtime is replaced by a hand-written
# Win32 backend (user32 window + GDI DIB blit) plus a pure-Rust software
# rasterizer for egui tessellated primitives.
[[bin]]
name = "deaicup"
path = "src/main.rs"
[dependencies]
egui = { version = "=0.27.2", default-features = false, features = ["default_fonts"] }
[profile.release]
opt-level = 2
debug = false
debug-assertions = false
lto = "thin"
codegen-units = 1
panic = "abort"
-438
View File
@@ -1,438 +0,0 @@
//! Deaicup Software — Windows PE64 entry point.
//!
//! The original application runs on eframe/egui. For the Deshab UTSM PE
//! compatibility layer we keep the egui UI logic but replace the eframe
//! runtime with a minimal hand-written Win32 backend:
//! user32 window + message pump -> egui RawInput events
//! egui tessellation -> SoftRaster (pure Rust, raster.rs)
//! 32bpp DIB backbuffer -> GDI BitBlt onto the window DC
//!
//! No CRT: the PE entry is `mainCRTStartup` (see .cargo/config.toml) and the
//! process ends via ExitProcess.
#![no_main]
#![allow(static_mut_refs)]
mod raster;
#[allow(dead_code)]
mod rt;
mod win32;
use egui::{self, Color32, Context, Event, Key, Modifiers, PointerButton, Pos2, Rect};
use raster::SoftRaster;
use win32::*;
/* ============================================================
* UI 逻辑(移植自原 eframe 应用 CODE/Deaicup Software/src)
* ========================================================== */
#[derive(Debug, PartialEq, Clone, Copy, Default)]
enum Page {
#[default]
Home,
Tool,
Game,
LinuxWare,
WindowsWare,
Set,
About,
}
#[derive(Default)]
struct DeaicupSoftware {
current_page: Page,
}
impl DeaicupSoftware {
fn ui(&mut self, ctx: &Context) {
// 原应用顶部是远程图片 https://deaicup.com/.../top.png。
// PE 环境无网络,用标题栏替代,保留 75px 高度布局。
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui| {
ui.set_min_height(75.0);
ui.vertical_centered(|ui| {
ui.add_space(15.0);
ui.label(egui::RichText::new("Deaicup Software").size(30.0).strong());
});
});
// main
egui::SidePanel::left("navigation_panel")
.default_width(200.0)
.resizable(true)
.show(ctx, |ui| {
ui.heading("Menu");
ui.separator();
ui.vertical(|ui| {
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked() {
self.current_page = Page::Home;
}
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked() {
self.current_page = Page::Tool;
}
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked() {
self.current_page = Page::Game;
}
if ui
.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE")
.clicked()
{
self.current_page = Page::LinuxWare;
}
if ui
.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE")
.clicked()
{
self.current_page = Page::WindowsWare;
}
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked() {
self.current_page = Page::Set;
}
if ui
.selectable_label(self.current_page == Page::About, "ABOUT")
.clicked()
{
self.current_page = Page::About;
}
})
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.separator();
match self.current_page {
Page::Home => ui.label("Home"),
Page::Tool => ui.label("Tool"),
Page::Game => ui.label("Game"),
Page::LinuxWare => ui.label("LinuxWare"),
Page::WindowsWare => ui.label("WindowsWare"),
Page::Set => ui.label("Set"),
Page::About => ui.label("About"),
}
});
}
}
/* ============================================================
* 全局输入状态(单线程:wnd_proc 只被 DispatchMessageW 同步调用)
* ========================================================== */
static mut EVENTS: Vec<Event> = Vec::new();
static mut POINTER: Pos2 = Pos2::ZERO;
static mut MOD_ALT: bool = false;
static mut MOD_CTRL: bool = false;
static mut MOD_SHIFT: bool = false;
static mut QUIT: bool = false;
fn current_modifiers() -> Modifiers {
unsafe {
Modifiers {
alt: MOD_ALT,
ctrl: MOD_CTRL,
shift: MOD_SHIFT,
mac_cmd: false,
command: MOD_CTRL,
}
}
}
#[inline]
fn lparam_x(l: LPARAM) -> i32 {
(l as i32 & 0xFFFF) as i16 as i32
}
#[inline]
fn lparam_y(l: LPARAM) -> i32 {
((l as i32 >> 16) & 0xFFFF) as i16 as i32
}
fn push_pointer_button(button: PointerButton, pressed: bool, l: LPARAM) -> LRESULT {
unsafe {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerButton {
pos: POINTER,
button,
pressed,
modifiers: current_modifiers(),
});
}
0
}
fn vk_to_egui_key(vk: usize) -> Option<Key> {
// 字符键不需要映射为 egui::Key —— 文本输入走 WM_CHAR -> Event::Text。
Some(match vk {
VK_LEFT => Key::ArrowLeft,
VK_UP => Key::ArrowUp,
VK_RIGHT => Key::ArrowRight,
VK_DOWN => Key::ArrowDown,
VK_BACK => Key::Backspace,
VK_DELETE => Key::Delete,
VK_RETURN => Key::Enter,
VK_TAB => Key::Tab,
VK_ESCAPE => Key::Escape,
VK_SPACE => Key::Space,
_ => return None,
})
}
fn on_key(vk: usize, pressed: bool) -> LRESULT {
unsafe {
match vk {
VK_SHIFT => MOD_SHIFT = pressed,
VK_CONTROL => MOD_CTRL = pressed,
VK_MENU => MOD_ALT = pressed,
_ => {}
}
if let Some(key) = vk_to_egui_key(vk) {
EVENTS.push(Event::Key {
key,
physical_key: None,
pressed,
repeat: false,
modifiers: current_modifiers(),
});
}
}
0
}
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w: WPARAM, l: LPARAM) -> LRESULT {
unsafe {
match msg {
WM_CLOSE | WM_DESTROY => {
QUIT = true;
PostQuitMessage(0);
0
}
WM_MOUSEMOVE => {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerMoved(POINTER));
0
}
WM_LBUTTONDOWN => push_pointer_button(PointerButton::Primary, true, l),
WM_LBUTTONUP => push_pointer_button(PointerButton::Primary, false, l),
WM_RBUTTONDOWN => push_pointer_button(PointerButton::Secondary, true, l),
WM_RBUTTONUP => push_pointer_button(PointerButton::Secondary, false, l),
WM_MBUTTONDOWN => push_pointer_button(PointerButton::Middle, true, l),
WM_MBUTTONUP => push_pointer_button(PointerButton::Middle, false, l),
WM_KEYDOWN => on_key(w, true),
WM_KEYUP => on_key(w, false),
WM_CHAR => {
if let Some(c) = char::from_u32(w as u32) {
if c >= ' ' && c != '\x7f' {
EVENTS.push(Event::Text(c.to_string()));
}
}
0
}
WM_ERASEBKGND => 1, // 背景由 egui 全量绘制
_ => DefWindowProcW(hwnd, msg, w, l),
}
}
}
/* ============================================================
* 计时
* ========================================================== */
fn now_secs() -> f64 {
unsafe {
static mut FREQ: i64 = 0;
if FREQ == 0 {
QueryPerformanceFrequency(&mut FREQ);
if FREQ == 0 {
FREQ = 1000;
}
}
let mut c: i64 = 0;
QueryPerformanceCounter(&mut c);
c as f64 / FREQ as f64
}
}
/* ============================================================
* PE 入口
* ========================================================== */
/// ExitProcess wrapper: restore the kernel GS base before leaving.
fn exit_process(code: u32) -> ! {
unsafe {
rt::restore_tls();
ExitProcess(code);
}
}
#[no_mangle]
pub extern "C" fn mainCRTStartup() {
unsafe {
// Fake TEB/TLS must precede any std facility that might touch
// thread locals (parking_lot, std::io, etc.).
rt::install_fake_tls();
}
log("[deaicup] mainCRTStartup\n");
unsafe {
let hinst = GetModuleHandleW(core::ptr::null());
let class_name = wide("DeaicupWindow");
let wc = WNDCLASSEXW {
cbSize: core::mem::size_of::<WNDCLASSEXW>() as u32,
style: 0,
lpfnWndProc: wnd_proc,
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinst,
hIcon: 0,
hCursor: LoadCursorW(0, IDC_ARROW),
hbrBackground: 0,
lpszMenuName: core::ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: 0,
};
RegisterClassExW(&wc);
// 屏幕尺寸(shim 报 framebuffer 尺寸),窗口 800x600,超出则收缩
let sw = GetSystemMetrics(SM_CXSCREEN);
let sh = GetSystemMetrics(SM_CYSCREEN);
let mut ww = 800;
let mut wh = 600;
if sw > 0 && ww > sw {
ww = sw;
}
if sh > 0 && wh > sh {
wh = sh;
}
let title = wide("Deaicup Software");
let hwnd = CreateWindowExW(
0,
class_name.as_ptr(),
title.as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
ww,
wh,
0,
0,
hinst,
core::ptr::null_mut(),
);
if hwnd == 0 {
log("[deaicup] CreateWindowExW failed\n");
exit_process(1);
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 32bpp top-down DIB 后台缓冲
let hdc = GetDC(hwnd);
let memdc = CreateCompatibleDC(hdc);
let mut bmi = BITMAPINFO::default();
bmi.bmiHeader.biSize = core::mem::size_of::<BITMAPINFOHEADER>() as u32;
bmi.bmiHeader.biWidth = ww;
bmi.bmiHeader.biHeight = -wh; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
let mut bits: *mut core::ffi::c_void = core::ptr::null_mut();
let hbmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &mut bits, 0, 0);
if hbmp == 0 || bits.is_null() {
log("[deaicup] CreateDIBSection failed\n");
exit_process(1);
}
SelectObject(memdc, hbmp);
log("[dc] SelectObject ok\n");
/* 二分探针:逐项验证 std 设施,定位 Context::default 崩溃点 */
{
let mut v: Vec<u8> = Vec::with_capacity(16);
for i in 0..64u8 { v.push(i); }
log("[dc] vec ok\n");
let s = String::from("hello");
let _ = s.len();
log("[dc] string ok\n");
let mut h: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
h.insert(1, 2); h.insert(3, 4);
let _ = h.get(&1);
log("[dc] hashmap ok\n");
let a = std::sync::Arc::new(42u32);
let b = a.clone();
let _ = *b;
log("[dc] arc ok\n");
let m = std::sync::Mutex::new(7u32);
*m.lock().unwrap() = 8;
log("[dc] mutex ok\n");
}
let ctx = Context::default();
log("[dc] Context::default ok\n");
ctx.set_pixels_per_point(1.0);
let mut app = DeaicupSoftware::default();
let mut raster = SoftRaster::new();
let mut msg = MSG::default();
let mut frame: u64 = 0;
log("[dc] app+raster ok\n");
while !QUIT {
// 消息泵:把 Win32 消息转成 egui 事件(由 wnd_proc 推入 EVENTS)
while PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != 0 {
if msg.message == WM_QUIT {
QUIT = true;
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if QUIT {
break;
}
if frame < 3 { log("[dc] msgpump ok\n"); }
let events = core::mem::take(&mut *core::ptr::addr_of_mut!(EVENTS));
let had_events = !events.is_empty();
let input = egui::RawInput {
screen_rect: Some(Rect::from_min_size(
Pos2::ZERO,
egui::vec2(ww as f32, wh as f32),
)),
time: Some(now_secs()),
modifiers: current_modifiers(),
events,
..Default::default()
};
if frame < 3 { log("[dc] rawinput ok\n"); }
let full = ctx.run(input, |ctx| app.ui(&ctx));
if frame < 3 { log("[dc] ctx.run ok\n"); }
let repaint_after = full
.viewport_output
.values()
.next()
.map(|v| v.repaint_delay)
.unwrap_or(std::time::Duration::from_millis(16));
raster.apply_delta(&full.textures_delta);
if frame < 3 { log("[dc] apply_delta ok\n"); }
let prims = ctx.tessellate(full.shapes, full.pixels_per_point);
if frame < 3 { log("[dc] tessellate ok\n"); }
let dst = core::slice::from_raw_parts_mut(bits as *mut u32, (ww * wh) as usize);
raster::clear(dst, Color32::from_rgb(0x30, 0x34, 0x3d));
raster.paint(dst, ww, wh, &prims);
if frame < 3 { log("[dc] paint ok\n"); }
BitBlt(hdc, 0, 0, ww, wh, memdc, 0, 0, SRCCOPY);
if frame < 3 { log("[dc] bitblt ok\n"); }
frame += 1;
if frame > 2 && !had_events && repaint_after > std::time::Duration::from_millis(5) {
let ms = repaint_after.as_millis().min(50) as u32;
Sleep(if ms == 0 { 5 } else { ms });
}
}
log("[deaicup] quit, ExitProcess(0)\n");
exit_process(0);
}
}
-241
View File
@@ -1,241 +0,0 @@
//! Pure-Rust software rasterizer for egui tessellated primitives.
//! Renders `ClippedPrimitive` meshes (font atlas + color triangles) into a
//! 32bpp 0xAARRGGBB backbuffer that is later blitted via GDI BitBlt.
use egui::epaint::Primitive;
use egui::{ClippedPrimitive, Color32, Rect, TextureFilter, TextureId, TexturesDelta};
struct Texture {
w: usize,
h: usize,
/// RGBA8, row-major.
rgba: Vec<u8>,
linear: bool,
}
pub struct SoftRaster {
/// Small texture store (font atlas + user images). Vec-based to avoid
/// std HashMap RandomState (which would import an OS RNG API).
textures: Vec<(TextureId, Texture)>,
}
impl SoftRaster {
pub fn new() -> Self {
SoftRaster {
textures: Vec::new(),
}
}
fn get(&self, id: TextureId) -> Option<&Texture> {
self.textures.iter().find(|(tid, _)| *tid == id).map(|(_, t)| t)
}
/// Apply egui's texture updates (font atlas etc).
pub fn apply_delta(&mut self, delta: &TexturesDelta) {
for (id, img_delta) in &delta.set {
let size = img_delta.image.size();
let mut rgba = Vec::with_capacity(size[0] * size[1] * 4);
match &img_delta.image {
egui::ImageData::Color(color) => {
for p in &color.pixels {
rgba.extend_from_slice(&p.to_array());
}
}
egui::ImageData::Font(font) => {
// Coverage -> straight-alpha white texel (255,255,255,a).
for c in font.srgba_pixels(None) {
let a = c.a();
rgba.extend_from_slice(&[255, 255, 255, a]);
}
}
}
let linear = img_delta.options.magnification == TextureFilter::Linear;
let tex = Texture {
w: size[0],
h: size[1],
rgba,
linear,
};
if let Some(slot) = self.textures.iter_mut().find(|(tid, _)| tid == id) {
slot.1 = tex;
} else {
self.textures.push((*id, tex));
}
}
for id in &delta.free {
self.textures.retain(|(tid, _)| tid != id);
}
}
/// Paint all clipped primitives into `dst` (0xAARRGGBB pixels, dw*dh).
pub fn paint(&self, dst: &mut [u32], dw: i32, dh: i32, prims: &[ClippedPrimitive]) {
for cp in prims {
if let Primitive::Mesh(mesh) = &cp.primitive {
let Some(tex) = self.get(mesh.texture_id) else {
continue;
};
let clip = clip_rect_px(&cp.clip_rect, dw, dh);
if clip.2 <= clip.0 || clip.3 <= clip.1 {
continue;
}
for tri in mesh.indices.chunks_exact(3) {
let v0 = &mesh.vertices[tri[0] as usize];
let v1 = &mesh.vertices[tri[1] as usize];
let v2 = &mesh.vertices[tri[2] as usize];
raster_tri(dst, dw, dh, clip, v0, v1, v2, tex);
}
}
}
}
}
/// (x0, y0, x1, y1) integer clip bounds from an egui rect (points == pixels here).
fn clip_rect_px(r: &Rect, dw: i32, dh: i32) -> (i32, i32, i32, i32) {
let x0 = (r.min.x.floor() as i32).clamp(0, dw);
let y0 = (r.min.y.floor() as i32).clamp(0, dh);
let x1 = (r.max.x.ceil() as i32).clamp(0, dw);
let y1 = (r.max.y.ceil() as i32).clamp(0, dh);
(x0, y0, x1, y1)
}
#[inline]
fn edge(ax: f32, ay: f32, bx: f32, by: f32, px: f32, py: f32) -> f32 {
(px - ax) * (by - ay) - (py - ay) * (bx - ax)
}
fn raster_tri(
dst: &mut [u32],
dw: i32,
_dh: i32,
clip: (i32, i32, i32, i32),
v0: &egui::epaint::Vertex,
v1: &egui::epaint::Vertex,
v2: &egui::epaint::Vertex,
tex: &Texture,
) {
let x0 = v0.pos.x;
let y0 = v0.pos.y;
let x1 = v1.pos.x;
let y1 = v1.pos.y;
let x2 = v2.pos.x;
let y2 = v2.pos.y;
let area = edge(x0, y0, x1, y1, x2, y2);
if area.abs() < 1e-6 {
return;
}
let inv_area = 1.0 / area;
let min_x = ((x0.min(x1).min(x2)).floor() as i32).max(clip.0);
let min_y = ((y0.min(y1).min(y2)).floor() as i32).max(clip.1);
let max_x = ((x0.max(x1).max(x2)).ceil() as i32).min(clip.2);
let max_y = ((y0.max(y1).max(y2)).ceil() as i32).min(clip.3);
if min_x >= max_x || min_y >= max_y {
return;
}
for py in min_y..max_y {
let fy = py as f32 + 0.5;
let mut px = min_x;
// Row start barycentrics
let mut w0 = edge(x1, y1, x2, y2, px as f32 + 0.5, fy) * inv_area;
let mut w1 = edge(x2, y2, x0, y0, px as f32 + 0.5, fy) * inv_area;
let mut w2 = 1.0 - w0 - w1;
// Per-pixel increments (dx = +1).
// d/dpx edge(a, b, P) = (b.y - a.y):
// w0 = edge(p1, p2, P) -> (y2 - y1)
// w1 = edge(p2, p0, P) -> (y0 - y2)
// 符号写反会让 u/v/顶点色沿 +x 反向插值:文字逐行起始像素正确、
// 向右逐渐反向采样图集(字形水平镜像/竖条碎裂),纯色面板因
// 采样恒白区域不可见而掩盖了该 bug。
let dw0 = (y2 - y1) * inv_area;
let dw1 = (y0 - y2) * inv_area;
while px < max_x {
if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 {
let u = w0 * v0.uv.x + w1 * v1.uv.x + w2 * v2.uv.x;
let v = w0 * v0.uv.y + w1 * v1.uv.y + w2 * v2.uv.y;
let t = sample(tex, u, v);
let c0 = v0.color;
let c1 = v1.color;
let c2 = v2.color;
let cr = (w0 * c0.r() as f32 + w1 * c1.r() as f32 + w2 * c2.r() as f32) as u32;
let cg = (w0 * c0.g() as f32 + w1 * c1.g() as f32 + w2 * c2.g() as f32) as u32;
let cb = (w0 * c0.b() as f32 + w1 * c1.b() as f32 + w2 * c2.b() as f32) as u32;
let ca = (w0 * c0.a() as f32 + w1 * c1.a() as f32 + w2 * c2.a() as f32) as u32;
// src color = vertex * texture (straight), alpha = va * ta
let sa = (ca * t[3] as u32 + 127) / 255;
if sa != 0 {
let sr = (cr * t[0] as u32 + 127) / 255;
let sg = (cg * t[1] as u32 + 127) / 255;
let sb = (cb * t[2] as u32 + 127) / 255;
let idx = (py * dw + px) as usize;
let d = dst[idx];
let dr = (d >> 16) & 0xFF;
let dg = (d >> 8) & 0xFF;
let db = d & 0xFF;
let inv = 255 - sa;
let or = (sr * sa + dr * inv + 127) / 255;
let og = (sg * sa + dg * inv + 127) / 255;
let ob = (sb * sa + db * inv + 127) / 255;
dst[idx] = 0xFF00_0000 | (or << 16) | (og << 8) | ob;
}
}
px += 1;
w0 += dw0;
w1 += dw1;
w2 = 1.0 - w0 - w1;
}
}
}
#[inline]
fn sample(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
if tex.linear {
sample_linear(tex, u, v)
} else {
sample_nearest(tex, u, v)
}
}
#[inline]
fn sample_nearest(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let x = (u * tex.w as f32 - 0.5).round() as i32;
let y = (v * tex.h as f32 - 0.5).round() as i32;
texel(tex, x, y)
}
fn sample_linear(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let fx = u * tex.w as f32 - 0.5;
let fy = v * tex.h as f32 - 0.5;
let x0 = fx.floor() as i32;
let y0 = fy.floor() as i32;
let tx = ((fx - x0 as f32) * 256.0) as i32;
let ty = ((fy - y0 as f32) * 256.0) as i32;
let p00 = texel(tex, x0, y0);
let p10 = texel(tex, x0 + 1, y0);
let p01 = texel(tex, x0, y0 + 1);
let p11 = texel(tex, x0 + 1, y0 + 1);
let mut out = [0u8; 4];
for i in 0..4 {
let top = (p00[i] as i32 * (256 - tx) + p10[i] as i32 * tx) >> 8;
let bot = (p01[i] as i32 * (256 - tx) + p11[i] as i32 * tx) >> 8;
out[i] = ((top * (256 - ty) + bot * ty) >> 8) as u8;
}
out
}
#[inline]
fn texel(tex: &Texture, x: i32, y: i32) -> [u8; 4] {
let x = x.clamp(0, tex.w as i32 - 1) as usize;
let y = y.clamp(0, tex.h as i32 - 1) as usize;
let i = (y * tex.w + x) * 4;
[tex.rgba[i], tex.rgba[i + 1], tex.rgba[i + 2], tex.rgba[i + 3]]
}
/// Clear the whole backbuffer to an opaque color.
pub fn clear(dst: &mut [u32], c: Color32) {
let px = 0xFF00_0000 | ((c.r() as u32) << 16) | ((c.g() as u32) << 8) | c.b() as u32;
dst.fill(px);
}
-600
View File
@@ -1,600 +0,0 @@
//! Minimal CRT / libm runtime for the no-CRT PE64 build.
//!
//! The UTSM PE shim only resolves DLL imports (kernel32/user32/gdi32).
//! Symbols below are *link-time* references emitted by LLVM/std, so they must
//! live inside the PE image itself:
//! - string intrinsics: memcpy / memmove / memset / memcmp
//! - MSVC runtime: __chkstk, _fltused, _tls_index, _tls_used,
//! __CxxFrameHandler3 (never called, panic = abort)
//! - f32 libm: floorf/ceilf/roundf, sinf/cosf, atan2f, powf,
//! exp2f, acosf, cbrtf, _hypotf
//!
//! Math routines are ported from musl-libm (MIT) where accuracy matters
//! (sin/cos/atan) and use simple polynomial/series elsewhere. Visual use by
//! egui only needs ~1e-5 relative accuracy.
//!
//! TLS: Rust std on windows-msvc checks `_tls_used` and (if nonzero) accesses
//! thread locals through gs:[0x58] -> slots[_tls_index]. We are Ring0 with no
//! TEB, so mainCRTStartup installs a fake GS base pointing at a zeroed TLS
//! block (install_fake_tls) and restores it before ExitProcess.
#![allow(non_snake_case)]
use core::arch::asm;
/* ============================================================
* string intrinsics
* ========================================================== */
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep movsb",
inout("rcx") n => _,
inout("rsi") src => _,
inout("rdi") dst => _,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memmove(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let d = dst as usize;
let s = src as usize;
if d < s || d >= s.wrapping_add(n) {
memcpy(dst, src, n)
} else {
// Backward copy without touching DF (ABI requires DF=0).
let mut i = n;
while i > 0 {
i -= 1;
core::ptr::write_volatile(dst.add(i), core::ptr::read_volatile(src.add(i)));
}
dst
}
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memset(dst: *mut u8, val: i32, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep stosb",
inout("rcx") n => _,
inout("rdi") dst => _,
in("al") val as u8,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
// Volatile reads keep LLVM from idiom-recognizing this back into memcmp.
for i in 0..n {
let x = core::ptr::read_volatile(a.add(i));
let y = core::ptr::read_volatile(b.add(i));
if x != y {
return x as i32 - y as i32;
}
}
0
}
/* ============================================================
* MSVC runtime bits
* ========================================================== */
/// Stack probe. Our stack is one large committed region (see /STACK flag),
/// so probing pages is unnecessary. An empty body compiles to `ret`,
/// which trivially satisfies the preserve-everything contract.
#[no_mangle]
pub extern "C" fn __chkstk() {}
/// Referenced by std's unwind tables (.pdata personality). With
/// panic = "abort" it is never invoked; loop loudly if it ever is.
#[no_mangle]
pub extern "C" fn __CxxFrameHandler3() -> ! {
loop {
core::hint::spin_loop();
}
}
/// CRT float-usage marker (data symbol only).
#[no_mangle]
pub static _fltused: i32 = 0x9875;
/// TLS slot index of this module. We use slot 0 of the fake TLS array.
#[no_mangle]
pub static _tls_index: u32 = 0;
/// IMAGE_TLS_DIRECTORY64 stand-in (all zeros: no raw template, no callbacks).
/// std only checks that its address is nonzero to decide TLS is supported.
#[no_mangle]
pub static _tls_used: [u8; 0x30] = [0; 0x30];
/* ---- fake TEB / TLS so std thread_local access can't fault ---- */
const IA32_GS_BASE: u32 = 0xC000_0101;
static mut OLD_GS_BASE: u64 = 0;
static mut TLS_READY: bool = false;
fn rdmsr(msr: u32) -> u64 {
let lo: u32;
let hi: u32;
unsafe {
asm!("rdmsr", in("ecx") msr, out("eax") lo, out("edx") hi,
options(nomem, nostack, preserves_flags));
}
((hi as u64) << 32) | lo as u64
}
fn wrmsr(msr: u32, val: u64) {
unsafe {
asm!("wrmsr", in("ecx") msr, in("eax") val as u32,
in("edx") (val >> 32) as u32, options(nomem, nostack, preserves_flags));
}
}
/// Point GS at a minimal fake TEB whose ThreadLocalStoragePointer (0x58)
/// references a zeroed slots array; slot 0 covers `_tls_index == 0`.
/// All const-init thread locals in std are zero-valid (None/0).
///
/// # Safety
/// Ring0 only; call once at PE entry before touching std facilities.
pub unsafe fn install_fake_tls() {
static mut TEB: [u8; 0x100] = [0; 0x100];
static mut SLOTS: [usize; 64] = [0; 64];
static mut TLS_BLOCK: [u8; 8192] = [0; 8192];
if TLS_READY {
return;
}
let teb = core::ptr::addr_of_mut!(TEB);
let slots = core::ptr::addr_of_mut!(SLOTS);
let block = core::ptr::addr_of_mut!(TLS_BLOCK);
(*slots)[0] = block as usize;
*((*teb).as_mut_ptr().add(0x58) as *mut usize) = slots as usize;
OLD_GS_BASE = rdmsr(IA32_GS_BASE);
wrmsr(IA32_GS_BASE, teb as u64);
TLS_READY = true;
}
/// Restore the kernel's GS base (call before ExitProcess).
///
/// # Safety
/// Ring0 only; pair with install_fake_tls.
pub unsafe fn restore_tls() {
if TLS_READY {
wrmsr(IA32_GS_BASE, OLD_GS_BASE);
TLS_READY = false;
}
}
/* ============================================================
* f32 libm
* ========================================================== */
#[inline(always)]
fn asu32(x: f32) -> u32 {
x.to_bits()
}
#[inline(always)]
fn asf32(x: u32) -> f32 {
f32::from_bits(x)
}
#[no_mangle]
pub extern "C" fn floorf(x: f32) -> f32 {
// |x| >= 2^23 is already integral; NaN passes the compare false and
// degrades to 0.0, which egui never feeds us anyway.
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32; // truncate toward zero
let f = i as f32;
if f > x {
f - 1.0
} else {
f
}
}
#[no_mangle]
pub extern "C" fn ceilf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32;
let f = i as f32;
if f < x {
f + 1.0
} else {
f
}
}
/// C semantics: round half away from zero.
#[no_mangle]
pub extern "C" fn roundf(x: f32) -> f32 {
if x >= 0.0 {
floorf(x + 0.5)
} else {
ceilf(x - 0.5)
}
}
#[no_mangle]
pub extern "C" fn truncf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
x as i32 as f32
}
/// f64 truncation toward zero (LLVM libcall on SSE2-only targets).
#[no_mangle]
pub extern "C" fn trunc(x: f64) -> f64 {
// 2^52: every f64 with |x| >= 2^52 is already integral.
if x.abs() >= 4503599627370496.0 {
return x;
}
x as i64 as f64
}
/* ---- sin/cos: musl __sindf/__cosdf + medium Cody-Waite reduction ---- */
// |sin(x)/x - s(x)| < 2**-27.5 on the reduced range.
const S1: f64 = -0.166666666416265235595;
const S2: f64 = 0.0083333293858894631756;
const S3: f64 = -0.000198393348360966317347;
const S4: f64 = 0.0000027183114939898219064;
const C0: f64 = -0.499999997251031003120;
const C1: f64 = 0.0416666233237390631894;
const C2: f64 = -0.00138867637746099294692;
const C3: f64 = 0.0000243904487962774090654;
const PIO2_HI: f64 = 1.57079625129699707031e+00;
const PIO2_LO: f64 = 7.54978941586159635335e-08;
const INV_PIO2: f64 = 6.36619772367581382433e-01; // 2/pi
const TOINT: f64 = 1.5 / f64::EPSILON;
const TWO_PI: f64 = 6.28318530717958647693;
#[inline]
fn sindf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = S3 + z * S4;
let s = z * x;
(x + s * (S1 + z * S2)) + s * w * r
}
#[inline]
fn cosdf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = C2 + z * C3;
((1.0 + z * C0) + w * C1) + (w * z) * r
}
/// Reduce x modulo pi/2 (Cody-Waite, double precision). Returns (n, y)
/// with y = x - n*pi/2 accurate for moderate |x|.
#[inline]
fn rem_pio2_medium(x: f64) -> (i32, f64) {
// Coarse pre-reduction keeps the accurate path valid for any angle egui
// can produce (ellipse/bezier angles are already tiny).
let x = if x.abs() > 105.0 {
x - (x / TWO_PI).trunc() * TWO_PI
} else {
x
};
let fn_ = x * INV_PIO2 + TOINT - TOINT;
let n = fn_ as i32;
(n, x - fn_ * PIO2_HI - fn_ * PIO2_LO)
}
#[no_mangle]
pub extern "C" fn sinf(x: f32) -> f32 {
if x.abs() < 1.0e-30 {
return x;
}
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => sindf(y),
1 => cosdf(y),
2 => sindf(-y),
_ => -cosdf(y),
}) as f32
}
#[no_mangle]
pub extern "C" fn cosf(x: f32) -> f32 {
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => cosdf(y),
1 => sindf(-y),
2 => -cosdf(y),
_ => sindf(y),
}) as f32
}
/* ---- atan2f: musl atanf + quadrant fix ---- */
const ATAN_HI: [f32; 4] = [
4.6364760399e-01,
7.8539812565e-01,
9.8279371262e-01,
1.5707962513e+00,
];
const ATAN_LO: [f32; 4] = [
8.8023035407e-08,
1.2093354704e-08,
1.2587686365e-08,
7.5497894159e-08,
];
const AT: [f32; 5] = [
3.3333328366e-01,
-1.9999158382e-01,
1.4253635705e-01,
-1.0648017377e-01,
6.1687607318e-02,
];
const PI_F: f32 = 3.1415927410e+00;
fn atanf(x: f32) -> f32 {
let mut x = x;
let ix = asu32(x);
let sign = ix >> 31;
let aix = ix & 0x7fffffff;
if aix >= 0x4c800000 {
// |x| >= 2^26
if x.is_nan() {
return x;
}
let z = ATAN_HI[3] + 7.5231638e-37;
return if sign != 0 { -z } else { z };
}
let id: i32;
if aix < 0x3ee00000 {
// |x| < 0.4375
if aix < 0x39800000 {
return x; // |x| < 2^-12: atan(x) ~ x
}
id = -1;
} else {
x = x.abs();
if aix < 0x3f980000 {
if aix < 0x3f300000 {
id = 0;
x = (2.0 * x - 1.0) / (2.0 + x);
} else {
id = 1;
x = (x - 1.0) / (x + 1.0);
}
} else if aix < 0x401c0000 {
id = 2;
x = (x - 1.5) / (1.0 + 1.5 * x);
} else {
id = 3;
x = -1.0 / x;
}
}
let z = x * x;
let w = z * z;
let s1 = z * (AT[0] + w * (AT[2] + w * AT[4]));
let s2 = w * (AT[1] + w * AT[3]);
if id < 0 {
return x - x * (s1 + s2);
}
let i = id as usize;
let z2 = ATAN_HI[i] - ((x * (s1 + s2) - ATAN_LO[i]) - x);
if sign != 0 {
-z2
} else {
z2
}
}
#[no_mangle]
pub extern "C" fn atan2f(y: f32, x: f32) -> f32 {
if y == 0.0 {
return if x < 0.0 { PI_F } else { y }; // crude, matches C for our uses
}
if x == 0.0 {
return if y < 0.0 { -PI_F / 2.0 } else { PI_F / 2.0 };
}
let ax = x.abs();
let ay = y.abs();
let z = if ax > ay {
atanf(ay / ax)
} else {
PI_F / 2.0 - atanf(ax / ay)
};
if x < 0.0 {
if y < 0.0 {
z - PI_F
} else {
PI_F - z
}
} else if y < 0.0 {
-z
} else {
z
}
}
/* ---- exp2f / log2f / powf ---- */
/// ldexp-style scaling by 2^n (n clamped to f32 range by callers).
fn scalbnf(mut r: f32, mut n: i32) -> f32 {
while n > 127 {
r *= asf32(0x7F00_0000); // 2^127
n -= 127;
}
while n < -126 {
r *= asf32(0x0080_0000); // 2^-126
n += 126;
}
r * asf32(((127 + n) as u32) << 23)
}
#[no_mangle]
pub extern "C" fn exp2f(x: f32) -> f32 {
if x > 128.0 {
return f32::INFINITY;
}
if x < -150.0 {
return 0.0;
}
// n = round-to-nearest-even(x) via the magic-number trick.
let t = x + 12582912.0; // 1.5 * 2^23
let nf = t - 12582912.0;
let n = nf as i32;
let f = x - nf; // in [-0.5, 0.5]
// 2^f: Taylor in ln2, degree 6 -> ~1e-10 rel error on [-0.5, 0.5].
let p = 1.0
+ f * (0.6931471805599453
+ f * (0.24022650695910070
+ f * (0.05550410866482158
+ f * (0.009618129107628477
+ f * (0.0013333558146428443 + f * 0.00015403530393381609)))));
scalbnf(p, n)
}
/// log2(x) for x > 0; accuracy ~1e-7 via atanh series in double.
fn log2f(x: f32) -> f32 {
let ix = asu32(x);
let e = ((ix >> 23) as i32) - 127;
let m = asf32((ix & 0x007F_FFFF) | 0x3F80_0000); // [1, 2)
let t = ((m - 1.0) / (m + 1.0)) as f64;
let t2 = t * t;
let mut sum = 0.0f64;
let mut term = t;
for k in 0..8 {
sum += term / (2 * k + 1) as f64;
term *= t2;
}
let ln_m = 2.0 * sum;
(e as f64 + ln_m * 1.4426950408889634) as f32
}
#[no_mangle]
pub extern "C" fn powf(x: f32, y: f32) -> f32 {
if y == 0.0 {
return 1.0;
}
if x == 1.0 {
return 1.0;
}
if x == 0.0 {
return 0.0;
}
if x == 2.0 {
return exp2f(y);
}
if x < 0.0 {
let yi = y as i32;
if yi as f32 == y {
let r = exp2f(y * log2f(-x));
return if yi & 1 != 0 { -r } else { r };
}
return f32::NAN;
}
exp2f(y * log2f(x))
}
/* ---- acosf: musl ---- */
const PIO2_HI_F: f32 = 1.5707962513e+00;
const PIO2_LO_F: f32 = 7.5497894159e-08;
const PS0: f32 = 1.6666586697e-01;
const PS1: f32 = -4.2743422091e-02;
const PS2: f32 = -8.6563630030e-03;
const QS1: f32 = -7.0662963390e-01;
#[inline]
fn acos_r(z: f32) -> f32 {
z * (PS0 + z * (PS1 + z * PS2)) / (1.0 + z * QS1)
}
#[no_mangle]
pub extern "C" fn acosf(x: f32) -> f32 {
let hx = asu32(x);
let ix = hx & 0x7fffffff;
if ix >= 0x3f800000 {
// |x| >= 1
if ix == 0x3f800000 {
if hx >> 31 != 0 {
return 2.0 * PIO2_HI_F; // acos(-1) = pi
}
return 0.0; // acos(1) = 0
}
return f32::NAN;
}
if ix < 0x3f000000 {
// |x| < 0.5
if ix <= 0x32800000 {
return PIO2_HI_F; // |x| < 2^-26
}
return PIO2_HI_F - (x - (PIO2_LO_F - x * acos_r(x * x)));
}
if hx >> 31 != 0 {
// x < -0.5
let z = (1.0 + x) * 0.5;
let s = z.sqrt();
let w = acos_r(z) * s - PIO2_LO_F;
return 2.0 * PIO2_HI_F - 2.0 * (s + w);
}
// x > 0.5
let z = (1.0 - x) * 0.5;
let s = z.sqrt();
let df = asf32(asu32(s) & 0xFFFFF000); // keep df*df rounding-exact
let c = (z - df * df) / (s + df);
let w = acos_r(z) * s + c;
2.0 * (df + w)
}
/* ---- cbrtf / hypotf ---- */
#[no_mangle]
pub extern "C" fn cbrtf(x: f32) -> f32 {
if x == 0.0 {
return x;
}
let r = exp2f(log2f(x.abs()) / 3.0);
if x < 0.0 {
-r
} else {
r
}
}
#[no_mangle]
pub extern "C" fn _hypotf(x: f32, y: f32) -> f32 {
let ax = x.abs();
let ay = y.abs();
let (a, b) = if ax > ay { (ax, ay) } else { (ay, ax) };
if a == 0.0 {
return 0.0;
}
let r = b / a;
a * (1.0 + r * r).sqrt()
}
/// Plain alias in case LLVM references the C99 spelling.
#[no_mangle]
pub extern "C" fn hypotf(x: f32, y: f32) -> f32 {
_hypotf(x, y)
}
-315
View File
@@ -1,315 +0,0 @@
//! Minimal Win32 bindings used by the Deaicup PE backend.
//! Only the API subset that the Deshab UTSM PE shim implements is declared here.
//! All imports use raw-dylib so rust-lld can emit the import table without
//! any MSVC import libraries.
#![allow(non_snake_case, non_camel_case_types, dead_code)]
pub type HWND = isize;
pub type HDC = isize;
pub type HINSTANCE = isize;
pub type HICON = isize;
pub type HCURSOR = isize;
pub type HBRUSH = isize;
pub type HMENU = isize;
pub type HGDIOBJ = isize;
pub type HBITMAP = isize;
pub type HANDLE = isize;
pub type ATOM = u16;
pub type WPARAM = usize;
pub type LPARAM = isize;
pub type LRESULT = isize;
pub type BOOL = i32;
pub type UINT = u32;
pub type DWORD = u32;
pub type WORD = u16;
pub type LPCWSTR = *const u16;
pub const CW_USEDEFAULT: i32 = 0x80000000u32 as i32;
pub const WS_OVERLAPPED: u32 = 0x00000000;
pub const WS_CAPTION: u32 = 0x00C00000;
pub const WS_SYSMENU: u32 = 0x00080000;
pub const WS_THICKFRAME: u32 = 0x00040000;
pub const WS_MINIMIZEBOX: u32 = 0x00020000;
pub const WS_MAXIMIZEBOX: u32 = 0x00010000;
pub const WS_VISIBLE: u32 = 0x10000000;
pub const WS_OVERLAPPEDWINDOW: u32 = WS_OVERLAPPED
| WS_CAPTION
| WS_SYSMENU
| WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX;
pub const SW_SHOW: i32 = 5;
pub const WM_CREATE: u32 = 0x0001;
pub const WM_DESTROY: u32 = 0x0002;
pub const WM_SIZE: u32 = 0x0005;
pub const WM_PAINT: u32 = 0x000F;
pub const WM_CLOSE: u32 = 0x0010;
pub const WM_QUIT: u32 = 0x0012;
pub const WM_ERASEBKGND: u32 = 0x0014;
pub const WM_KEYDOWN: u32 = 0x0100;
pub const WM_KEYUP: u32 = 0x0101;
pub const WM_CHAR: u32 = 0x0102;
pub const WM_TIMER: u32 = 0x0113;
pub const WM_MOUSEMOVE: u32 = 0x0200;
pub const WM_LBUTTONDOWN: u32 = 0x0201;
pub const WM_LBUTTONUP: u32 = 0x0202;
pub const WM_RBUTTONDOWN: u32 = 0x0204;
pub const WM_RBUTTONUP: u32 = 0x0205;
pub const WM_MBUTTONDOWN: u32 = 0x0207;
pub const WM_MBUTTONUP: u32 = 0x0208;
pub const PM_NOREMOVE: u32 = 0x0000;
pub const PM_REMOVE: u32 = 0x0001;
pub const VK_BACK: usize = 0x08;
pub const VK_TAB: usize = 0x09;
pub const VK_RETURN: usize = 0x0D;
pub const VK_SHIFT: usize = 0x10;
pub const VK_CONTROL: usize = 0x11;
pub const VK_MENU: usize = 0x12;
pub const VK_ESCAPE: usize = 0x1B;
pub const VK_SPACE: usize = 0x20;
pub const VK_LEFT: usize = 0x25;
pub const VK_UP: usize = 0x26;
pub const VK_RIGHT: usize = 0x27;
pub const VK_DOWN: usize = 0x28;
pub const VK_DELETE: usize = 0x2E;
pub const IDC_ARROW: usize = 32512;
pub const COLOR_WINDOW: u32 = 5;
pub const GWLP_USERDATA: i32 = -21;
pub const GWLP_WNDPROC: i32 = -4;
pub const SM_CXSCREEN: i32 = 0;
pub const SM_CYSCREEN: i32 = 1;
pub const SRCCOPY: u32 = 0x00CC0020;
pub const DIB_RGB_COLORS: u32 = 0;
pub const BI_RGB: u32 = 0;
pub const WHITE_BRUSH: i32 = 0;
pub const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5;
pub const STD_ERROR_HANDLE: u32 = 0xFFFFFFF4;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct POINT {
pub x: i32,
pub y: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RECT {
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct MSG {
pub hwnd: HWND,
pub message: u32,
pub wParam: WPARAM,
pub lParam: LPARAM,
pub time: u32,
pub pt: POINT,
}
#[repr(C)]
pub struct WNDCLASSEXW {
pub cbSize: UINT,
pub style: UINT,
pub lpfnWndProc: WNDPROC,
pub cbClsExtra: i32,
pub cbWndExtra: i32,
pub hInstance: HINSTANCE,
pub hIcon: HICON,
pub hCursor: HCURSOR,
pub hbrBackground: HBRUSH,
pub lpszMenuName: LPCWSTR,
pub lpszClassName: LPCWSTR,
pub hIconSm: HICON,
}
pub type WNDPROC = extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT;
#[repr(C)]
pub struct PAINTSTRUCT {
pub hdc: HDC,
pub fErase: BOOL,
pub rcPaint: RECT,
pub fRestore: BOOL,
pub fIncUpdate: BOOL,
pub rgbReserved: [u8; 32],
}
impl Default for PAINTSTRUCT {
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFOHEADER {
pub biSize: u32,
pub biWidth: i32,
pub biHeight: i32,
pub biPlanes: u16,
pub biBitCount: u16,
pub biCompression: u32,
pub biSizeImage: u32,
pub biXPelsPerMeter: i32,
pub biYPelsPerMeter: i32,
pub biClrUsed: u32,
pub biClrImportant: u32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RGBQUAD {
pub rgbBlue: u8,
pub rgbGreen: u8,
pub rgbRed: u8,
pub rgbReserved: u8,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFO {
pub bmiHeader: BITMAPINFOHEADER,
pub bmiColors: [RGBQUAD; 1],
}
#[link(name = "user32", kind = "raw-dylib")]
extern "system" {
pub fn RegisterClassExW(lpwcx: *const WNDCLASSEXW) -> ATOM;
pub fn CreateWindowExW(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
x: i32,
y: i32,
nWidth: i32,
nHeight: i32,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: *mut core::ffi::c_void,
) -> HWND;
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
pub fn ShowWindow(hWnd: HWND, nCmdShow: i32) -> BOOL;
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
pub fn GetMessageW(lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
pub fn PeekMessageW(
lpMsg: *mut MSG,
hWnd: HWND,
wMsgFilterMin: u32,
wMsgFilterMax: u32,
wRemoveMsg: UINT,
) -> BOOL;
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
pub fn PostQuitMessage(nExitCode: i32);
pub fn DefWindowProcW(hWnd: HWND, msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
pub fn GetClientRect(hWnd: HWND, lpRect: *mut RECT) -> BOOL;
pub fn InvalidateRect(hWnd: HWND, lpRect: *const RECT, bErase: BOOL) -> BOOL;
pub fn BeginPaint(hWnd: HWND, lpPaint: *mut PAINTSTRUCT) -> HDC;
pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
pub fn GetDC(hWnd: HWND) -> HDC;
pub fn ReleaseDC(hWnd: HWND, hDC: HDC) -> i32;
pub fn LoadCursorW(hInstance: HINSTANCE, lpCursorName: usize) -> HCURSOR;
pub fn SetCursor(hCursor: HCURSOR) -> HCURSOR;
pub fn GetCursorPos(lpPoint: *mut POINT) -> BOOL;
pub fn ScreenToClient(hWnd: HWND, lpPoint: *mut POINT) -> BOOL;
pub fn GetAsyncKeyState(vKey: i32) -> i16;
pub fn SetTimer(hWnd: HWND, nIDEvent: usize, uElapse: UINT, lpTimerFunc: usize) -> usize;
pub fn KillTimer(hWnd: HWND, uIDEvent: usize) -> BOOL;
pub fn MessageBeep(uType: UINT) -> BOOL;
pub fn GetSystemMetrics(nIndex: i32) -> i32;
}
#[link(name = "gdi32", kind = "raw-dylib")]
extern "system" {
pub fn CreateCompatibleDC(hdc: HDC) -> HDC;
pub fn CreateDIBSection(
hdc: HDC,
pbmi: *const BITMAPINFO,
usage: UINT,
ppvBits: *mut *mut core::ffi::c_void,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP;
pub fn SelectObject(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ;
pub fn DeleteObject(ho: HGDIOBJ) -> BOOL;
pub fn DeleteDC(hdc: HDC) -> BOOL;
pub fn BitBlt(
hdc: HDC,
x: i32,
y: i32,
cx: i32,
cy: i32,
hdcSrc: HDC,
x1: i32,
y1: i32,
rop: DWORD,
) -> BOOL;
pub fn GetStockObject(i: i32) -> HGDIOBJ;
pub fn SetDIBitsToDevice(
hdc: HDC,
xDest: i32,
yDest: i32,
w: DWORD,
h: DWORD,
xSrc: i32,
ySrc: i32,
startScan: UINT,
numLines: UINT,
lpvBits: *const core::ffi::c_void,
lpbmi: *const BITMAPINFO,
colorUse: UINT,
) -> i32;
pub fn GdiFlush() -> BOOL;
}
#[link(name = "kernel32", kind = "raw-dylib")]
extern "system" {
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HINSTANCE;
pub fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
pub fn WriteFile(
hFile: HANDLE,
lpBuffer: *const u8,
nNumberOfBytesToWrite: DWORD,
lpNumberOfBytesWritten: *mut DWORD,
lpOverlapped: *mut core::ffi::c_void,
) -> BOOL;
pub fn ExitProcess(uExitCode: UINT) -> !;
pub fn GetCommandLineA() -> *const u8;
pub fn GetTickCount() -> DWORD;
pub fn QueryPerformanceCounter(lpPerformanceCount: *mut i64) -> BOOL;
pub fn QueryPerformanceFrequency(lpFrequency: *mut i64) -> BOOL;
pub fn Sleep(dwMilliseconds: DWORD);
}
/// UTF-16 helper: build a null-terminated wide string.
pub fn wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(core::iter::once(0)).collect()
}
/// Debug log through the shim's stdout (serial).
pub fn log(s: &str) {
unsafe {
let h = GetStdHandle(STD_OUTPUT_HANDLE);
let mut written: DWORD = 0;
WriteFile(h, s.as_ptr(), s.len() as DWORD, &mut written, core::ptr::null_mut());
}
}
-4639
View File
@@ -1,4639 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "Deaicup-Software"
version = "0.1.0"
dependencies = [
"eframe",
"egui",
"egui_extras",
]
[[package]]
name = "ab_glyph"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "accesskit"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b"
dependencies = [
"enumn",
"serde",
]
[[package]]
name = "accesskit_consumer"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6"
dependencies = [
"accesskit",
]
[[package]]
name = "accesskit_macos"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7"
dependencies = [
"accesskit",
"accesskit_consumer",
"objc2 0.3.0-beta.3.patch-leaks.3",
"once_cell",
]
[[package]]
name = "accesskit_unix"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08"
dependencies = [
"accesskit",
"accesskit_consumer",
"async-channel",
"async-once-cell",
"atspi",
"futures-lite 1.13.0",
"once_cell",
"serde",
"zbus",
]
[[package]]
name = "accesskit_windows"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b"
dependencies = [
"accesskit",
"accesskit_consumer",
"once_cell",
"paste",
"static_assertions",
"windows 0.48.0",
]
[[package]]
name = "accesskit_winit"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67"
dependencies = [
"accesskit",
"accesskit_macos",
"accesskit_unix",
"accesskit_windows",
"winit",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"getrandom 0.3.4",
"once_cell",
"serde",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-activity"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
dependencies = [
"android-properties",
"bitflags 2.13.1",
"cc",
"cesu8",
"jni",
"jni-sys 0.3.1",
"libc",
"log",
"ndk",
"ndk-context",
"ndk-sys",
"num_enum",
"thiserror 1.0.69",
]
[[package]]
name = "android-properties"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arboard"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
dependencies = [
"clipboard-win",
"log",
"objc2 0.6.4",
"objc2-app-kit",
"objc2-foundation",
"parking_lot",
"percent-encoding",
"windows-sys 0.60.2",
"x11rb",
]
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "as-raw-xcb-connection"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
dependencies = [
"libloading 0.7.4",
]
[[package]]
name = "async-broadcast"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
dependencies = [
"event-listener 2.5.3",
"futures-core",
]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand 2.5.0",
"futures-lite 2.6.1",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-fs"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
dependencies = [
"async-lock 2.8.0",
"autocfg",
"blocking",
"futures-lite 1.13.0",
]
[[package]]
name = "async-io"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock 2.8.0",
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-lite 1.13.0",
"log",
"parking",
"polling 2.8.0",
"rustix 0.37.28",
"slab",
"socket2",
"waker-fn",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite 2.6.1",
"parking",
"polling 3.11.0",
"rustix 1.1.4",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
dependencies = [
"event-listener 2.5.3",
]
[[package]]
name = "async-lock"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener 5.4.2",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-once-cell"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]]
name = "async-process"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
dependencies = [
"async-io 1.13.0",
"async-lock 2.8.0",
"async-signal",
"blocking",
"cfg-if",
"event-listener 3.1.0",
"futures-lite 1.13.0",
"rustix 0.38.44",
"windows-sys 0.48.0",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "async-signal"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
dependencies = [
"async-io 2.6.0",
"async-lock 3.4.2",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.4",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atspi"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca"
dependencies = [
"atspi-common",
"atspi-connection",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5"
dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zbus",
"zbus_names",
"zvariant",
]
[[package]]
name = "atspi-connection"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939"
dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite 1.13.0",
"zbus",
]
[[package]]
name = "atspi-proxies"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
dependencies = [
"atspi-common",
"serde",
"zbus",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-sys"
version = "0.1.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146"
dependencies = [
"objc-sys 0.2.0-beta.2",
]
[[package]]
name = "block-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
dependencies = [
"objc-sys 0.3.5",
]
[[package]]
name = "block2"
version = "0.2.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42"
dependencies = [
"block-sys 0.1.0-beta.1",
"objc2-encode 2.0.0-pre.2",
]
[[package]]
name = "block2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
dependencies = [
"block-sys 0.2.1",
"objc2 0.4.1",
]
[[package]]
name = "blocking"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite 2.6.1",
"piper",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "calloop"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
dependencies = [
"bitflags 2.13.1",
"log",
"polling 3.11.0",
"rustix 0.38.44",
"slab",
"thiserror 1.0.69",
]
[[package]]
name = "calloop"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7"
dependencies = [
"bitflags 2.13.1",
"polling 3.11.0",
"rustix 1.1.4",
"slab",
"tracing",
]
[[package]]
name = "calloop-wayland-source"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
dependencies = [
"calloop 0.12.4",
"rustix 0.38.44",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "calloop-wayland-source"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
dependencies = [
"calloop 0.14.4",
"rustix 1.1.4",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "cc"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "cgl"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
dependencies = [
"libc",
]
[[package]]
name = "clipboard-win"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
"error-code",
]
[[package]]
name = "cocoa"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
dependencies = [
"bitflags 1.3.2",
"block",
"cocoa-foundation",
"core-foundation",
"core-graphics",
"foreign-types",
"libc",
"objc",
]
[[package]]
name = "cocoa-foundation"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
dependencies = [
"bitflags 1.3.2",
"block",
"core-foundation",
"core-graphics-types",
"libc",
"objc",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "com"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
dependencies = [
"com_macros",
]
[[package]]
name = "com_macros"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
dependencies = [
"com_macros_support",
"proc-macro2",
"syn 1.0.109",
]
[[package]]
name = "com_macros_support"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-graphics"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "cursor-icon"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dispatch2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
]
[[package]]
name = "displaydoc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "dlib"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
dependencies = [
"libloading 0.8.9",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "ecolor"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
dependencies = [
"bytemuck",
"serde",
]
[[package]]
name = "eframe"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2"
dependencies = [
"bytemuck",
"cocoa",
"document-features",
"egui",
"egui-wgpu",
"egui-winit",
"egui_glow",
"glow",
"glutin",
"glutin-winit",
"image",
"js-sys",
"log",
"objc",
"parking_lot",
"percent-encoding",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"static_assertions",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"web-time",
"winapi",
"winit",
]
[[package]]
name = "egui"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
dependencies = [
"accesskit",
"ahash",
"epaint",
"log",
"nohash-hasher",
"serde",
]
[[package]]
name = "egui-wgpu"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
dependencies = [
"bytemuck",
"document-features",
"egui",
"epaint",
"log",
"thiserror 1.0.69",
"type-map",
"web-time",
"wgpu",
"winit",
]
[[package]]
name = "egui-winit"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
dependencies = [
"accesskit_winit",
"arboard",
"egui",
"log",
"raw-window-handle 0.6.2",
"smithay-clipboard",
"web-time",
"webbrowser",
"winit",
]
[[package]]
name = "egui_extras"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
dependencies = [
"egui",
"ehttp",
"enum-map",
"image",
"log",
"mime_guess2",
"resvg",
"serde",
]
[[package]]
name = "egui_glow"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
dependencies = [
"bytemuck",
"egui",
"glow",
"log",
"memoffset 0.9.1",
"wasm-bindgen",
"web-sys",
"winit",
]
[[package]]
name = "ehttp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876"
dependencies = [
"document-features",
"js-sys",
"ureq",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "emath"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
dependencies = [
"bytemuck",
"serde",
]
[[package]]
name = "enum-map"
version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
dependencies = [
"enum-map-derive",
"serde",
]
[[package]]
name = "enum-map-derive"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "enumn"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "epaint"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
dependencies = [
"ab_glyph",
"ahash",
"bytemuck",
"ecolor",
"emath",
"log",
"nohash-hasher",
"parking_lot",
"serde",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "error-code"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "event-listener"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener"
version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
dependencies = [
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener 5.4.2",
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-core"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
[[package]]
name = "futures-io"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand 1.9.0",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand 2.5.0",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-sink"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
[[package]]
name = "futures-task"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix 1.1.4",
"windows-link",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi 5.3.0",
"wasip2",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
]
[[package]]
name = "gl_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
dependencies = [
"khronos_api",
"log",
"xml-rs",
]
[[package]]
name = "glow"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
dependencies = [
"js-sys",
"slotmap",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "glutin"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
dependencies = [
"bitflags 2.13.1",
"cfg_aliases",
"cgl",
"core-foundation",
"dispatch",
"glutin_egl_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
"icrate",
"libloading 0.8.9",
"objc2 0.4.1",
"once_cell",
"raw-window-handle 0.5.2",
"wayland-sys",
"windows-sys 0.48.0",
"x11-dl",
]
[[package]]
name = "glutin-winit"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
dependencies = [
"cfg_aliases",
"glutin",
"raw-window-handle 0.5.2",
"winit",
]
[[package]]
name = "glutin_egl_sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd"
dependencies = [
"gl_generator",
"windows-sys 0.48.0",
]
[[package]]
name = "glutin_glx_sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f"
dependencies = [
"gl_generator",
"x11-dl",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
dependencies = [
"gl_generator",
]
[[package]]
name = "gpu-alloc"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9"
dependencies = [
"bitflags 2.13.1",
"gpu-alloc-types",
]
[[package]]
name = "gpu-alloc-types"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "gpu-allocator"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
dependencies = [
"log",
"presser",
"thiserror 1.0.69",
"winapi",
"windows 0.52.0",
]
[[package]]
name = "gpu-descriptor"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
dependencies = [
"bitflags 2.13.1",
"gpu-descriptor-types",
"hashbrown 0.14.5",
]
[[package]]
name = "gpu-descriptor-types"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hassle-rs"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
dependencies = [
"bitflags 2.13.1",
"com",
"libc",
"libloading 0.8.9",
"thiserror 1.0.69",
"widestring",
"winapi",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hexf-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "home"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "icrate"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
dependencies = [
"block2 0.3.0",
"dispatch",
"objc2 0.4.1",
]
[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "image"
version = "0.24.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-traits",
"png",
]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys 0.3.1",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
dependencies = [
"jni-sys 0.4.1",
]
[[package]]
name = "jni-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
dependencies = [
"jni-sys-macros",
]
[[package]]
name = "jni-sys-macros"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
dependencies = [
"quote",
"syn 2.0.119",
]
[[package]]
name = "jobserver"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom 0.4.3",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "khronos-egl"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
dependencies = [
"libc",
"libloading 0.8.9",
"pkg-config",
]
[[package]]
name = "khronos_api"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libredox"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
dependencies = [
"bitflags 2.13.1",
"libc",
"plain",
"redox_syscall 0.9.1",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memmap2"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "metal"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
dependencies = [
"bitflags 2.13.1",
"block",
"core-graphics-types",
"foreign-types",
"log",
"objc",
"paste",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess2"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca"
dependencies = [
"mime",
"phf",
"phf_shared",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "naga"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
dependencies = [
"bit-set",
"bitflags 2.13.1",
"codespan-reporting",
"hexf-parse",
"indexmap",
"log",
"num-traits",
"rustc-hash 1.1.0",
"spirv",
"termcolor",
"thiserror 1.0.69",
"unicode-xid",
]
[[package]]
name = "ndk"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
dependencies = [
"bitflags 2.13.1",
"jni-sys 0.3.1",
"log",
"ndk-sys",
"num_enum",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"thiserror 1.0.69",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-sys"
version = "0.5.0+25.2.9519653"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
dependencies = [
"jni-sys 0.3.1",
]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.7.1",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [
"proc-macro-crate 3.5.0",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
"objc_exception",
]
[[package]]
name = "objc-sys"
version = "0.2.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
[[package]]
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.3.0-beta.3.patch-leaks.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468"
dependencies = [
"block2 0.2.0-alpha.6",
"objc-sys 0.2.0-beta.2",
"objc2-encode 2.0.0-pre.2",
]
[[package]]
name = "objc2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
dependencies = [
"objc-sys 0.3.5",
"objc2-encode 3.0.0",
]
[[package]]
name = "objc2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
dependencies = [
"objc2-encode 4.1.0",
]
[[package]]
name = "objc2-app-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-graphics",
"objc2-foundation",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.13.1",
"dispatch2",
"objc2 0.6.4",
]
[[package]]
name = "objc2-core-graphics"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
dependencies = [
"bitflags 2.13.1",
"dispatch2",
"objc2 0.6.4",
"objc2-core-foundation",
"objc2-io-surface",
]
[[package]]
name = "objc2-encode"
version = "2.0.0-pre.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"
dependencies = [
"objc-sys 0.2.0-beta.2",
]
[[package]]
name = "objc2-encode"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
]
[[package]]
name = "objc2-io-surface"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
dependencies = [
"cc",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "orbclient"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
dependencies = [
"libc",
"libredox",
]
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "owned_ttf_parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.18",
"smallvec",
"windows-link",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.119",
"unicase",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher 1.0.3",
"unicase",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "piper"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
dependencies = [
"atomic-waker",
"fastrand 2.5.0",
"futures-io",
]
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys 0.48.0",
]
[[package]]
name = "polling"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.5.2",
"pin-project-lite",
"rustix 1.1.4",
"windows-sys 0.61.2",
]
[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "presser"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [
"toml_edit 0.25.13+spec-1.1.0",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]]
name = "quick-xml"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "raw-window-handle"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "redox_syscall"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07507be7b4a5f9f26eeb41eeaebb1f5a7ff29dfb29739facc21d35bf8b11c21e"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "renderdoc-sys"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "resvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
dependencies = [
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roxmltree"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]]
name = "rustix"
version = "0.37.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
"windows-sys 0.48.0",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.13.1",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.13.1",
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sctk-adwaita"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7"
dependencies = [
"ab_glyph",
"log",
"memmap2",
"smithay-client-toolkit 0.18.1",
"tiny-skia",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "serde_repr"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "sha1"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "simplecss"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "siphasher"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "slotmap"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
dependencies = [
"version_check",
]
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "smithay-client-toolkit"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
dependencies = [
"bitflags 2.13.1",
"calloop 0.12.4",
"calloop-wayland-source 0.2.0",
"cursor-icon",
"libc",
"log",
"memmap2",
"rustix 0.38.44",
"thiserror 1.0.69",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols 0.31.2",
"wayland-protocols-wlr 0.2.0",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-client-toolkit"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
dependencies = [
"bitflags 2.13.1",
"calloop 0.14.4",
"calloop-wayland-source 0.4.1",
"cursor-icon",
"libc",
"log",
"memmap2",
"rustix 1.1.4",
"thiserror 2.0.19",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols 0.32.13",
"wayland-protocols-experimental",
"wayland-protocols-misc",
"wayland-protocols-wlr 0.3.12",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-clipboard"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
dependencies = [
"libc",
"smithay-client-toolkit 0.20.0",
"wayland-backend",
]
[[package]]
name = "smol_str"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
dependencies = [
"serde",
]
[[package]]
name = "socket2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
dependencies = [
"kurbo",
"siphasher 0.3.11",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand 2.5.0",
"getrandom 0.4.3",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.61.2",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
dependencies = [
"thiserror-impl 2.0.19",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "thiserror-impl"
version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "tiny-skia"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"png",
"tiny-skia-path",
]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.25.13+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
dependencies = [
"indexmap",
"toml_datetime 1.1.1+spec-1.1.0",
"toml_parser",
"winnow 1.0.4",
]
[[package]]
name = "toml_parser"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [
"winnow 1.0.4",
]
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.3",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset 0.9.1",
"tempfile",
"windows-sys 0.61.2",
]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"url",
"webpki-roots 0.26.11",
]
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "usvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
dependencies = [
"base64 0.21.7",
"log",
"pico-args",
"usvg-parser",
"usvg-tree",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"roxmltree",
"simplecss",
"siphasher 0.3.11",
"svgtypes",
"usvg-tree",
]
[[package]]
name = "usvg-tree"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "waker-fn"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.119",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wayland-backend"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8"
dependencies = [
"cc",
"downcast-rs",
"rustix 1.1.4",
"scoped-tls",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
dependencies = [
"bitflags 2.13.1",
"rustix 1.1.4",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-csd-frame"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [
"bitflags 2.13.1",
"cursor-icon",
"wayland-backend",
]
[[package]]
name = "wayland-cursor"
version = "0.31.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d"
dependencies = [
"rustix 1.1.4",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-experimental"
version = "20250721.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-misc"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-plasma"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [
"dlib",
"log",
"once_cell",
"pkg-config",
]
[[package]]
name = "web-sys"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b"
dependencies = [
"core-foundation",
"home",
"jni",
"log",
"ndk-context",
"objc",
"raw-window-handle 0.5.2",
"url",
"web-sys",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.9",
]
[[package]]
name = "webpki-roots"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "wgpu"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
dependencies = [
"arrayvec",
"cfg-if",
"cfg_aliases",
"js-sys",
"log",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"smallvec",
"static_assertions",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu-core",
"wgpu-hal",
"wgpu-types",
]
[[package]]
name = "wgpu-core"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
dependencies = [
"arrayvec",
"bit-vec",
"bitflags 2.13.1",
"cfg_aliases",
"codespan-reporting",
"indexmap",
"log",
"naga",
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"rustc-hash 1.1.0",
"smallvec",
"thiserror 1.0.69",
"web-sys",
"wgpu-hal",
"wgpu-types",
]
[[package]]
name = "wgpu-hal"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703"
dependencies = [
"android_system_properties",
"arrayvec",
"ash",
"bitflags 2.13.1",
"cfg_aliases",
"core-graphics-types",
"glow",
"glutin_wgl_sys",
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",
"hassle-rs",
"js-sys",
"khronos-egl",
"libc",
"libloading 0.8.9",
"log",
"metal",
"naga",
"ndk-sys",
"objc",
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"renderdoc-sys",
"rustc-hash 1.1.0",
"smallvec",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types",
"winapi",
]
[[package]]
name = "wgpu-types"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
dependencies = [
"bitflags 2.13.1",
"js-sys",
"web-sys",
]
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-implement",
"windows-interface",
"windows-targets 0.48.5",
]
[[package]]
name = "windows"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "windows-interface"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winit"
version = "0.29.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
dependencies = [
"ahash",
"android-activity",
"atomic-waker",
"bitflags 2.13.1",
"bytemuck",
"calloop 0.12.4",
"cfg_aliases",
"core-foundation",
"core-graphics",
"cursor-icon",
"icrate",
"js-sys",
"libc",
"log",
"memmap2",
"ndk",
"ndk-sys",
"objc2 0.4.1",
"once_cell",
"orbclient",
"percent-encoding",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"redox_syscall 0.3.5",
"rustix 0.38.44",
"sctk-adwaita",
"smithay-client-toolkit 0.18.1",
"smol_str",
"unicode-segmentation",
"wasm-bindgen",
"wasm-bindgen-futures",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-protocols-plasma",
"web-sys",
"web-time",
"windows-sys 0.48.0",
"x11-dl",
"x11rb",
"xkbcommon-dl",
]
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "writeable"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "x11-dl"
version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
dependencies = [
"libc",
"once_cell",
"pkg-config",
]
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading 0.8.9",
"once_cell",
"rustix 1.1.4",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xcursor"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
[[package]]
name = "xdg-home"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "xkbcommon-dl"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
dependencies = [
"bitflags 2.13.1",
"dlib",
"log",
"once_cell",
"xkeysym",
]
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xml-rs"
version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "yoke"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"synstructure",
]
[[package]]
name = "zbus"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
dependencies = [
"async-broadcast",
"async-executor",
"async-fs",
"async-io 1.13.0",
"async-lock 2.8.0",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"byteorder",
"derivative",
"enumflags2",
"event-listener 2.5.3",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix",
"once_cell",
"ordered-stream",
"rand",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tracing",
"uds_windows",
"winapi",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
dependencies = [
"serde",
"static_assertions",
"zvariant",
]
[[package]]
name = "zerocopy"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerotrie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zvariant"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db"
dependencies = [
"byteorder",
"enumflags2",
"libc",
"serde",
"static_assertions",
"zvariant_derive",
]
[[package]]
name = "zvariant_derive"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
-13
View File
@@ -1,13 +0,0 @@
[package]
name = "Deaicup-Software"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "Deaicup-Software"
path = "src/main.rs"
[dependencies]
egui = "0.27"
eframe = "0.27"
egui_extras = { version = "0.27" , features = ["all_loaders"] }
-104
View File
@@ -1,104 +0,0 @@
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
use eframe::egui;
fn main() -> Result<(), eframe::Error>{
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
};
eframe::run_native(
"Deaicup Software",
options,
Box::new(|cc| {
egui_extras::install_image_loaders(&cc.egui_ctx);
Box::new(DeaicupSoftware::default())
},)
)
}
#[derive(Default)]
struct DeaicupSoftware {
current_page: Page,
}
#[derive(Debug, PartialEq, Clone, Copy, Default)]
enum Page {
#[default]
Home,
Tool,
Game,
LinuxWare,
WindowsWare,
Set,
About
}
impl eframe::App for DeaicupSoftware{
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame){
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui|{
let image_url = "https://deaicup.com/DeaicupSoftware/top.png";
ui.set_min_height(75.0);
let size = ui.available_size();
ui.image(egui::Image::new(image_url).fit_to_exact_size(egui::Vec2::new(size.x, 75.0)));
});
//main
egui::SidePanel::left("navigation_panel")
.default_width(200.0)
.resizable(true)
.show(ctx, |ui| {
ui.heading("Menu");
ui.separator();
ui.vertical(|ui| {
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked(){
self.current_page = Page::Home;
}
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked(){
self.current_page = Page::Tool;
}
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked(){
self.current_page = Page::Game;
}
if ui.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE").clicked(){
self.current_page = Page::LinuxWare;
}
if ui.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE").clicked(){
self.current_page = Page::WindowsWare;
}
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked(){
self.current_page = Page::Set;
}
if ui.selectable_label(self.current_page == Page::About, "ABOUT").clicked(){
self.current_page = Page::About;
}
})
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.separator();
match self.current_page {
Page::Home => {
ui.label("Home")
}
Page::Tool => {
ui.label("Tool")
}
Page::Game => {
ui.label("Game")
}
Page::LinuxWare => {
ui.label("LinuxWare")
}
Page::WindowsWare => {
ui.label("WindowsWare")
}
Page::Set => {
ui.label("Set")
}
Page::About => {
ui.label("About")
}
}
});
}
}
-39
View File
@@ -1,39 +0,0 @@
CC ?= clang
LD ?= ld.lld
OBJCOPY := llvm-objcopy
ROOT := ../..
OUT := $(ROOT)/SYSTEM/boot/utsm.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -Iinclude -I../utsm-ipc -O2
ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude -I../utsm-ipc
LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c sched/*.c dkm/*.c test/*.c vmm/*.c pe/*.c brand/*.c pkg/*.c)
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S sched/*.S)
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
DEPS := $(C_SOURCES:.c=.d)
.PHONY: all clean
# 显式指定默认目标:防止 -include 的 .d 文件(halt.d 等)抢占默认目标
.DEFAULT_GOAL := all
all: $(OUT)
# 头文件依赖追踪:clang -MMD 生成 .d,避免 config.h 等头修改后对象文件不重建
# 注意:-include 必须放在 all: 之后,否则第一个 .d 中的目标会成为 make 默认目标
-include $(DEPS)
$(OUT): $(OBJECTS) linker.ld
@mkdir -p $(dir $@)
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.o: %.S
$(CC) $(ASFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Include *.o,*.d | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
-935
View File
@@ -1,935 +0,0 @@
// 编译到 SYSTEM/boot/utsm.elf
# UTSM 内核模块设计
UTSM 是当前内核架构中的封缄内存模块。
```text
UTSM = UUID-Tuned Sealed Memory
中文名:UUID 调谐封缄内存
```
UTSM 的目标是在单地址空间 Ring0 高性能内核中提供一种轻量内存语义:
```text
所有普通内存可以被直接读取,但直接读取到的是密文。
真正有效的明文读写必须经过 UTSM 读写器。
UUID 作为进程和内存段的调谐因子。
DRR 负责 root key、checkpoint、recovery log 和回滚。
```
---
## 1. 所属总架构
```text
SAS-R0-PCQ
单地址空间 Ring0 任务模型
Per-CPU O(1) 位图调度器
DRR
Dedicated Recovery Root
看门狗 / checkpoint / recovery log / A-B 回滚
UTSM
UUID-Tuned Sealed Memory
UUID 调谐封缄内存
UTRW
UTSM Tagged Reader/Writer
UTSM 带标记读写器
DMP
Descriptor Meta Page
段描述符元页
PCKC
Per-CPU Key Cache
每 CPU 密钥缓存
```
---
## 2. UTSM 核心规则
```text
1. 数据区只存连续密文。
2. UUID 不插入数据区。
3. UUID 使用 128-bit 二进制格式。
4. 每个加密段有独立 DMP。
5. 每个任务有 process_uuid 和 capability table。
6. 读写明文必须经过 UTRW。
7. 直接读取 cipher_base 只得到密文。
8. 直接写入 cipher_base 会破坏段一致性,段可被标记为 POISONED。
9. DRR 保存 root key、checkpoint、recovery log 和恢复状态。
10. 调度器只加载 crypto context,不扫描段,不破坏 O(1) 调度。
```
---
## 3. 内存布局
每个 UTSM 段由 DMP 和密文数据区组成。
```text
+-------------------------------+
| DMP: Descriptor Meta Page |
| 段描述符元页 |
+-------------------------------+
| Cipher Data Page 0 |
+-------------------------------+
| Cipher Data Page 1 |
+-------------------------------+
| Cipher Data Page 2 |
+-------------------------------+
| ... |
+-------------------------------+
```
全局区域:
```text
+--------------------------------------------------+
| Normal Encrypted Segment Region |
| 普通加密段数据区 |
+--------------------------------------------------+
| DMP Pool |
| 段描述符元页池 |
+--------------------------------------------------+
| Segment Table |
| 全局段表 |
+--------------------------------------------------+
| PCKC Area |
| Per-CPU Key Cache 区 |
+--------------------------------------------------+
| DRR Reserved Region |
| root key / checkpoint / recovery log |
+--------------------------------------------------+
```
---
## 4. 基础类型
```c
typedef struct {
uint64_t hi;
uint64_t lo;
} uuid128_t;
```
UUID 不转十进制字符串,原因:
```text
16 字节固定长度
比较快
cache 友好
适合 KDF
适合 SIMD
避免十进制解析和孔洞布局
```
---
## 5. 进程加密上下文
```c
struct utsm_process_context {
uuid128_t process_uuid;
uint64_t crypto_epoch;
uint64_t recovery_generation;
uint32_t capability_table_slot;
uint32_t capability_count;
uint32_t flags;
};
```
字段含义:
```text
process_uuid
创建任务时生成的进程 UUID。
crypto_epoch
当前密钥版本,用于重加密、回滚和 crypto erase。
recovery_generation
当前 DRR checkpoint 版本。
capability_table_slot
该任务 capability table 的位置。
capability_count
该任务拥有的段能力数量。
flags
PWC、DMA、强恢复等策略标志。
```
---
## 6. 加密段描述符
```c
struct utsm_segment_desc {
uuid128_t segment_uuid;
uuid128_t owner_process_uuid;
void *cipher_base;
uint64_t cipher_length;
void *dmp_base;
uint64_t key_epoch;
uint64_t tweak_seed;
void *dirty_bitmap;
void *mac_table;
uint32_t generation;
uint32_t flags;
uint32_t state;
uint32_t last_cpu;
uint64_t writer_seq;
uint64_t checkpoint_generation;
};
```
段状态:
```text
FREE
ACTIVE
CHECKPOINTING
SEALED
RECOVERING
POISONED
DESTROYED
```
---
## 7. DMP 段描述符元页
DMP 是热路径加速结构,替代“UUID 数字位置表”。
```c
struct utsm_dmp {
uint64_t magic;
uint32_t version;
uint32_t segment_slot;
uint64_t fast_base;
uint64_t fast_limit;
uint64_t fast_tweak_seed;
uint64_t fast_key_epoch;
uint64_t dirty_bitmap_base;
uint64_t mac_table_base;
uint64_t last_writer_seq;
uint32_t last_cpu;
uint32_t flags;
uint64_t crc;
};
```
DMP 用于:
```text
O(1) 段定位
快速边界检查
tweak 生成
Dirty Bitmap 定位
MAC Table 定位
checkpoint
recovery
```
---
## 8. Capability
任务访问段时使用 capability,而不是裸 UUID。
```c
struct utsm_capability {
uint32_t segment_slot;
uint32_t generation;
uint32_t rights;
uint32_t epoch;
uint64_t uuid_digest;
uint64_t auth_tag;
};
```
权限:
```text
READ
WRITE
EXEC
SHARE
DMA
```
热路径通过:
```text
segment_slot + generation + epoch
```
直接定位并验证段。
---
## 9. 加密规则
UTSM 不使用十进制凯撒和 UUID 孔洞,改为按 cache line 的 UUID 调谐流式加密。
```text
segment_key = KDF(
DRR_root_key,
process_uuid,
segment_uuid,
key_epoch
)
```
每 64B cache line 生成 tweak:
```text
line_index = offset / 64
tweak = hash(tweak_seed, line_index, key_epoch)
```
加密和解密:
```text
ciphertext = plaintext XOR stream(segment_key, tweak)
plaintext = ciphertext XOR stream(segment_key, tweak)
```
---
## 10. UTRW 读写器
UTRW 分三层:
```text
Level 0: Raw Access
直接读 cipher_base,得到密文。
Level 1: Fast Path
capability O(1) 定位段,PCKC 命中,无全局锁,无 trap。
Level 2: Slow Path
key miss、epoch mismatch、checkpoint、recovery、MAC fail 等异常处理。
```
### 10.1 读流程
```text
UTRW_READ(cap, offset, len, dst)
-> segment_slot 找 desc
-> generation 检查
-> rights 检查
-> epoch 检查
-> 边界检查
-> PCKC 取 key
-> 按 64B line 解密
-> 返回明文
```
### 10.2 写流程
```text
UTRW_WRITE(cap, offset, len, src)
-> segment_slot 找 desc
-> generation 检查
-> rights 检查
-> epoch 检查
-> 边界检查
-> PCKC 取 key
-> 按 64B line 加密
-> 写入 cipher data
-> 标记 dirty bitmap
-> writer_seq++
-> 按策略写 recovery log
```
部分写必须先解密旧 64B line,合并新明文后重新加密整条 line。
---
## 11. PCKC 每 CPU 密钥缓存
```c
struct utsm_pckc {
uuid128_t current_process_uuid;
uint32_t hot_segment_slot[8];
uint64_t hot_key_epoch[8];
void *key_schedule[8];
uint64_t hit_count;
uint64_t miss_count;
};
```
规则:
```text
每 CPU 一个 PCKC。
调度切换时只加载 next.crypto 指针和 hot_segment_hint。
不扫描 capability table。
不刷新全部 key。
不破坏 PCQ 的 O(1) 调度。
```
---
## 12. 与 SAS-R0-PCQ 的整合
任务结构扩展:
```c
struct task {
uint64_t tid;
uint32_t state;
uint32_t priority;
uint64_t runtime;
uint64_t time_slice;
void *stack_top;
void *entry;
struct context cpu_context;
uint32_t last_cpu;
uint64_t affinity_mask;
struct utsm_process_context *crypto;
struct utsm_capability_table *cap_table;
uint32_t hot_segment_hint;
uint32_t fault_segment_slot;
uint64_t recovery_generation;
uint64_t heartbeat_seq;
uint32_t flags;
struct task *prev;
struct task *next;
};
```
任务状态:
```text
READY
RUNNING
BLOCKED
SLEEPING
RECOVERING
FAULTED
ZOMBIE
```
---
## 13. 任务创建流程
```text
task_create(entry, priority, flags)
-> 分配 TCB
-> 生成 tid
-> 生成 process_uuid
-> 创建 utsm_process_context
-> 创建 stack segment
-> 创建 heap segment
-> 创建 ipc segment 可选
-> 生成 capability table
-> cap[0] = stack
-> cap[1] = heap
-> 初始化 CPU context
-> 通过 UTRW_WRITE 写入 start packet
-> hot_segment_hint = stack segment
-> state = READY
-> enqueue_task(current_cpu)
```
默认 capability:
```text
cap[0] = stack segment, READ | WRITE
cap[1] = heap segment, READ | WRITE
cap[2] = ipc segment, READ | WRITE | SHARE,可选
cap[3] = code segment, READ | EXEC,可选
```
---
## 14. 调度切换流程
```text
schedule()
-> old = current
-> utsm_switch_out(old)
-> next = pick_next_task()
-> utsm_switch_in(next)
-> context_switch(old, next)
```
`utsm_switch_out` 只做:
```text
标记 PWC dirty flush
保存 hot_segment_hint
更新 recovery_generation
更新 heartbeat
```
`utsm_switch_in` 只做:
```text
设置 PCKC.current_process_uuid
加载 next.crypto
检查 recovery_generation
预热 hot_segment_hint
```
禁止在调度路径中:
```text
扫描所有 segment
扫描 capability table
计算 MAC
写 checkpoint
重加密整段
```
---
## 15. Checkpoint / Rollback 协议
### 15.1 Checkpoint 类型
```text
Light Checkpoint
保存 segment table、DMP、dirty shard summary、writer_seq、key_epoch、MAC root。
Dirty Page Checkpoint
保存 dirty page 的密文副本、dirty page MAC、dirty shard、segment descriptor。
Boot Checkpoint
保存内核镜像 slot、UTSM root epoch、DRR boot status、启动阶段状态。
```
### 15.2 Dirty Shard 分片
Dirty Bitmap 不再只是一整块大 bitmap,而是拆成多个 Dirty Shard。
```c
struct utsm_dirty_shard {
uint32_t dirty_count;
uint32_t shard_id;
uint64_t page_base;
uint64_t page_count;
uint64_t last_checkpoint_epoch;
void *bitmap;
};
```
规则:
```text
1. 每个 shard 管理一段连续页范围。
2. UTRW_WRITE 标记 dirty page 时,同时更新 dirty_count。
3. checkpoint 时 dirty_count == 0 的 shard 直接跳过。
4. checkpoint 只扫描 dirty_count > 0 的 shard。
5. 每 CPU 可以维护 dirty shard list,避免全局扫描。
```
复杂度从:
```text
扫描全 dirty bitmap
```
优化为:
```text
O(number_of_dirty_shards + number_of_dirty_pages)
```
### 15.3 A/B Checkpoint Metadata 双槽
每个 checkpoint metadata 使用双槽。
```text
Checkpoint Metadata Slot A
Checkpoint Metadata Slot B
```
提交规则:
```text
当前 active = A
新 checkpoint 写 B
B 写完 metadata、dirty index、MAC root、CRC
flush
最后原子切换 active = B
旧 A 保留为 fallback
```
恢复规则:
```text
优先读取 active slot
如果 active slot CRC 错误或 generation 不完整
fallback 到另一个 slot
如果两个 slot 都损坏
进入 system rollback
```
这样可以避免 checkpoint 写一半崩溃导致旧稳定点也损坏。
### 15.4 正常 checkpoint 流程
```text
DRR 发起 checkpoint
-> checkpoint_generation++
-> 选择 inactive metadata slot
-> UTSM 遍历 dirty shard list
-> 跳过 dirty_count == 0 的 shard
-> 对 dirty shard 内的 ACTIVE 段执行 checkpoint
-> desc.state = CHECKPOINTING
-> 刷出 PWC / per-CPU dirty buffer
-> 复制 dirty ciphertext page 到 checkpoint region
-> 计算 page MAC
-> 写 segment desc snapshot
-> 写 DMP snapshot
-> 写 dirty shard summary
-> 写 checkpoint metadata CRC
-> flush checkpoint metadata
-> 原子切换 active metadata slot
-> CHECKPOINT_COMMIT
-> 清 dirty bitmap / dirty_count
-> desc.state = ACTIVE
```
### 15.5 rollback 级别
```text
Page Rollback
单页损坏,从 checkpoint slot 恢复密文页。
Segment Rollback
整段损坏,恢复 segment desc、DMP、key_epoch、dirty shard、MAC table。
System Rollback
控制面严重损坏,写 crash reason,reboot,由 bootloader 切换 last_good_slot。
```
### 15.6 DRR Emergency Pool
DRR 启动时必须预留独立紧急内存池。
```text
DRR Emergency Pool
emergency pages
recovery log buffer
checkpoint metadata buffer
DRR private stack
DRR metadata slab
crash buffer
```
规则:
```text
1. 普通 allocator 永远不能分配 Emergency Pool。
2. Emergency Pool 只供 watchdog、checkpoint、rollback、crash dump 使用。
3. 普通系统 OOM 不等于 DRR OOM。
4. recovery 路径禁止依赖普通堆分配器。
5. Emergency Pool 耗尽时直接进入 system rollback。
```
---
## 16. 启动流程
### 16.1 Bootloader 阶段
```text
1. 读取 Boot Control Block。
2. 校验 BCB CRC。
3. 选择 active_slot。
4. 如果 active_slot 失败次数超限,切换 last_good_slot。
5. 将 SYSTEM 作为打包后 IMG 内的系统根目录。
6. 加载 SYSTEM/boot/utsm.elf。
7. 加载或暴露 SYSTEM/driver/manifest.json。
8. 传入 memory map、BCB 地址、DRR reserved region 信息。
```
### 16.2 DRR 初始化
```text
1. 保留 DRR region。
2. 初始化 watchdog。
3. 建立 root key 或 root key handle。
4. 初始化 recovery log。
5. 初始化 checkpoint region。
6. 初始化 heartbeat page。
7. 初始化 DRR Emergency Pool。
8. 初始化 A/B checkpoint metadata slot。
```
### 16.3 UTSM 初始化
```text
1. 接收 DRR root key handle。
2. 初始化 global crypto epoch。
3. 初始化 segment table。
4. 初始化 DMP pool。
5. 初始化 PCKC area。
6. 初始化 dirty shard pool。
7. 初始化 dirty bitmap pool。
8. 初始化 MAC table pool。
9. 注册 UTRW fast path / slow path。
```
### 16.4 Scheduler 初始化
```text
1. 初始化 per-CPU runqueue。
2. 初始化 per-CPU heartbeat。
3. 初始化每 CPU PCKC。
4. 创建 bootstrap task。
5. 创建核心服务 task。
6. 将 task 加入 PCQ runqueue。
```
### 16.5 Driver Manifest 加载
驱动清单位于系统根目录下:
```text
SYSTEM/driver/manifest.json
```
规则:
```text
1. manifest.json 是启动驱动模块的唯一默认清单。
2. 驱动文件使用 ELF64 .drv 格式。
3. 驱动按 stage 分阶段加载。
4. required=true 的驱动失败时进入 DRR recovery 或 panic。
5. required=false 的驱动失败时标记 FAILED 并继续启动。
6. stage0/stage1 可由 bootloader 预加载为 boot module。
7. stage2/stage3 在文件系统可用后从 SYSTEM/driver 扫描加载。
```
默认阶段:
```text
stage0: platform,timer / apic / acpi / pci
stage1: boot,console / block / bootfs
stage2: filesystem,vfs / fat32 / devfs
stage3: optional,net / input / gpu
```
### 16.6 DKM 驱动 ABI 摘要
驱动模块系统:
```text
DKM = Deshab Kernel Module
DSM = Driver Startup Manager
```
每个 ELF .drv 必须导出:
```text
driver_desc
driver_init
driver_exit
```
`driver_desc` 至少描述:
```text
magic / abi_version / desc_size
name / version / vendor
driver_class / stage / flags / priority
depends / provides
min_kernel_abi / feature_bits
```
驱动入口:
```c
int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
int driver_exit(struct dkm_driver_handle *handle);
```
驱动默认通过 `dkm_kernel_api` 访问内核服务:
```text
log
mem
utsm
irq
pci
dma
vfs
net
timer
drr
```
ELF loader 第一版支持:
```text
必须支持:
R_X86_64_64
R_X86_64_RELATIVE
R_X86_64_GLOB_DAT
R_X86_64_JUMP_SLOT
建议支持:
R_X86_64_PC32
R_X86_64_PLT32
R_X86_64_32 / R_X86_64_32S
第一版禁止:
TLS relocation
IFUNC
lazy binding
外部动态库依赖
用户态 libc 依赖
```
驱动加载状态机:
```text
DISCOVERED
-> QUEUED
-> DEP_WAIT
-> LOADING
-> ELF_CHECKED
-> MEMORY_ALLOCATED
-> RELOCATED
-> ABI_CHECKED
-> INITING
-> ACTIVE
```
失败状态:
```text
FAILED_ELF
FAILED_RELOCATION
FAILED_DEPENDENCY
FAILED_ABI
FAILED_INIT
FAILED_REQUIRED
```
stage0/stage1 boot module 查找规则:
```text
1. 如果 path 在 boot module table 中,直接从内存加载。
2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。
3. 否则返回 BOOT_MODULE_NOT_FOUND。
```
完整设计见:
```text
RE/驱动模块ABI设计.md
```
---
## 17. 错误处理
UTRW 错误状态:
```text
OK
KEY_MISS_RETRY
STALE_CAPABILITY
EPOCH_EXPIRED
OUT_OF_BOUNDS
ACCESS_DENIED
CHECKPOINT_BUSY
RECOVERING
MAC_FAILED
SEGMENT_POISONED
CORRUPTED_DESCRIPTOR
```
严重错误处理:
```text
1. 记录 task.fault_segment_slot。
2. task.state = FAULTED。
3. 从 runqueue 移除。
4. 通知 DRR。
5. DRR 决定刷新 capability、回滚 segment、kill task 或 system rollback。
```
---
## 18. UTSM v1 定稿
```text
UTSM v1 包含:
二进制 UUID
连续密文数据区
独立 DMP
全局 Segment Table
Segment Capability
Per-CPU Key Cache
UTRW 统一读写器
Dirty Shard + Dirty Bitmap
MAC Table
DRR Emergency Pool
A/B Checkpoint Metadata
DRR checkpoint / rollback 对接
SAS-R0-PCQ task 整合
加密粒度:
64B cache line
Dirty 粒度:
4KB page
Dirty 分片:
Dirty Shard,checkpoint 只扫描 dirty_count > 0 的 shard
MAC 粒度:
4KB page
Checkpoint 元数据:
A/B 双槽,CRC 校验,原子切换 active slot
大页优化:
2MB hugepage 可选
```
-21
View File
@@ -1,21 +0,0 @@
.section .text
.global _start
.type _start, @function
_start:
cli
cld
lea kernel_stack_top(%rip), %rsp
andq $-16, %rsp
call kernel_main
.hang:
cli
hlt
jmp .hang
.section .bss
.align 16
.global kernel_stack
.global kernel_stack_top
kernel_stack:
.skip 16384
kernel_stack_top:
-5
View File
@@ -1,5 +0,0 @@
void arch_halt_forever(void) {
for (;;) {
__asm__ volatile ("cli; hlt");
}
}
-759
View File
@@ -1,759 +0,0 @@
#include <utsm/idt.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/instr.h>
#include <utsm/panic.h>
/* IDT gate descriptor (64-bit) */
typedef struct __attribute__((packed)) {
u16 offset_low;
u16 selector;
u8 ist;
u8 type_attr;
u16 offset_mid;
u32 offset_high;
u32 reserved;
} idt_entry;
/* IDT register */
typedef struct __attribute__((packed)) {
u16 limit;
u64 base;
} idt_ptr;
#define IDT_ENTRIES 256
static idt_entry g_idt[IDT_ENTRIES] __attribute__((aligned(16)));
#define PIC1_CMD 0x20
#define PIC1_DATA 0x21
#define PIC2_CMD 0xA0
#define PIC2_DATA 0xA1
#define ICW1_ICW4 0x01
#define ICW1_INIT 0x10
/* assembly stub declarations — vectors 0-255 全覆盖 */
extern void isr_stub_0(void);
extern void isr_stub_1(void);
extern void isr_stub_2(void);
extern void isr_stub_3(void);
extern void isr_stub_4(void);
extern void isr_stub_5(void);
extern void isr_stub_6(void);
extern void isr_stub_7(void);
extern void isr_stub_8(void);
extern void isr_stub_9(void);
extern void isr_stub_10(void);
extern void isr_stub_11(void);
extern void isr_stub_12(void);
extern void isr_stub_13(void);
extern void isr_stub_14(void);
extern void isr_stub_15(void);
extern void isr_stub_16(void);
extern void isr_stub_17(void);
extern void isr_stub_18(void);
extern void isr_stub_19(void);
extern void isr_stub_20(void);
extern void isr_stub_21(void);
extern void isr_stub_22(void);
extern void isr_stub_23(void);
extern void isr_stub_24(void);
extern void isr_stub_25(void);
extern void isr_stub_26(void);
extern void isr_stub_27(void);
extern void isr_stub_28(void);
extern void isr_stub_29(void);
extern void isr_stub_30(void);
extern void isr_stub_31(void);
extern void isr_stub_32(void);
extern void isr_stub_33(void);
extern void isr_stub_34(void);
extern void isr_stub_35(void);
extern void isr_stub_36(void);
extern void isr_stub_37(void);
extern void isr_stub_38(void);
extern void isr_stub_39(void);
extern void isr_stub_40(void);
extern void isr_stub_41(void);
extern void isr_stub_42(void);
extern void isr_stub_43(void);
extern void isr_stub_44(void);
extern void isr_stub_45(void);
extern void isr_stub_46(void);
extern void isr_stub_47(void);
extern void isr_stub_48(void);
extern void isr_stub_49(void);
extern void isr_stub_50(void);
extern void isr_stub_51(void);
extern void isr_stub_52(void);
extern void isr_stub_53(void);
extern void isr_stub_54(void);
extern void isr_stub_55(void);
extern void isr_stub_56(void);
extern void isr_stub_57(void);
extern void isr_stub_58(void);
extern void isr_stub_59(void);
extern void isr_stub_60(void);
extern void isr_stub_61(void);
extern void isr_stub_62(void);
extern void isr_stub_63(void);
extern void isr_stub_64(void);
extern void isr_stub_65(void);
extern void isr_stub_66(void);
extern void isr_stub_67(void);
extern void isr_stub_68(void);
extern void isr_stub_69(void);
extern void isr_stub_70(void);
extern void isr_stub_71(void);
extern void isr_stub_72(void);
extern void isr_stub_73(void);
extern void isr_stub_74(void);
extern void isr_stub_75(void);
extern void isr_stub_76(void);
extern void isr_stub_77(void);
extern void isr_stub_78(void);
extern void isr_stub_79(void);
extern void isr_stub_80(void);
extern void isr_stub_81(void);
extern void isr_stub_82(void);
extern void isr_stub_83(void);
extern void isr_stub_84(void);
extern void isr_stub_85(void);
extern void isr_stub_86(void);
extern void isr_stub_87(void);
extern void isr_stub_88(void);
extern void isr_stub_89(void);
extern void isr_stub_90(void);
extern void isr_stub_91(void);
extern void isr_stub_92(void);
extern void isr_stub_93(void);
extern void isr_stub_94(void);
extern void isr_stub_95(void);
extern void isr_stub_96(void);
extern void isr_stub_97(void);
extern void isr_stub_98(void);
extern void isr_stub_99(void);
extern void isr_stub_100(void);
extern void isr_stub_101(void);
extern void isr_stub_102(void);
extern void isr_stub_103(void);
extern void isr_stub_104(void);
extern void isr_stub_105(void);
extern void isr_stub_106(void);
extern void isr_stub_107(void);
extern void isr_stub_108(void);
extern void isr_stub_109(void);
extern void isr_stub_110(void);
extern void isr_stub_111(void);
extern void isr_stub_112(void);
extern void isr_stub_113(void);
extern void isr_stub_114(void);
extern void isr_stub_115(void);
extern void isr_stub_116(void);
extern void isr_stub_117(void);
extern void isr_stub_118(void);
extern void isr_stub_119(void);
extern void isr_stub_120(void);
extern void isr_stub_121(void);
extern void isr_stub_122(void);
extern void isr_stub_123(void);
extern void isr_stub_124(void);
extern void isr_stub_125(void);
extern void isr_stub_126(void);
extern void isr_stub_127(void);
extern void isr_stub_128(void);
extern void isr_stub_129(void);
extern void isr_stub_130(void);
extern void isr_stub_131(void);
extern void isr_stub_132(void);
extern void isr_stub_133(void);
extern void isr_stub_134(void);
extern void isr_stub_135(void);
extern void isr_stub_136(void);
extern void isr_stub_137(void);
extern void isr_stub_138(void);
extern void isr_stub_139(void);
extern void isr_stub_140(void);
extern void isr_stub_141(void);
extern void isr_stub_142(void);
extern void isr_stub_143(void);
extern void isr_stub_144(void);
extern void isr_stub_145(void);
extern void isr_stub_146(void);
extern void isr_stub_147(void);
extern void isr_stub_148(void);
extern void isr_stub_149(void);
extern void isr_stub_150(void);
extern void isr_stub_151(void);
extern void isr_stub_152(void);
extern void isr_stub_153(void);
extern void isr_stub_154(void);
extern void isr_stub_155(void);
extern void isr_stub_156(void);
extern void isr_stub_157(void);
extern void isr_stub_158(void);
extern void isr_stub_159(void);
extern void isr_stub_160(void);
extern void isr_stub_161(void);
extern void isr_stub_162(void);
extern void isr_stub_163(void);
extern void isr_stub_164(void);
extern void isr_stub_165(void);
extern void isr_stub_166(void);
extern void isr_stub_167(void);
extern void isr_stub_168(void);
extern void isr_stub_169(void);
extern void isr_stub_170(void);
extern void isr_stub_171(void);
extern void isr_stub_172(void);
extern void isr_stub_173(void);
extern void isr_stub_174(void);
extern void isr_stub_175(void);
extern void isr_stub_176(void);
extern void isr_stub_177(void);
extern void isr_stub_178(void);
extern void isr_stub_179(void);
extern void isr_stub_180(void);
extern void isr_stub_181(void);
extern void isr_stub_182(void);
extern void isr_stub_183(void);
extern void isr_stub_184(void);
extern void isr_stub_185(void);
extern void isr_stub_186(void);
extern void isr_stub_187(void);
extern void isr_stub_188(void);
extern void isr_stub_189(void);
extern void isr_stub_190(void);
extern void isr_stub_191(void);
extern void isr_stub_192(void);
extern void isr_stub_193(void);
extern void isr_stub_194(void);
extern void isr_stub_195(void);
extern void isr_stub_196(void);
extern void isr_stub_197(void);
extern void isr_stub_198(void);
extern void isr_stub_199(void);
extern void isr_stub_200(void);
extern void isr_stub_201(void);
extern void isr_stub_202(void);
extern void isr_stub_203(void);
extern void isr_stub_204(void);
extern void isr_stub_205(void);
extern void isr_stub_206(void);
extern void isr_stub_207(void);
extern void isr_stub_208(void);
extern void isr_stub_209(void);
extern void isr_stub_210(void);
extern void isr_stub_211(void);
extern void isr_stub_212(void);
extern void isr_stub_213(void);
extern void isr_stub_214(void);
extern void isr_stub_215(void);
extern void isr_stub_216(void);
extern void isr_stub_217(void);
extern void isr_stub_218(void);
extern void isr_stub_219(void);
extern void isr_stub_220(void);
extern void isr_stub_221(void);
extern void isr_stub_222(void);
extern void isr_stub_223(void);
extern void isr_stub_224(void);
extern void isr_stub_225(void);
extern void isr_stub_226(void);
extern void isr_stub_227(void);
extern void isr_stub_228(void);
extern void isr_stub_229(void);
extern void isr_stub_230(void);
extern void isr_stub_231(void);
extern void isr_stub_232(void);
extern void isr_stub_233(void);
extern void isr_stub_234(void);
extern void isr_stub_235(void);
extern void isr_stub_236(void);
extern void isr_stub_237(void);
extern void isr_stub_238(void);
extern void isr_stub_239(void);
extern void isr_stub_240(void);
extern void isr_stub_241(void);
extern void isr_stub_242(void);
extern void isr_stub_243(void);
extern void isr_stub_244(void);
extern void isr_stub_245(void);
extern void isr_stub_246(void);
extern void isr_stub_247(void);
extern void isr_stub_248(void);
extern void isr_stub_249(void);
extern void isr_stub_250(void);
extern void isr_stub_251(void);
extern void isr_stub_252(void);
extern void isr_stub_253(void);
extern void isr_stub_254(void);
extern void isr_stub_255(void);
static void *g_isr_table[256] = {
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
isr_stub_48, isr_stub_49, isr_stub_50, isr_stub_51,
isr_stub_52, isr_stub_53, isr_stub_54, isr_stub_55,
isr_stub_56, isr_stub_57, isr_stub_58, isr_stub_59,
isr_stub_60, isr_stub_61, isr_stub_62, isr_stub_63,
isr_stub_64, isr_stub_65, isr_stub_66, isr_stub_67,
isr_stub_68, isr_stub_69, isr_stub_70, isr_stub_71,
isr_stub_72, isr_stub_73, isr_stub_74, isr_stub_75,
isr_stub_76, isr_stub_77, isr_stub_78, isr_stub_79,
isr_stub_80, isr_stub_81, isr_stub_82, isr_stub_83,
isr_stub_84, isr_stub_85, isr_stub_86, isr_stub_87,
isr_stub_88, isr_stub_89, isr_stub_90, isr_stub_91,
isr_stub_92, isr_stub_93, isr_stub_94, isr_stub_95,
isr_stub_96, isr_stub_97, isr_stub_98, isr_stub_99,
isr_stub_100, isr_stub_101, isr_stub_102, isr_stub_103,
isr_stub_104, isr_stub_105, isr_stub_106, isr_stub_107,
isr_stub_108, isr_stub_109, isr_stub_110, isr_stub_111,
isr_stub_112, isr_stub_113, isr_stub_114, isr_stub_115,
isr_stub_116, isr_stub_117, isr_stub_118, isr_stub_119,
isr_stub_120, isr_stub_121, isr_stub_122, isr_stub_123,
isr_stub_124, isr_stub_125, isr_stub_126, isr_stub_127,
isr_stub_128, isr_stub_129, isr_stub_130, isr_stub_131,
isr_stub_132, isr_stub_133, isr_stub_134, isr_stub_135,
isr_stub_136, isr_stub_137, isr_stub_138, isr_stub_139,
isr_stub_140, isr_stub_141, isr_stub_142, isr_stub_143,
isr_stub_144, isr_stub_145, isr_stub_146, isr_stub_147,
isr_stub_148, isr_stub_149, isr_stub_150, isr_stub_151,
isr_stub_152, isr_stub_153, isr_stub_154, isr_stub_155,
isr_stub_156, isr_stub_157, isr_stub_158, isr_stub_159,
isr_stub_160, isr_stub_161, isr_stub_162, isr_stub_163,
isr_stub_164, isr_stub_165, isr_stub_166, isr_stub_167,
isr_stub_168, isr_stub_169, isr_stub_170, isr_stub_171,
isr_stub_172, isr_stub_173, isr_stub_174, isr_stub_175,
isr_stub_176, isr_stub_177, isr_stub_178, isr_stub_179,
isr_stub_180, isr_stub_181, isr_stub_182, isr_stub_183,
isr_stub_184, isr_stub_185, isr_stub_186, isr_stub_187,
isr_stub_188, isr_stub_189, isr_stub_190, isr_stub_191,
isr_stub_192, isr_stub_193, isr_stub_194, isr_stub_195,
isr_stub_196, isr_stub_197, isr_stub_198, isr_stub_199,
isr_stub_200, isr_stub_201, isr_stub_202, isr_stub_203,
isr_stub_204, isr_stub_205, isr_stub_206, isr_stub_207,
isr_stub_208, isr_stub_209, isr_stub_210, isr_stub_211,
isr_stub_212, isr_stub_213, isr_stub_214, isr_stub_215,
isr_stub_216, isr_stub_217, isr_stub_218, isr_stub_219,
isr_stub_220, isr_stub_221, isr_stub_222, isr_stub_223,
isr_stub_224, isr_stub_225, isr_stub_226, isr_stub_227,
isr_stub_228, isr_stub_229, isr_stub_230, isr_stub_231,
isr_stub_232, isr_stub_233, isr_stub_234, isr_stub_235,
isr_stub_236, isr_stub_237, isr_stub_238, isr_stub_239,
isr_stub_240, isr_stub_241, isr_stub_242, isr_stub_243,
isr_stub_244, isr_stub_245, isr_stub_246, isr_stub_247,
isr_stub_248, isr_stub_249, isr_stub_250, isr_stub_251,
isr_stub_252, isr_stub_253, isr_stub_254, isr_stub_255,
};
static void outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
}
static u8 inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static void io_wait(void) {
outb(0x80, 0);
}
static void pic_remap(void) {
/* save masks (kept for reference; fixed masks are applied below) */
u8 m1 = inb(PIC1_DATA);
u8 m2 = inb(PIC2_DATA);
(void)m1;
(void)m2;
/* ICW1: start init + ICW4 */
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
io_wait();
/* ICW2: vector offsets */
outb(PIC1_DATA, 0x20); /* master: vectors 0x20-0x27 */
io_wait();
outb(PIC2_DATA, 0x28); /* slave: vectors 0x28-0x2F */
io_wait();
/* ICW3: master/slave wiring */
outb(PIC1_DATA, 0x04); /* master: IRQ2 → slave */
io_wait();
outb(PIC2_DATA, 0x02); /* slave: cascade identity */
io_wait();
/* ICW4: 8086 mode */
outb(PIC1_DATA, 0x01);
io_wait();
outb(PIC2_DATA, 0x01);
io_wait();
/* restore masks: mask all except IRQ1 (keyboard) */
outb(PIC1_DATA, 0xFD); /* 1111_1101: enable IRQ0 (timer) and IRQ1 (kb) */
outb(PIC2_DATA, 0xFF); /* mask all slave */
}
/* P1(万物互联): kern.irq.unmask/mask 总线服务后端 —— PIC IMR 位操作。
* 仅 ISA IRQ 0-15(越界 = 编程错误,IRQ-E01 零降级)。
* unmask IRQ8-15 时自动放开主片 IRQ2 级联。
* 注意 apic_route=1 时 PIC 已全屏蔽、由 IOAPIC 接管(B7);该路径下
* 的解屏蔽随 P6 真 IRQ 输入走 IOAPIC 重定向表接线。 */
void pic_irq_unmask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_unmask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m & (u8)~(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m & (u8)~(1u << (irq - 8)));
u8 m1 = inb(PIC1_DATA);
outb(PIC1_DATA, m1 & (u8)~(1u << 2)); /* 主片 IRQ2 级联 */
}
}
void pic_irq_mask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_mask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m | (u8)(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m | (u8)(1u << (irq - 8)));
}
}
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
* 代码段,异常派发时加载 CS 会 #GP(selector error),异常链升级为
* #DF -> triple fault,且全程无任何日志输出。 */
static u16 idt_kernel_cs(void) {
u16 cs;
__asm__ volatile("mov %%cs, %0" : "=r"(cs));
return cs;
}
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
u64 addr = (u64)handler;
g_idt[vector].offset_low = (u16)(addr & 0xffff);
g_idt[vector].selector = idt_kernel_cs();
g_idt[vector].ist = ist;
g_idt[vector].type_attr = type;
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
g_idt[vector].offset_high = (u32)(addr >> 32);
g_idt[vector].reserved = 0;
}
void idt_init(void) {
log_info("[IDT] init begin");
for (u16 i = 0; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */
}
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
idtr.base = (u64)&g_idt;
__asm__ volatile ("lidt %0" :: "m"(idtr));
pic_remap();
log_info("[IDT] PIC remapped (master=0x20 slave=0x28)");
__asm__ volatile ("sti");
log_info("[IDT] interrupts enabled");
log_info("[IDT] init ok");
}
/* 安全 halt stub: cli; hlt 循环。用于 idt_halt_all()。
* 任何异常/IRQ 触发此 stub 将安全停机,不会三重故障。 */
static void isr_halt_stub(void) {
for (;;) { __asm__ volatile("cli; hlt"); }
}
void idt_halt_all(void) {
/* 将 IDT 所有 256 个条目替换为安全 halt stub。
* 调用前应已 cli + mask PIC,此函数是额外防御层:
* - 实机 spurious IRQ7/15 即使 PIC 已 mask 也可能触发
* - NMI / SMI 可能导致异常
* - DSK 运行期间 CPU 异常 (PF/GP) 应安全停机而非执行过期 handler */
for (u16 i = 0; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
}
/* 重新加载 IDTR 以确保更改生效 */
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
idtr.base = (u64)&g_idt;
__asm__ volatile("lidt %0" :: "m"(idtr));
}
void idt_install_dsk_diag(void) {
/* BUG-20260801-005: DSK 交接诊断 IDT(替代原 idt_halt_all 静默化)。
* 原 halt_all 将 256 项全部换成静默 halt stub——防御正确,但 DSK
* 阶段任何 CPU 异常(PF/GP/UD)被无声吞掉,零日志无法诊断
* (实测 pe HELLO64.EXE 早期 #PF 直接表现为"卡死/三重故障")。
* 本函数在保留 IRQ 防御的前提下恢复异常可见性:
* vector 0-31 -> 原 isr_stub(走 idt_handler 异常路径,打完整
* vector/err/rip/CR2/寄存器日志后安全停机)
* vector 32-255 -> 保持 halt stub(PIC 已全 mask 且 IF=0,IRQ 本
* 不该到达;spurious IRQ 维持原防御行为)
* 不做 pic_remap / sti——DSK 上下文 IF=0 不变式保持不变。
* isr_stub / idt_handler 是 UTSM .text 代码,SAS-R0 下常驻内存
* 有效(BUG-20260729-013 登记行为:halt_all 引入前 DSK 阶段异常
* 本就能送达 idt_handler);异常路径只用 log/serial,不依赖已过期
* 的 IRQ handler 状态。 */
for (u16 i = 0; i < 32; i++) {
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E);
}
for (u16 i = 32; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
}
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
idtr.base = (u64)&g_idt;
__asm__ volatile("lidt %0" :: "m"(idtr));
log_info("[IDT] DSK diag IDT installed (exc 0-31 logged, IRQ halted)");
}
void idt_install_pe_diag(void) {
/* 语义与 idt_install_dsk_diag 相同:BUG-20260801-005 起 DSK 交接
* 默认装备诊断 IDT,PE 原生执行前重装一次保持幂等(防御 PE 运行
* 期间 IDT 被第三方改写的场景)。 */
idt_install_dsk_diag();
}
/* IRQ handler table — 扩展到 256 个 vector (真机 APIC/IOAPIC/MSI 需要) */
static irq_handler_t g_irq_handlers[256];
/* B7 阶段2: LAPIC EOI 钩子 (由 apic.drv 经 kernel_api.register_apic_eoi 注册)。
* 指向写 LAPIC EOI 寄存器 (0xFEE000B0) 的函数; NULL = PIC 路由模式, 不调用。
* 单核 BSP 场景: 注册发生在 apic.drv init (普通上下文), 调用发生在 IRQ
* 上下文; 函数指针赋值对齐原子, 无需锁。 */
static void (*g_apic_eoi_hook)(void) = 0;
void idt_register_apic_eoi(void (*eoi_fn)(void)) {
g_apic_eoi_hook = eoi_fn;
if (eoi_fn) {
log_info("[IDT] APIC EOI hook registered (LAPIC EOI on IRQ dispatch)");
} else {
log_info("[IDT] APIC EOI hook cleared");
}
}
/* ---- 插桩: IRQ 统计计数器 ---- */
INSTR_STAT_DECL(pic_irq_total);
INSTR_STAT_DECL(apic_irq_total);
/* ---- B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用) ----
* 分配范围 0x40-0xDF (160 个向量):
* 0x00-0x1F CPU 异常
* 0x20-0x2F PIC legacy IRQ (保留不动)
* 0x30-0x3F 保留给 IOAPIC GSI 16-31 未来扩展 (本阶段不动态分配)
* 0x40-0xDF 动态分配池 (MSI/MSI-X)
* 0xE0 APIC tick 自测预留 (阶段1 惯例)
* 0xFF LAPIC spurious vector
* 数据结构: 3 x u64 位图 (192 bit, 用后 160), __builtin_ctzll 找首个
* 零位, 分配/释放均 O(1)。热路径无锁: SAS-R0 单核 BSP, alloc/free 只在
* driver_init 主上下文调用 (MSI 编程期间对应设备尚未解除中断屏蔽,
* IRQ 上下文不可能并发分配), 位图字为对齐 u64 单写原子。
* 动态入口无需新增 stub: idt_stubs.S 已为 0-255 全部向量预生成入口,
* 分配后 irq_register(vector, handler) 直接挂上既有分发表。 */
#define IRQ_VEC_POOL_BASE 0x40
#define IRQ_VEC_POOL_COUNT 160
static u64 g_vec_bitmap[3];
int irq_vector_alloc(void) {
for (u32 w = 0; w < 3; w++) {
u64 free_bits = ~g_vec_bitmap[w];
if (!free_bits) continue;
u32 bit = (u32)__builtin_ctzll(free_bits);
u32 idx = w * 64u + bit;
if (idx >= IRQ_VEC_POOL_COUNT) break;
g_vec_bitmap[w] |= (1ULL << bit);
int vec = IRQ_VEC_POOL_BASE + (int)idx;
log_info("[IDT] vector alloc");
log_hex64("[IDT] vector=", (u64)vec);
return vec;
}
log_warn("[IDT] vector alloc failed: pool exhausted");
panic_full("IDT-E01 VECTOR POOL EXHAUSTED",
"irq_vector_alloc: dynamic vector pool exhausted", 0);
return -1;
}
void irq_vector_free(int vector) {
if (vector < IRQ_VEC_POOL_BASE ||
vector >= IRQ_VEC_POOL_BASE + (int)IRQ_VEC_POOL_COUNT) {
return; /* 越界输入幂等忽略 (静态向量不在池内) */
}
u32 idx = (u32)(vector - IRQ_VEC_POOL_BASE);
g_vec_bitmap[idx / 64u] &= ~(1ULL << (idx % 64u));
log_info("[IDT] vector free");
log_hex64("[IDT] vector=", (u64)vector);
}
int irq_register(u8 vector, irq_handler_t handler) {
/* IRQ号/向量兼容 shim: 0-15 视为 ISA IRQ 号, 换算 vector=0x20+IRQ。
* 依据: vector 0-15 是 CPU 异常向量 (#DE/#DB/...), idt_handler 的 IRQ
* 分发只覆盖 32-255, 故 0-15 作为注册目标永不可达、无歧义。
* 现状调用方两命名空间并存: ps2kbd(1)/e1000(11)/mouseInit(12) 传 IRQ 号,
* apic(0xE0)/virtio_net(0x20+n) 传向量 — 统一在此归一到向量空间。 */
if (vector < 16) {
u8 v = (u8)(0x20u + vector);
log_info("[IDT] irq_register: ISA IRQ number translated to vector");
log_hex64("[IDT] irq=", vector);
log_hex64("[IDT] vector=", v);
vector = v;
}
g_irq_handlers[vector] = handler;
INSTR_LOG(INSTR_LOG_DEBUG, INSTR_F_UTSM_TRACE, "[IDT] IRQ handler registered");
return 0;
}
/* Phase7: 显式 EOI(调度器 tick 等"handler 内可能切换任务"的路径用)。
* 序列复刻 idt_handler 默认 EOI: 先 LAPIC 钩子再 8259(按向量范围)。
* spurious 0xFF 特判与 idt_handler 一致(空 ISR 不该有 EOI)。 */
void idt_irq_eoi(u8 vector) {
if (vector < 16) vector = (u8)(0x20u + vector);
if (vector >= 32 && vector <= 47) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
if (vector >= 0x28) outb(PIC2_CMD, 0x20);
outb(PIC1_CMD, 0x20);
} else if (vector >= 48 && vector != 0xFF) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
}
}
/* Phase8: CPU 异常钩子(DRR 任务级 fault 接管)。 */
static int (*g_exc_hook)(u64 vector, u64 err, u64 rip) = 0;
void idt_set_exception_hook(int (*hook)(u64 vector, u64 err, u64 rip)) {
g_exc_hook = hook;
}
/* Stack frame pushed by isr_common in idt.S */
typedef struct {
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
u64 vector;
u64 error_code;
u64 rip, cs, rflags;
} isr_frame;
void idt_handler(isr_frame *frame) {
if (frame->vector < 32) {
/* Phase8: DRR 异常钩子最前——返回非 0 = 异常已被消费
* (fault 任务被杀/切换,控制权不回到日志+停机路径) */
if (g_exc_hook &&
g_exc_hook(frame->vector, frame->error_code, frame->rip)) {
return;
}
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
* 寄存器 dump 用于定位 fault 指令的操作数来源。
* F1: 异常统一走莲花崩溃屏(panic_full 内部保留串口 dump +
* DRR 归档 + 帧缓冲直绘,最后 halt——不返回)。 */
u64 cr2, cr3;
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
(void)cr3;
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
panic_regs pr;
pr.rip = frame->rip;
pr.rsp = 0; /* isr_frame 不含 rsp(stub 内使用),置 0 */
pr.rflags = frame->rflags;
pr.cs = frame->cs;
pr.err = frame->error_code;
pr.vector = frame->vector;
pr.cr2 = cr2;
pr.rax = frame->rax;
pr.rcx = frame->rcx;
pr.rdx = frame->rdx;
pr.rsi = frame->rsi;
pr.rdi = frame->rdi;
pr.r8 = frame->r8;
pr.r9 = frame->r9;
pr.r10 = frame->r10;
pr.r11 = frame->r11;
char sym[16];
sym[0] = 'U'; sym[1] = 'T'; sym[2] = 'S'; sym[3] = 'M'; sym[4] = '-';
sym[5] = 'E'; sym[6] = 'X'; sym[7] = 'C';
u32 v = frame->vector;
u32 hi = (v >> 4) & 0xF, lo = v & 0xF;
sym[8] = (char)(hi < 10 ? '0' + hi : 'A' + hi - 10);
sym[9] = (char)(lo < 10 ? '0' + lo : 'A' + lo - 10);
sym[10] = 0;
/* 异常帧上无法安全恢复执行:两种 panic 模式都 halt */
panic_halt(sym, "cpu exception", &pr);
}
/* PIC IRQ: vectors 0x20-0x2F (32-47)
* B7 阶段2: 本分支同时服务两种路由来源:
* - 8259 PIC 路由 (默认): handler 后只需 8259 EOI, 钩子为 NULL 跳过;
* - IOAPIC->LAPIC 路由 (apic_route=1, legacy 线重定向到 0x20-0x2F):
* 中断由 LAPIC 递交, ISR 位置位, 必须写 LAPIC EOI (钩子) 清除,
* 否则同级 (vector[7:4] 相同) 中断被永久阻塞 — 每条线只投递一次。
* LAPIC EOI 先于 8259 EOI; PIC 全屏蔽时 8259 EOI 为无害 no-op
* (8259 无 in-service 位, 非特异性 EOI 不写任何状态)。 */
if (frame->vector >= 32 && frame->vector <= 47) {
INSTR_STAT_INC(pic_irq_total);
u8 irq = (u8)(frame->vector - 0x20);
/* call registered handler; 返回 1 = 自负 EOI 全责 (两者皆抑制) */
if (g_irq_handlers[frame->vector]) {
int suppress_eoi = g_irq_handlers[frame->vector](irq);
if (suppress_eoi) return;
}
/* LAPIC EOI (IOAPIC 路由时必需; PIC 路由时钩子为 NULL) */
if (g_apic_eoi_hook) {
g_apic_eoi_hook();
}
/* send PIC EOI */
if (frame->vector >= 0x28) {
outb(PIC2_CMD, 0x20);
}
outb(PIC1_CMD, 0x20);
return;
}
/* APIC/IOAPIC/MSI IRQ: vectors 0x30-0xFF (48-255)
* handler 约定: 返回 0 -> 由钩子补 LAPIC EOI (安全网);
* 返回 1 -> handler 已自行 LAPIC EOI, 跳过钩子。
* 钩子未注册 (纯 PIC 路由) 时本分支不做任何 EOI — 与旧版行为一致,
* 此时 handler 必须自行 LAPIC EOI (参见 apic.drv tick handler)。 */
if (frame->vector >= 48 && frame->vector <= 255) {
/* LAPIC spurious (SVR 低 8 位 = 0xFF): 规范上不置 ISR 位, 永不需 EOI。
* 若在此调钩子写 EOI, 会误清嵌套场景下真正 in-service 的最高位,
* 故特判直接返回 (EOI 空 ISR 虽无害, 但语义上 spurious 不该有 EOI)。 */
if (frame->vector == 0xFF) return;
INSTR_STAT_INC(apic_irq_total);
int suppress_eoi = 0;
if (g_irq_handlers[frame->vector]) {
suppress_eoi = g_irq_handlers[frame->vector]((u8)frame->vector);
}
if (!suppress_eoi && g_apic_eoi_hook) {
g_apic_eoi_hook();
}
return;
}
}
-122
View File
@@ -1,122 +0,0 @@
/* IDT assembly stubs — SAS-R0 interrupt vectors
* Vectors 0-31: CPU exceptions (some push error code)
* Vectors 32-47: PIC IRQ remap region
* Vectors 48-255: APIC/IOAPIC/MSI dynamic vectors
*/
.section .text
.macro isr_no_err n
.global isr_stub_\n
.type isr_stub_\n, @function
isr_stub_\n:
pushq $0
pushq $\n
jmp isr_common
.endm
.macro isr_err n
.global isr_stub_\n
.type isr_stub_\n, @function
isr_stub_\n:
pushq $\n
jmp isr_common
.endm
/* Exceptions 0-31 */
isr_no_err 0; isr_no_err 1; isr_no_err 2; isr_no_err 3
isr_no_err 4; isr_no_err 5; isr_no_err 6; isr_no_err 7
isr_err 8; isr_no_err 9; isr_err 10; isr_err 11
isr_err 12; isr_err 13; isr_err 14; isr_no_err 15
isr_no_err 16; isr_err 17; isr_no_err 18; isr_no_err 19
isr_no_err 20; isr_no_err 21; isr_no_err 22; isr_no_err 23
isr_no_err 24; isr_no_err 25; isr_no_err 26; isr_no_err 27
isr_no_err 28; isr_no_err 29; isr_err 30; isr_no_err 31
/* IRQ 32-47 */
isr_no_err 32; isr_no_err 33; isr_no_err 34; isr_no_err 35
isr_no_err 36; isr_no_err 37; isr_no_err 38; isr_no_err 39
isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43
isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47
/* Vectors 48-255: APIC/IOAPIC/MSI 真机动态向量
* 真机需要更多 vector: APIC timer (0x30+), IOAPIC GSI, MSI/MSI-X 等。
* 全部生成 stub 确保任何 vector 命中都有 handler, 不会 triple fault。 */
isr_no_err 48; isr_no_err 49; isr_no_err 50; isr_no_err 51
isr_no_err 52; isr_no_err 53; isr_no_err 54; isr_no_err 55
isr_no_err 56; isr_no_err 57; isr_no_err 58; isr_no_err 59
isr_no_err 60; isr_no_err 61; isr_no_err 62; isr_no_err 63
isr_no_err 64; isr_no_err 65; isr_no_err 66; isr_no_err 67
isr_no_err 68; isr_no_err 69; isr_no_err 70; isr_no_err 71
isr_no_err 72; isr_no_err 73; isr_no_err 74; isr_no_err 75
isr_no_err 76; isr_no_err 77; isr_no_err 78; isr_no_err 79
isr_no_err 80; isr_no_err 81; isr_no_err 82; isr_no_err 83
isr_no_err 84; isr_no_err 85; isr_no_err 86; isr_no_err 87
isr_no_err 88; isr_no_err 89; isr_no_err 90; isr_no_err 91
isr_no_err 92; isr_no_err 93; isr_no_err 94; isr_no_err 95
isr_no_err 96; isr_no_err 97; isr_no_err 98; isr_no_err 99
isr_no_err 100; isr_no_err 101; isr_no_err 102; isr_no_err 103
isr_no_err 104; isr_no_err 105; isr_no_err 106; isr_no_err 107
isr_no_err 108; isr_no_err 109; isr_no_err 110; isr_no_err 111
isr_no_err 112; isr_no_err 113; isr_no_err 114; isr_no_err 115
isr_no_err 116; isr_no_err 117; isr_no_err 118; isr_no_err 119
isr_no_err 120; isr_no_err 121; isr_no_err 122; isr_no_err 123
isr_no_err 124; isr_no_err 125; isr_no_err 126; isr_no_err 127
isr_no_err 128; isr_no_err 129; isr_no_err 130; isr_no_err 131
isr_no_err 132; isr_no_err 133; isr_no_err 134; isr_no_err 135
isr_no_err 136; isr_no_err 137; isr_no_err 138; isr_no_err 139
isr_no_err 140; isr_no_err 141; isr_no_err 142; isr_no_err 143
isr_no_err 144; isr_no_err 145; isr_no_err 146; isr_no_err 147
isr_no_err 148; isr_no_err 149; isr_no_err 150; isr_no_err 151
isr_no_err 152; isr_no_err 153; isr_no_err 154; isr_no_err 155
isr_no_err 156; isr_no_err 157; isr_no_err 158; isr_no_err 159
isr_no_err 160; isr_no_err 161; isr_no_err 162; isr_no_err 163
isr_no_err 164; isr_no_err 165; isr_no_err 166; isr_no_err 167
isr_no_err 168; isr_no_err 169; isr_no_err 170; isr_no_err 171
isr_no_err 172; isr_no_err 173; isr_no_err 174; isr_no_err 175
isr_no_err 176; isr_no_err 177; isr_no_err 178; isr_no_err 179
isr_no_err 180; isr_no_err 181; isr_no_err 182; isr_no_err 183
isr_no_err 184; isr_no_err 185; isr_no_err 186; isr_no_err 187
isr_no_err 188; isr_no_err 189; isr_no_err 190; isr_no_err 191
isr_no_err 192; isr_no_err 193; isr_no_err 194; isr_no_err 195
isr_no_err 196; isr_no_err 197; isr_no_err 198; isr_no_err 199
isr_no_err 200; isr_no_err 201; isr_no_err 202; isr_no_err 203
isr_no_err 204; isr_no_err 205; isr_no_err 206; isr_no_err 207
isr_no_err 208; isr_no_err 209; isr_no_err 210; isr_no_err 211
isr_no_err 212; isr_no_err 213; isr_no_err 214; isr_no_err 215
isr_no_err 216; isr_no_err 217; isr_no_err 218; isr_no_err 219
isr_no_err 220; isr_no_err 221; isr_no_err 222; isr_no_err 223
isr_no_err 224; isr_no_err 225; isr_no_err 226; isr_no_err 227
isr_no_err 228; isr_no_err 229; isr_no_err 230; isr_no_err 231
isr_no_err 232; isr_no_err 233; isr_no_err 234; isr_no_err 235
isr_no_err 236; isr_no_err 237; isr_no_err 238; isr_no_err 239
isr_no_err 240; isr_no_err 241; isr_no_err 242; isr_no_err 243
isr_no_err 244; isr_no_err 245; isr_no_err 246; isr_no_err 247
isr_no_err 248; isr_no_err 249; isr_no_err 250; isr_no_err 251
isr_no_err 252; isr_no_err 253; isr_no_err 254; isr_no_err 255
/* Common ISR */
isr_common:
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %rax
pushq %r8
pushq %r9
pushq %r10
pushq %r11
movq %rsp, %rdi
call idt_handler
popq %r11
popq %r10
popq %r9
popq %r8
popq %rax
popq %rcx
popq %rdx
popq %rsi
popq %rdi
addq $16, %rsp
iretq
-15
View File
@@ -1,15 +0,0 @@
#include <utsm/types.h>
void outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
}
u8 inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
void io_wait(void) {
outb(0x80, 0);
}
-192
View File
@@ -1,192 +0,0 @@
/* lapic_timer.c — 宿主自持 LAPIC 定时器(调度器 tick 源)
*
* 为什么不用 PIT 周期中断:默认 FUCK apic_route=1 时 apic.drv 验证完
* tick 链路后重新 mask GSI2(PIT IRQ0 线),PIT 周期中断不可达;
* LAPIC timer 不经 IOAPIC/PIC 路由,与路由模式解耦,且是 ROADMAP
* Phase 2 "LAPIC timer 替代 PIT" 的目标方案。
*
* 流程:MSR 0x1B → 物理基址 → map_mmio(SAS-R0 直接扩当前 CR3)
* → PIT ch0 单发 10ms 采样 CCR 差值校准
* → irq_vector_alloc 动态向量 + LVT periodic(先 mask,start 再放开)
*/
#include <utsm/lapic_timer.h>
#include <utsm/pit.h>
#include <utsm/paging.h>
#include <utsm/idt.h>
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
extern void outb(u16 port, u8 value);
extern u8 inb(u16 port);
#define MSR_IA32_APIC_BASE 0x1Bu
#define APIC_BASE_ENABLE (1ULL << 11)
#define APIC_BASE_X2APIC (1ULL << 10)
#define LAPIC_DEFAULT_PHYS 0xFEE00000ULL
#define LAPIC_EOI 0x0B0u
#define LAPIC_TPR 0x080u
#define LAPIC_SPURIOUS 0x0F0u
#define LAPIC_LVT_TIMER 0x320u
#define LAPIC_DIV_CONF 0x3E0u
#define LAPIC_INIT_CNT 0x380u
#define LAPIC_CUR_CNT 0x390u
#define LVT_MASK (1u << 16)
#define LVT_PERIODIC (1u << 17)
static volatile u32 *g_lapic = 0;
static int g_timer_vector = -1;
static inline u64 rdmsr_(u32 msr) {
u32 lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((u64)hi << 32) | lo;
}
static inline void wrmsr_(u32 msr, u64 v) {
u32 lo = (u32)v, hi = (u32)(v >> 32);
__asm__ volatile("wrmsr" :: "c"(msr), "a"(lo), "d"(hi));
}
static u32 lapic_read(u32 reg) { return g_lapic[reg / 4]; }
static void lapic_write(u32 reg, u32 v) { g_lapic[reg / 4] = v; }
int lapic_timer_init(u32 freq_hz) {
if (freq_hz == 0) freq_hz = 100;
if (!g_lapic) {
u64 base = rdmsr_(MSR_IA32_APIC_BASE);
if (!(base & APIC_BASE_ENABLE)) {
wrmsr_(MSR_IA32_APIC_BASE, base | APIC_BASE_ENABLE);
base = rdmsr_(MSR_IA32_APIC_BASE);
}
if (base & APIC_BASE_X2APIC) {
/* 【插桩白名单】设计内降级:x2APIC 不支持 MMIO timer,降级协作模式 */
log_warn("[LAPIC] x2APIC mode, MMIO timer unsupported");
return -1;
}
u64 phys = base & 0xFFFFF000ULL;
if (phys == 0) phys = LAPIC_DEFAULT_PHYS;
const dkm_mmio_api *mm = paging_get_api();
if (!mm || !mm->map_mmio) return -2;
if (mm->map_mmio(phys, 0x1000) != 0) {
log_warn("[LAPIC] map_mmio failed");
panic_full("LAPIC-E01 MAP_MMIO FAILED",
"lapic_timer_init: LAPIC MMIO mapping failed", 0);
return -3;
}
u64 hhdm = 0;
const dkm_kernel_api *api = dkm_get_kernel_api();
if (api) hhdm = api->hhdm_offset;
g_lapic = (volatile u32 *)(hhdm + phys);
if (mm->is_mapped && !mm->is_mapped((u64)g_lapic)) {
g_lapic = 0;
log_warn("[LAPIC] mapped page not reachable");
panic_full("LAPIC-E02 MAPPED PAGE NOT REACHABLE",
"lapic_timer_init: mapped LAPIC page not reachable", 0);
return -4;
}
/* 软件使能(spurious bit8);vector 保留既有值,无则 0xFF */
u32 svr = lapic_read(LAPIC_SPURIOUS);
if (!(svr & 0x100u)) {
u8 vec = (u8)(svr & 0xFFu);
if (vec == 0) vec = 0xFFu;
lapic_write(LAPIC_SPURIOUS, (u32)vec | 0x100u);
}
lapic_write(LAPIC_TPR, 0);
log_info("[LAPIC] mmio mapped");
log_hex64("[LAPIC] phys=", phys);
}
/* ---- PIT 单发校准:divide=16,10ms 窗口采样 CCR 差值 ---- */
lapic_write(LAPIC_DIV_CONF, 0x3); /* divide by 16 */
lapic_write(LAPIC_LVT_TIMER, (u32)0xFF | LVT_MASK | LVT_PERIODIC); /* 校准期 mask */
lapic_write(LAPIC_INIT_CNT, 0xFFFFFFFFu);
u32 ccr0 = 0;
for (int i = 0; i < 1000; i++) {
ccr0 = lapic_read(LAPIC_CUR_CNT);
if (ccr0 != 0) break;
__asm__ volatile("pause");
}
pit_oneshot_ms(10);
u16 prev = pit_latch_count();
u32 loops = 0;
for (;;) {
u16 cur = pit_latch_count();
if (cur > prev && loops > 10) break; /* 回绕 = 10ms 到期 */
prev = cur;
loops++;
}
u32 ccr1 = lapic_read(LAPIC_CUR_CNT);
u64 delta = (u64)ccr0 - (u64)ccr1; /* 16 分频时钟 10ms tick 数 */
if (delta < 1000) {
log_warn("[LAPIC] timer calibration implausible");
panic_full("LAPIC-E03 CALIBRATION IMPLAUSIBLE",
"lapic_timer_init: PIT-vs-LAPIC calibration implausible", 0);
lapic_timer_stop();
return -5;
}
/* 期望周期 = delta * (1000 / freq_hz) / 10 */
u64 init = delta * 1000ULL / (u64)freq_hz / 10ULL;
if (init == 0) init = 1;
if (init > 0xFFFFFFFFULL) init = 0xFFFFFFFFULL;
if (g_timer_vector < 0) {
int vec = irq_vector_alloc();
if (vec < 0) {
log_warn("[LAPIC] irq_vector_alloc failed");
panic_full("LAPIC-E04 IRQ VECTOR ALLOC FAILED",
"lapic_timer_init: dynamic vector allocation failed", 0);
return -6;
}
g_timer_vector = vec;
}
/* 按校准值定频编程(LVT 保持 mask,start() 再放开)。
* 顺序:divide config → LVT(masked) → initial count。 */
lapic_write(LAPIC_DIV_CONF, 0x3);
lapic_write(LAPIC_LVT_TIMER,
((u32)g_timer_vector & 0xFFu) | LVT_MASK | LVT_PERIODIC);
lapic_write(LAPIC_INIT_CNT, (u32)init);
log_info("[LAPIC] timer calibrated");
log_hex64("[LAPIC] ticks/10ms=", delta);
log_hex64("[LAPIC] init_count=", init);
log_hex64("[LAPIC] vector=", (u64)g_timer_vector);
return 0;
}
int lapic_timer_vector(void) {
return g_timer_vector;
}
void lapic_timer_start(void) {
if (!g_lapic || g_timer_vector < 0) return;
/* 保持 divide/init 不变,仅解除 mask(periodic 模式已配) */
lapic_write(LAPIC_LVT_TIMER,
((u32)g_timer_vector & 0xFFu) | LVT_PERIODIC);
}
void lapic_timer_stop(void) {
if (g_lapic) {
lapic_write(LAPIC_LVT_TIMER, lapic_read(LAPIC_LVT_TIMER) | LVT_MASK);
lapic_write(LAPIC_INIT_CNT, 0);
}
if (g_timer_vector >= 0) {
irq_vector_free(g_timer_vector);
g_timer_vector = -1;
}
}
void lapic_timer_eoi(void) {
if (g_lapic) {
lapic_write(LAPIC_EOI, 0);
}
}
-154
View File
@@ -1,154 +0,0 @@
#ifndef UTSM_LIMINE_H
#define UTSM_LIMINE_H
#include <utsm/types.h>
#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88ULL, 0x0a82e883a194f07bULL
struct limine_hhdm_response {
u64 revision;
u64 offset;
};
struct limine_hhdm_request {
u64 id[4];
u64 revision;
struct limine_hhdm_response *response;
};
struct limine_kernel_address_response {
u64 revision;
u64 physical_base;
u64 virtual_base;
};
struct limine_kernel_address_request {
u64 id[4];
u64 revision;
struct limine_kernel_address_response *response;
};
#define LIMINE_KERNEL_ADDRESS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63ULL, 0xb2644a48c516a487ULL }
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
struct limine_module_request {
u64 id[4];
u64 revision;
struct limine_module_response *response;
};
#define LIMINE_MODULE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3e7e279702be32afULL, 0xca1c4f3bd1280ceeULL }
struct limine_rsdp_response {
u64 revision;
void *address;
};
struct limine_rsdp_request {
u64 id[4];
u64 revision;
struct limine_rsdp_response *response;
};
#define LIMINE_RSDP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43ULL, 0x27637845accdcf3cULL }
struct limine_framebuffer {
void *address;
u64 width;
u64 height;
u64 pitch;
u16 bpp;
u8 memory_model;
u8 red_mask_size;
u8 red_mask_shift;
u8 green_mask_size;
u8 green_mask_shift;
u8 blue_mask_size;
u8 blue_mask_shift;
u8 unused[7];
u64 edid_size;
void *edid;
u64 mode_count;
struct limine_video_mode **modes;
};
struct limine_video_mode {
u64 pitch;
u64 width;
u64 height;
u16 bpp;
u8 memory_model;
u8 red_mask_size;
u8 red_mask_shift;
u8 green_mask_size;
u8 green_mask_shift;
u8 blue_mask_size;
u8 blue_mask_shift;
};
struct limine_framebuffer_response {
u64 revision;
u64 framebuffer_count;
struct limine_framebuffer **framebuffers;
};
struct limine_framebuffer_request {
u64 id[4];
u64 revision;
struct limine_framebuffer_response *response;
};
#define LIMINE_FRAMEBUFFER_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75ULL, 0xa3148604f6fab11bULL }
#define LIMINE_MEMMAP_USABLE 0
#define LIMINE_MEMMAP_RESERVED 1
#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
#define LIMINE_MEMMAP_ACPI_NVS 3
#define LIMINE_MEMMAP_BAD_MEMORY 4
#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6
#define LIMINE_MEMMAP_FRAMEBUFFER 7
struct limine_memmap_entry {
u64 base;
u64 length;
u64 type;
};
struct limine_memmap_response {
u64 revision;
u64 entry_count;
struct limine_memmap_entry **entries;
};
struct limine_memmap_request {
u64 id[4];
u64 revision;
struct limine_memmap_response *response;
};
#define LIMINE_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806fULL, 0xe304acdfc50c3c62ULL }
#endif
-45
View File
@@ -1,45 +0,0 @@
/* pit.c — PIT ch0 单发定时(LAPIC timer 校准用)
*
* 只用 mode 0(terminal count):单发、无周期中断、不注册 IRQ handler。
* 禁用 mode 3 —— B7 定论:PIT mode3 方波经 IOAPIC 边沿路由会双触发 (~200Hz)。
*/
#include <utsm/pit.h>
extern void outb(u16 port, u8 value);
extern u8 inb(u16 port);
#define PIT_CH0_DATA 0x40
#define PIT_CMD 0x43
#define PIT_BASE_HZ 1193182ULL
void pit_oneshot_ms(u32 ms) {
u64 div = (PIT_BASE_HZ * (u64)ms) / 1000ULL;
if (div == 0) div = 1;
if (div > 65535) div = 65535;
/* ch0 | lo/hi | mode0 (terminal count) | binary */
outb(PIT_CMD, 0x30);
outb(PIT_CH0_DATA, (u8)(div & 0xFF));
outb(PIT_CH0_DATA, (u8)((div >> 8) & 0xFF));
}
u16 pit_latch_count(void) {
outb(PIT_CMD, 0x00); /* ch0 latch 计数(读前锁存) */
u16 lo = inb(PIT_CH0_DATA);
u16 hi = inb(PIT_CH0_DATA);
return (u16)(lo | (hi << 8));
}
int pit_oneshot_expired(void) {
u16 c1 = pit_latch_count();
u16 c2 = pit_latch_count();
/* mode0 计到 0 后回绕继续递减:cur 回升即视为到期 */
return c2 >= c1;
}
void pit_quiet(void) {
/* ch0 mode0、计数 0:terminal count 立即到期,无后续输出/中断 */
outb(PIT_CMD, 0x30);
outb(PIT_CH0_DATA, 0x00);
outb(PIT_CH0_DATA, 0x00);
}
-96
View File
@@ -1,96 +0,0 @@
#include <utsm/types.h>
void outb(u16 port, u8 value);
u8 inb(u16 port);
#define COM1 0x3F8
/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */
static u64 g_tsc_per_ms = 0;
static inline u64 rdtsc_ser(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
/* 用 PIT (8254, 1.193182 MHz) 校准 TSC 频率 */
static void tsc_calibrate_ser(void) {
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
outb(0x40, 0x7c); /* 11932 low = ~10ms */
outb(0x40, 0x2e); /* 11932 high */
u64 tsc_start = rdtsc_ser();
u16 prev = 0; u64 loops = 0;
for (;;) {
outb(0x43, 0x00);
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
if (cur > prev && loops > 10) break;
prev = cur; loops++;
}
u64 tsc_end = rdtsc_ser();
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
}
static int serial_ready(void) {
return (inb(COM1 + 5) & 0x20) != 0;
}
/* 实机: 基于 TSC 的 100ms 超时等待 */
static int serial_wait_ready(void) {
if (!g_tsc_per_ms) {
/* 兜底: 校准未完成时使用循环 */
for (u64 spin = 0; spin < 1000000ULL; spin++) {
if (serial_ready()) return 1;
}
return 0;
}
u64 deadline = rdtsc_ser() + g_tsc_per_ms * 100; /* 100ms 超时 */
while (rdtsc_ser() < deadline) {
if (serial_ready()) return 1;
__asm__ volatile("pause");
}
return 0;
}
void serial_init(void) {
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
tsc_calibrate_ser();
outb(COM1 + 1, 0x00);
outb(COM1 + 3, 0x80);
outb(COM1 + 0, 0x03);
outb(COM1 + 1, 0x00);
outb(COM1 + 3, 0x03);
outb(COM1 + 2, 0xC7);
outb(COM1 + 4, 0x0B);
}
void serial_putc(char c) {
if (c == '\n') {
serial_putc('\r');
}
if (!serial_wait_ready()) {
return;
}
outb(COM1, (u8)c);
}
void serial_write(const char *s) {
while (*s) {
serial_putc(*s++);
}
}
/* 非阻塞读取 host 串口一个字节(QEMU stdio → guest COM1 输入桥)。
* 返回 0-255,无数据返回 -1。供 vmexit 在 guest resume 前轮询。 */
int serial_try_read(void) {
if (inb(COM1 + 5) & 0x01) {
return inb(COM1);
}
return -1;
}
/* 导出 PIT 校准的 TSC 频率(kHz 量级,单位 ticks/ms),
* 供 VMX preemption timer 与 guest PIT tick 注入使用。 */
u64 serial_tsc_per_ms(void) {
return g_tsc_per_ms;
}
-30
View File
@@ -1,30 +0,0 @@
/* ===================================================================
* brand.c — 五期 E 系列:品牌体验骨架
*
* 设计已定、实现未做的品牌项,全部立骨架 + 调用即莲花 panic。
* 骨架不接线:当前启动走静态 Logo(DSK),接线上屏是后续任务。
*
* E1 启动莲花动画:静态 Logo → 分瓣绽放动画(时间轴设计见
* docs/ROADMAP_INSANE.md E1)。实现前调用即 panic。
* E2 假想时光键彩蛋:特定按键序列触发"时光倒流"演出(回放最近
* 一段帧缓冲历史,DRR checkpoint 帧源)。实现前调用即 panic。
* =================================================================== */
#include <utsm/types.h>
#include <utsm/panic.h>
#include <utsm/log.h>
/* E1: 启动莲花动画(brand-E 系列) */
void brand_lotus_boot_anim(u32 hold_ms) {
(void)hold_ms;
log_error("[BRAND] lotus boot animation requested");
panic_full("BRAND-E01 LOTUS BOOT ANIM NOT IMPLEMENTED",
"E1 petal-bloom boot animation: designed, not implemented", 0);
}
/* E2: 假想时光键彩蛋(Ctrl+Alt+Shift+T 时光倒流演出) */
void brand_time_egg_trigger(void) {
log_error("[BRAND] time egg triggered");
panic_full("BRAND-E02 TIME EGG NOT IMPLEMENTED",
"E2 imaginary time-key easter egg: designed, not implemented", 0);
}
-29
View File
@@ -1,29 +0,0 @@
#include <utsm/capability.h>
#include <utsm/segment.h>
int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, utsm_segment_desc **out_desc) {
utsm_segment_desc *desc = utsm_get_segment(cap.segment_slot);
if (!desc) {
return UTSM_ERR_INVALID;
}
if (desc->state == UTSM_SEG_POISONED) {
return UTSM_ERR_POISONED;
}
if (desc->state != UTSM_SEG_ACTIVE) {
return UTSM_ERR_STATE;
}
if (desc->generation != cap.generation) {
return UTSM_ERR_STALE_CAP;
}
if ((cap.rights & required_rights) != required_rights) {
return UTSM_ERR_ACCESS;
}
if ((u32)desc->key_epoch != cap.epoch) {
return UTSM_ERR_EPOCH;
}
if (offset > desc->cipher_length || len > desc->cipher_length || offset + len > desc->cipher_length) {
return UTSM_ERR_BOUNDS;
}
*out_desc = desc;
return UTSM_OK;
}
-47
View File
@@ -1,47 +0,0 @@
#include <utsm/dirty.h>
#include <utsm/segment.h>
#include <utsm/arena.h>
#include <utsm/config.h>
static void bitmap_set(u64 *bitmap, u64 bit) {
bitmap[bit / 64] |= 1ULL << (bit % 64);
}
static bool bitmap_test(u64 *bitmap, u64 bit) {
return (bitmap[bit / 64] & (1ULL << (bit % 64))) != 0;
}
void utsm_dirty_init(utsm_segment_desc *desc, u64 page_count) {
desc->dirty_shard_count = 1;
desc->dirty_shards = (utsm_dirty_shard *)kmem_alloc(sizeof(utsm_dirty_shard));
if (!desc->dirty_shards) {
return;
}
u64 words = (page_count + 63) / 64;
desc->dirty_shards[0].dirty_count = 0;
desc->dirty_shards[0].shard_id = 0;
desc->dirty_shards[0].page_base = 0;
desc->dirty_shards[0].page_count = page_count;
desc->dirty_shards[0].last_checkpoint_epoch = 0;
desc->dirty_shards[0].bitmap = (u64 *)kmem_alloc_aligned(words * sizeof(u64), 8);
if (desc->dirty_shards[0].bitmap) {
for (u64 i = 0; i < words; i++) {
desc->dirty_shards[0].bitmap[i] = 0;
}
}
}
void utsm_mark_dirty(utsm_segment_desc *desc, u64 offset, u64 len) {
if (!desc || !desc->dirty_shards || !desc->dirty_shards[0].bitmap || len == 0) {
return;
}
u64 first = offset / UTSM_PAGE_SIZE;
u64 last = (offset + len - 1) / UTSM_PAGE_SIZE;
utsm_dirty_shard *shard = &desc->dirty_shards[0];
for (u64 page = first; page <= last && page < shard->page_count; page++) {
if (!bitmap_test(shard->bitmap, page)) {
bitmap_set(shard->bitmap, page);
shard->dirty_count++;
}
}
}
-20
View File
@@ -1,20 +0,0 @@
#include <utsm/dmp.h>
#include <utsm/segment.h>
#define UTSM_DMP_MAGIC 0x5554534d444d5031ULL
void utsm_dmp_init(utsm_dmp *dmp, utsm_segment_desc *desc, u32 slot) {
dmp->magic = UTSM_DMP_MAGIC;
dmp->version = 1;
dmp->segment_slot = slot;
dmp->fast_base = (u64)desc->cipher_base;
dmp->fast_limit = (u64)desc->cipher_base + desc->cipher_length;
dmp->fast_tweak_seed = desc->tweak_seed;
dmp->fast_key_epoch = desc->key_epoch;
dmp->dirty_bitmap_base = desc->dirty_shards ? (u64)desc->dirty_shards[0].bitmap : 0;
dmp->mac_table_base = (u64)desc->mac_table;
dmp->last_writer_seq = desc->writer_seq;
dmp->last_cpu = desc->last_cpu;
dmp->flags = desc->flags;
dmp->crc = dmp->magic ^ dmp->fast_base ^ dmp->fast_limit ^ dmp->fast_tweak_seed;
}
-126
View File
@@ -1,126 +0,0 @@
/* process.c — UTSM 进程上下文管理
*
* 进程表分配/释放、capability table 操作、crypto context 管理。
*/
#include <utsm/process.h>
#include <utsm/drr.h>
#include <utsm/arena.h>
#include <utsm/log.h>
/* ---- 进程表 ---- */
static utsm_process_context g_process_table[UTSM_MAX_PROCESSES];
static u32 g_process_count;
void utsm_process_init(void) {
log_info("[UTSM] process table init");
for (u32 i = 0; i < UTSM_MAX_PROCESSES; i++) {
g_process_table[i].state = UTSM_PROC_UNUSED;
g_process_table[i].process_slot = i;
g_process_table[i].cap_table.count = 0;
g_process_table[i].cap_table.generation = 0;
}
g_process_count = 0;
}
void utsm_process_create_test(utsm_process_context *process, u64 seed) {
process->process_uuid = uuid_make_test(seed);
process->crypto_epoch = 1;
process->recovery_generation = drr_recovery_generation();
process->capability_count = 0;
process->flags = UTSM_PROC_F_KERNEL;
process->state = UTSM_PROC_ACTIVE;
process->hot_segment_slot = 0;
process->drr_recovery_gen = drr_recovery_generation();
/* 初始化 crypto context */
process->crypto_ctx.crypto_epoch = 1;
process->crypto_ctx.key_derivation_seed = seed ^ 0x9e3779b97f4a7c15ULL;
process->crypto_ctx.pckc_hot_hint = 0;
/* 初始化 capability table */
process->cap_table.count = 0;
process->cap_table.generation = 1;
}
utsm_process_context *utsm_process_alloc(void) {
for (u32 i = 0; i < UTSM_MAX_PROCESSES; i++) {
if (g_process_table[i].state == UTSM_PROC_UNUSED) {
utsm_process_context *p = &g_process_table[i];
p->process_slot = i;
p->state = UTSM_PROC_ACTIVE;
p->crypto_epoch = 1;
p->recovery_generation = drr_recovery_generation();
p->capability_count = 0;
p->flags = 0;
p->hot_segment_slot = 0;
p->drr_recovery_gen = drr_recovery_generation();
p->cap_table.count = 0;
p->cap_table.generation = 1;
p->crypto_ctx.crypto_epoch = 1;
p->crypto_ctx.key_derivation_seed = 0;
p->crypto_ctx.pckc_hot_hint = 0;
g_process_count++;
return p;
}
}
return NULL;
}
void utsm_process_free(utsm_process_context *process) {
if (!process) return;
process->state = UTSM_PROC_UNUSED;
process->cap_table.count = 0;
process->capability_count = 0;
if (g_process_count > 0) g_process_count--;
}
utsm_process_context *utsm_process_get(u32 slot) {
if (slot >= UTSM_MAX_PROCESSES) return NULL;
return &g_process_table[slot];
}
/* ---- Capability 操作 ---- */
int utsm_process_grant_cap(utsm_process_context *process, utsm_capability cap) {
if (!process || process->cap_table.count >= UTSM_MAX_CAPABILITIES) {
return -1;
}
u32 idx = process->cap_table.count;
process->cap_table.caps[idx] = cap;
process->cap_table.count++;
process->capability_count = process->cap_table.count;
process->cap_table.generation++;
return 0;
}
int utsm_process_revoke_cap(utsm_process_context *process, u32 cap_index) {
if (!process || cap_index >= process->cap_table.count) {
return -1;
}
/* 将最后一个 capability 移到被删除的位置 */
u32 last = process->cap_table.count - 1;
if (cap_index != last) {
process->cap_table.caps[cap_index] = process->cap_table.caps[last];
}
process->cap_table.count--;
process->capability_count = process->cap_table.count;
process->cap_table.generation++;
return 0;
}
utsm_capability *utsm_process_get_cap(utsm_process_context *process, u32 cap_index) {
if (!process || cap_index >= process->cap_table.count) {
return NULL;
}
return &process->cap_table.caps[cap_index];
}
/* ---- Crypto context 操作 ---- */
void utsm_process_advance_epoch(utsm_process_context *process) {
if (!process) return;
process->crypto_epoch++;
process->crypto_ctx.crypto_epoch = process->crypto_epoch;
/* 清空 PCKC 热提示(epoch 变化后旧缓存无效) */
process->crypto_ctx.pckc_hot_hint = 0;
process->hot_segment_slot = 0;
}
-78
View File
@@ -1,78 +0,0 @@
/* ===================================================================
* sealed.c — 封缄内存极早期初始化与校验
*
* 链接脚本已有 .sealed section([__sealed_start, __sealed_end),
* U3 @sealed 标记),但历史上无任何代码引用它:封缄段从未建立、
* 从未 MAC、从未校验——"封缄内存还没做好"。
*
* 本模块在 UTSM 极早期(utsm_init 之后、驱动/guest/DSK 之前):
* 1. .sealed 区为空 → panic UTSM-E01(封缄基础设施未就绪,零降级)
* 2. 否则以 root key 为 key,BLAKE2b 对整个 .sealed 区算封缄 MAC,
* 存入 g_sealed_mac,置 ready。
* 3. utsm_sealed_verify() 重算比对,不符 → UTSM-E03。
* (供 selftest / checkpoint 路径调用;骨架阶段无固定调用方。)
* =================================================================== */
#include <utsm/types.h>
#include <utsm/panic.h>
#include <utsm/log.h>
#include <utsm/blake2b.h>
#include <utsm/drr.h>
/* linker.ld 导出的 .sealed 段边界 */
extern u8 __sealed_start[];
extern u8 __sealed_end[];
#define UTSM_SEALED_MAC_LEN 32
static u8 g_sealed_mac[UTSM_SEALED_MAC_LEN];
static int g_sealed_ready = 0;
/* 封缄区常驻品牌落款:保证 .sealed 区非空(否则 UTSM-E01 panic),
* 同时是封缄 MAC 保护的第一份真实数据。后续模块把机密放进
* __attribute__((section(".sealed"))) 即自动纳入同一 MAC 域。 */
__attribute__((used, section(".sealed")))
static const char g_sealed_brand[] = "DESHAB-SEALED-MEMORY-V1-DEAICUP";
/* 极早期封缄初始化:任何不就绪都在这里 panic,绝不带病前进 */
int utsm_sealed_early_init(void) {
u64 region = (u64)(__sealed_end - __sealed_start);
if (region == 0) {
/* .sealed 区为空 = 没有任何数据被真正封缄 = 基础设施未就绪 */
panic_full("UTSM-E01 SEALED MEMORY NOT READY",
".sealed section is empty: sealing infrastructure not implemented", 0);
return -1;
}
const u64 *rk = drr_get_root_key();
if (!rk) {
panic_full("UTSM-E02 SEALED NO ROOT KEY",
"root key unavailable before sealing .sealed region", 0);
return -1;
}
utsm_blake2b(__sealed_start, region, rk, 32, g_sealed_mac, UTSM_SEALED_MAC_LEN);
g_sealed_ready = 1;
log_hex64("[SEAL] .sealed region bytes=", region);
log_info("[SEAL] sealed MAC established");
return 0;
}
/* 封缄完整性校验:重算 MAC 并与 init 时的封缄比对 */
void utsm_sealed_verify(void) {
if (!g_sealed_ready) {
panic_full("UTSM-E04 SEALED VERIFY BEFORE INIT",
"utsm_sealed_verify called before utsm_sealed_early_init", 0);
return;
}
u64 region = (u64)(__sealed_end - __sealed_start);
u8 mac[UTSM_SEALED_MAC_LEN];
const u64 *rk = drr_get_root_key();
utsm_blake2b(__sealed_start, region, rk, 32, mac, UTSM_SEALED_MAC_LEN);
u64 diff = 0;
for (int i = 0; i < UTSM_SEALED_MAC_LEN; i++) diff |= (u64)(mac[i] ^ g_sealed_mac[i]);
if (diff != 0) {
log_hex64("[SEAL] verify diff=", diff);
panic_full("UTSM-E03 SEALED MAC MISMATCH",
".sealed region content changed without re-seal", 0);
}
log_info("[SEAL] sealed verify ok");
}
-80
View File
@@ -1,80 +0,0 @@
#include <utsm/segment.h>
#include <utsm/process.h>
#include <utsm/arena.h>
#include <utsm/dmp.h>
#include <utsm/config.h>
u64 utsm_next_generation(void);
static u64 digest_uuid(uuid128_t id) {
return id.hi ^ id.lo ^ (id.hi >> 32) ^ (id.lo << 17);
}
static u64 make_tweak_seed(uuid128_t process_uuid, uuid128_t segment_uuid, u64 epoch) {
return process_uuid.hi ^ process_uuid.lo ^ segment_uuid.hi ^ segment_uuid.lo ^ (epoch * 0x9e3779b97f4a7c15ULL);
}
int utsm_create_segment(utsm_process_context *process, u64 size, u32 flags, utsm_capability *out_cap) {
if (!process || !out_cap || size == 0) {
return UTSM_ERR_INVALID;
}
u32 slot = UTSM_MAX_SEGMENTS;
utsm_segment_desc *desc = NULL;
for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) {
utsm_segment_desc *candidate = utsm_get_segment(i);
if (candidate->state == UTSM_SEG_FREE) {
slot = i;
desc = candidate;
break;
}
}
if (!desc) {
return UTSM_ERR_NO_MEMORY;
}
u64 length = utsm_align_up_u64(size, UTSM_CACHE_LINE_SIZE);
u64 pages = utsm_align_up_u64(length, UTSM_PAGE_SIZE) / UTSM_PAGE_SIZE;
u8 *cipher = (u8 *)kmem_alloc_page(pages);
utsm_dmp *dmp = (utsm_dmp *)kmem_alloc_aligned(sizeof(utsm_dmp), 64);
void *mac_table = kmem_alloc_aligned(pages * sizeof(u64), 8);
if (!cipher || !dmp || !mac_table) {
return UTSM_ERR_NO_MEMORY;
}
for (u64 i = 0; i < pages * UTSM_PAGE_SIZE; i++) {
cipher[i] = 0;
}
desc->segment_uuid = uuid_make_test(utsm_next_generation() + slot);
desc->owner_process_uuid = process->process_uuid;
desc->cipher_base = cipher;
desc->cipher_length = length;
desc->dmp_base = dmp;
desc->key_epoch = process->crypto_epoch;
desc->tweak_seed = make_tweak_seed(process->process_uuid, desc->segment_uuid, desc->key_epoch);
desc->mac_table = mac_table;
desc->generation++;
desc->flags = flags;
desc->state = UTSM_SEG_ACTIVE;
desc->last_cpu = 0;
desc->writer_seq = 0;
desc->checkpoint_generation = process->recovery_generation;
utsm_dirty_init(desc, pages);
utsm_dmp_init(dmp, desc, slot);
out_cap->segment_slot = slot;
out_cap->generation = desc->generation;
out_cap->rights = 0;
if (flags & UTSM_SEG_F_READ) out_cap->rights |= UTSM_RIGHT_READ;
if (flags & UTSM_SEG_F_WRITE) out_cap->rights |= UTSM_RIGHT_WRITE;
if (flags & UTSM_SEG_F_EXEC) out_cap->rights |= UTSM_RIGHT_EXEC;
if (flags & UTSM_SEG_F_DMA) out_cap->rights |= UTSM_RIGHT_DMA;
out_cap->epoch = (u32)desc->key_epoch;
out_cap->uuid_digest = digest_uuid(desc->segment_uuid);
out_cap->auth_tag = out_cap->uuid_digest ^ digest_uuid(process->process_uuid) ^ desc->key_epoch;
process->capability_count++;
return UTSM_OK;
}
-60
View File
@@ -1,60 +0,0 @@
#include <utsm/utsm.h>
#include <utsm/segment.h>
#include <utsm/pckc.h>
#include <utsm/crypto.h>
#include <utsm/process.h>
#include <utsm/panic.h>
#include <utsm/log.h>
#include <utsm/config.h>
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
static u64 g_generation = 1;
void utsm_init(void) {
log_info("[UTSM] segment table reset begin");
u32 max_seg = g_utsm_max_segments;
if (max_seg > UTSM_MAX_SEGMENTS) max_seg = UTSM_MAX_SEGMENTS;
for (u32 i = 0; i < max_seg; i++) {
g_segments[i].state = UTSM_SEG_FREE;
g_segments[i].generation = 1;
}
log_info("[UTSM] segment table reset ok");
g_generation = 1;
log_info("[UTSM] pckc init begin");
utsm_pckc_init();
log_info("[UTSM] pckc init ok");
/* SAS-R0 特色自证:PCKC 启动期 key 派生往返。派生失败或密钥全零
* = Per-CPU 密钥缓存没有真正立起来,零降级 panic(PCKC-E01)。 */
{
utsm_segment_desc probe;
for (u32 i = 0; i < sizeof(probe); i++) ((u8 *)&probe)[i] = 0;
probe.key_epoch = 1;
utsm_key_material km;
for (u32 i = 0; i < 4; i++) km.words[i] = 0;
if (utsm_pckc_get_or_derive(0xFFFFFFFFu, &probe, &km) != UTSM_OK) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time get_or_derive returned error", 0);
}
if (km.words[0] == 0 && km.words[1] == 0 && km.words[2] == 0 && km.words[3] == 0) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time derive produced all-zero key material", 0);
}
utsm_pckc_invalidate_slot(0xFFFFFFFFu); /* 探针段作废,不留缓存 */
log_info("[UTSM] pckc derive roundtrip ok");
}
utsm_process_init();
}
utsm_segment_desc *utsm_get_segment(u32 slot) {
if (slot >= g_utsm_max_segments || slot >= UTSM_MAX_SEGMENTS) {
return NULL;
}
return &g_segments[slot];
}
u64 utsm_next_generation(void) {
return g_generation++;
}
-137
View File
@@ -1,137 +0,0 @@
/* blake2b.c — BLAKE2b(RFC 7693)实现
*
* 12 轮、128B 块、64-bit G 函数(旋转 32/24/16/63)。
* keyed 模式:init 时将 key 填充为首个 128B 块(RFC 7693 §2.3),
* update/final 沿用引用实现语义(惰性缓冲 + 计数器含 key 块)。
*/
#include <utsm/blake2b.h>
static const u64 blake2b_iv[8] = {
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
};
static const u8 blake2b_sigma[10][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }
};
static inline u64 rotr64(u64 v, u32 c) {
return (v >> c) | (v << (64 - c));
}
#define B2B_G(v, a, b, c, d, x, y) do { \
(v)[a] = (v)[a] + (v)[b] + (x); (v)[d] = rotr64((v)[d] ^ (v)[a], 32); \
(v)[c] = (v)[c] + (v)[d]; (v)[b] = rotr64((v)[b] ^ (v)[c], 24); \
(v)[a] = (v)[a] + (v)[b] + (y); (v)[d] = rotr64((v)[d] ^ (v)[a], 16); \
(v)[c] = (v)[c] + (v)[d]; (v)[b] = rotr64((v)[b] ^ (v)[c], 63); \
} while (0)
static void blake2b_compress(utsm_blake2b_ctx *ctx, const u8 *block) {
u64 m[16];
u64 v[16];
for (int i = 0; i < 16; i++) {
m[i] = utsm_load64_le(block + 8 * i);
}
for (int i = 0; i < 8; i++) {
v[i] = ctx->h[i];
v[i + 8] = blake2b_iv[i];
}
v[12] ^= ctx->t[0];
v[13] ^= ctx->t[1];
if (ctx->last_block) {
v[14] ^= ~(u64)0;
}
for (int r = 0; r < 12; r++) {
const u8 *s = blake2b_sigma[r % 10];
B2B_G(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
B2B_G(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
B2B_G(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
B2B_G(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
B2B_G(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
B2B_G(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
B2B_G(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
B2B_G(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
}
for (int i = 0; i < 8; i++) {
ctx->h[i] ^= v[i] ^ v[i + 8];
}
}
void utsm_blake2b_init(utsm_blake2b_ctx *ctx, u32 out_len, const void *key, u32 key_len) {
if (out_len == 0 || out_len > UTSM_BLAKE2B_MAXOUT) out_len = UTSM_BLAKE2B_MAXOUT;
if (key_len > 64u) key_len = 64u;
for (int i = 0; i < 8; i++) ctx->h[i] = blake2b_iv[i];
ctx->h[0] ^= 0x01010000ULL ^ ((u64)key_len << 8) ^ (u64)out_len;
ctx->t[0] = 0;
ctx->t[1] = 0;
ctx->out_len = out_len;
ctx->buf_len = 0;
ctx->last_block = 0;
if (key && key_len > 0u) {
const u8 *k = (const u8 *)key;
for (u32 i = 0; i < key_len; i++) ctx->buf[i] = k[i];
for (u32 i = key_len; i < 128u; i++) ctx->buf[i] = 0;
ctx->buf_len = 128u; /* key 块挂起,随首个 update/final 压缩 */
}
}
void utsm_blake2b_update(utsm_blake2b_ctx *ctx, const void *data, u64 len) {
const u8 *in = (const u8 *)data;
while (len > 0) {
u32 left = ctx->buf_len;
u32 fill = UTSM_BLAKE2B_BLOCKBYTES - left;
if (len > (u64)fill) {
for (u32 i = 0; i < fill; i++) ctx->buf[left + i] = in[i];
ctx->t[0] += 128;
if (ctx->t[0] < 128) ctx->t[1]++;
blake2b_compress(ctx, ctx->buf);
ctx->buf_len = 0;
in += fill;
len -= fill;
} else {
for (u64 i = 0; i < len; i++) ctx->buf[left + i] = in[i];
ctx->buf_len += (u32)len;
return;
}
}
}
void utsm_blake2b_final(utsm_blake2b_ctx *ctx, void *out) {
ctx->t[0] += ctx->buf_len;
if (ctx->t[0] < (u64)ctx->buf_len) ctx->t[1]++;
ctx->last_block = 1;
for (u32 i = ctx->buf_len; i < UTSM_BLAKE2B_BLOCKBYTES; i++) ctx->buf[i] = 0;
blake2b_compress(ctx, ctx->buf);
u8 tmp[64];
for (int i = 0; i < 8; i++) utsm_store64_le(tmp + 8 * i, ctx->h[i]);
u8 *o = (u8 *)out;
for (u32 i = 0; i < ctx->out_len; i++) o[i] = tmp[i];
}
void utsm_blake2b(const void *data, u64 len, const void *key, u32 key_len, void *out, u32 out_len) {
utsm_blake2b_ctx ctx;
utsm_blake2b_init(&ctx, out_len, key, key_len);
utsm_blake2b_update(&ctx, data, len);
utsm_blake2b_final(&ctx, out);
}
-60
View File
@@ -1,60 +0,0 @@
/* chacha20.c — ChaCha20 block function(RFC 8439 §2.3)
*
* block function = 20 轮纯 ARX(无 message word 吸收;sigma 表仅用于
* 带明文的加密模式)。UTSM 用途:1 block = 1×64B cache line keystream。
* counter = 0(整条 line 只消费一个 block)
* nonce = tweak(低 32 位在前,高 32 位在后,第三字为 0)
* 纯 32-bit 整数运算,-mno-sse 下可用。
*/
#include <utsm/blake2b.h>
#include <utsm/crypto.h>
static const u32 chacha_iv[4] = {
0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u
};
static inline u32 rotl32(u32 v, u32 c) {
return (v << c) | (v >> (32 - c));
}
static void chacha_qr(u32 *v, int a, int b, int c, int d) {
v[a] += v[b]; v[d] ^= v[a]; v[d] = rotl32(v[d], 16);
v[c] += v[d]; v[b] ^= v[c]; v[b] = rotl32(v[b], 12);
v[a] += v[b]; v[d] ^= v[a]; v[d] = rotl32(v[d], 8);
v[c] += v[d]; v[b] ^= v[c]; v[b] = rotl32(v[b], 7);
}
void utsm_chacha20_block(const u32 key_words[8], u64 nonce, u64 counter, u8 out[64]) {
u32 st[16];
u32 v[16];
st[0] = chacha_iv[0];
st[1] = chacha_iv[1];
st[2] = chacha_iv[2];
st[3] = chacha_iv[3];
for (int i = 0; i < 8; i++) st[4 + i] = key_words[i];
st[12] = (u32)counter;
st[13] = (u32)(counter >> 32);
st[14] = (u32)nonce;
st[15] = (u32)(nonce >> 32);
for (int i = 0; i < 16; i++) v[i] = st[i];
for (int r = 0; r < 10; r++) {
/* column rounds */
chacha_qr(v, 0, 4, 8, 12);
chacha_qr(v, 1, 5, 9, 13);
chacha_qr(v, 2, 6, 10, 14);
chacha_qr(v, 3, 7, 11, 15);
/* diagonal rounds */
chacha_qr(v, 0, 5, 10, 15);
chacha_qr(v, 1, 6, 11, 12);
chacha_qr(v, 2, 7, 8, 13);
chacha_qr(v, 3, 4, 9, 14);
}
for (int i = 0; i < 16; i++) {
utsm_store32_le(out + 4 * i, v[i] + st[i]);
}
}
-31
View File
@@ -1,31 +0,0 @@
/* kdf.c — 段密钥派生(Phase 9:keyed BLAKE2b)
*
* segment_key = BLAKE2b-256(key=root_key, "UTSMSEGK"||p_uuid||s_uuid||key_epoch)
* root key 不直接加密数据;UUID 作为派生调谐因子(非明文密钥)。
* 确定性:同 (puuid, suuid, epoch) 恒得同 key —— PCKC 缓存正确性依赖此性质。
*/
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
#include <utsm/drr.h>
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) {
const u64 *root = drr_get_root_key();
u8 root_le[32];
u8 data[8 + 8 + 8 + 8 + 8];
u8 digest[32];
for (int i = 0; i < 4; i++) utsm_store64_le(root_le + 8 * i, root[i]);
utsm_store64_le(data + 0, 0x4B4745534D535455ULL); /* "UTSMSEGK" LE */
utsm_store64_le(data + 8, process_uuid.hi);
utsm_store64_le(data + 16, process_uuid.lo);
utsm_store64_le(data + 24, segment_uuid.hi);
utsm_store64_le(data + 32, segment_uuid.lo);
utsm_store64_le(data + 40, key_epoch);
utsm_blake2b(data, sizeof(data), root_le, 32, digest, 32);
for (int i = 0; i < 4; i++) out_key->words[i] = utsm_load64_le(digest + 8 * i);
}
-32
View File
@@ -1,32 +0,0 @@
/* mac.c — 4KB 密文页 MAC(Phase 9:keyed BLAKE2b)
*
* page_mac = BLAKE2b-256(key=root_key, "UTSMPAGE"||seg_slot||page_index||key_epoch||cipher_page)[0..8)
* 绑定页归属(段/页号/纪元),防密文页在段间移位或跨纪元重放。
* 用途:DRR checkpoint 快照完整性 / rollback 校验(替换 CRC64 占位 MAC)。
*/
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
#include <utsm/config.h>
u64 utsm_page_mac(const u64 *root_key, u32 seg_slot, u64 page_index, u64 key_epoch, const void *page) {
u8 root_le[32];
u8 ctx_data[8 + 4 + 4 + 8 + 8];
u8 digest[32];
for (int i = 0; i < 4; i++) utsm_store64_le(root_le + 8 * i, root_key[i]);
utsm_store64_le(ctx_data + 0, 0x454741504D535455ULL); /* "UTSMPAGE" LE */
utsm_store32_le(ctx_data + 8, seg_slot);
utsm_store32_le(ctx_data + 12, 0);
utsm_store64_le(ctx_data + 16, page_index);
utsm_store64_le(ctx_data + 24, key_epoch);
utsm_blake2b_ctx c;
utsm_blake2b_init(&c, 32, root_le, 32);
utsm_blake2b_update(&c, ctx_data, sizeof(ctx_data));
utsm_blake2b_update(&c, page, UTSM_PAGE_SIZE);
utsm_blake2b_final(&c, digest);
return utsm_load64_le(digest);
}
-26
View File
@@ -1,26 +0,0 @@
/* stream.c — 64B cache line 流加密(Phase 9:ChaCha20)
*
* ct = pt XOR ChaCha20_block(segment_key, nonce=tweak, counter=0)
* 1 block = 1 line;加密解密同函数(XOR 流)。
* nonce 唯一性:(segment_key 全局唯一) × (line_index 段内唯一),
* key_epoch 混入 key 派生,故同 (key,nonce) 只生成一次 keystream。
*/
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
#include <utsm/config.h>
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) {
u32 kw[8];
for (int i = 0; i < 4; i++) {
kw[2 * i] = (u32)(key->words[i] & 0xFFFFFFFFu);
kw[2 * i + 1] = (u32)(key->words[i] >> 32);
}
u8 ks[64];
utsm_chacha20_block(kw, tweak, 0, ks);
for (u32 i = 0; i < UTSM_CACHE_LINE_SIZE; i++) {
out[i] = in[i] ^ ks[i];
}
}
-14
View File
@@ -1,14 +0,0 @@
#include <utsm/crypto.h>
static u64 mix64_tweak(u64 x) {
x ^= x >> 33;
x *= 0xff51afd7ed558ccdULL;
x ^= x >> 33;
x *= 0xc4ceb9fe1a85ec53ULL;
x ^= x >> 33;
return x;
}
u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch) {
return mix64_tweak(tweak_seed ^ (line_index * 0x9e3779b97f4a7c15ULL) ^ (key_epoch << 32));
}
-486
View File
@@ -1,486 +0,0 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/arena.h>
#include <utsm/panic.h>
#include "../arch/x86_64/limine.h"
#define ELF_MAGIC0 0x7f
#define ELF_MAGIC1 'E'
#define ELF_MAGIC2 'L'
#define ELF_MAGIC3 'F'
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define ET_REL 1
#define ET_DYN 3
#define ET_EXEC 2
#define EM_X86_64 62
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_PROGBITS 1
#define SHT_NOBITS 8
#define SHF_ALLOC 0x2
#define SHF_WRITE 0x1
#define SHF_EXECINSTR 0x4
#define R_X86_64_64 1
#define R_X86_64_32 10
#define R_X86_64_32S 11
#define R_X86_64_PC32 2
/* PLT32:lld 对(可能被覆盖的)全局函数调用生成,静态可重定位场景下
* 语义与 PC32 相同(patch = S + A - P)。多编译单元驱动(如 ath9k +
* wpa2_crypto)跨 TU 调用即为此类型。 */
#define R_X86_64_PLT32 4
void *memset(void *dst, int value, usize len);
void *memcpy(void *dst, const void *src, usize len);
__attribute__((used, section(".limine_requests")))
volatile struct limine_module_request g_module_request = {
.id = LIMINE_MODULE_REQUEST_ID,
.revision = 0,
.response = 0
};
__attribute__((used, section(".limine_requests_start")))
static volatile u64 g_limine_requests_start[4] = { LIMINE_COMMON_MAGIC, 0xf6b8f4b39de7d1aeULL, 0xfab91a6940fcb9cfULL };
__attribute__((used, section(".limine_requests_end")))
static volatile u64 g_limine_requests_end[2] = { 0, 0 };
__attribute__((used, section(".limine_requests")))
volatile struct limine_rsdp_request g_rsdp_request = {
.id = LIMINE_RSDP_REQUEST_ID,
.revision = 0,
.response = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_framebuffer_request g_fb_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST_ID,
.revision = 0,
.response = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_hhdm_request g_hhdm_request = {
.id = { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852ULL, 0x63984e959a98244bULL },
.revision = 0,
.response = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_kernel_address_request g_kernel_address_request = {
.id = LIMINE_KERNEL_ADDRESS_REQUEST_ID,
.revision = 0,
.response = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_memmap_request g_memmap_request = {
.id = LIMINE_MEMMAP_REQUEST_ID,
.revision = 0,
.response = 0
};
typedef struct elf64_ehdr {
u8 ident[16];
u16 type;
u16 machine;
u32 version;
u64 entry;
u64 phoff;
u64 shoff;
u32 flags;
u16 ehsize;
u16 phentsize;
u16 phnum;
u16 shentsize;
u16 shnum;
u16 shstrndx;
} elf64_ehdr;
typedef struct elf64_shdr {
u32 name;
u32 type;
u64 flags;
u64 addr;
u64 offset;
u64 size;
u32 link;
u32 info;
u64 addralign;
u64 entsize;
} elf64_shdr;
typedef struct elf64_sym {
u32 name;
u8 info;
u8 other;
u16 shndx;
u64 value;
u64 size;
} elf64_sym;
typedef struct elf64_rela {
u64 offset;
u64 info;
i64 addend;
} elf64_rela;
static int streq(const char *a, const char *b) {
usize i = 0;
while (a[i] && b[i]) {
if (a[i] != b[i]) {
return 0;
}
i++;
}
return a[i] == b[i];
}
int dkm_check_elf64(const void *address, u64 size) {
if (!address || size < sizeof(elf64_ehdr)) {
return -1;
}
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
if (ehdr->ident[0] != ELF_MAGIC0 || ehdr->ident[1] != ELF_MAGIC1 || ehdr->ident[2] != ELF_MAGIC2 || ehdr->ident[3] != ELF_MAGIC3) {
return -2;
}
if (ehdr->ident[4] != ELFCLASS64 || ehdr->ident[5] != ELFDATA2LSB) {
return -3;
}
if (ehdr->machine != EM_X86_64) {
return -4;
}
if (ehdr->type != ET_REL && ehdr->type != ET_DYN && ehdr->type != ET_EXEC) {
return -5;
}
if (ehdr->ehsize < sizeof(elf64_ehdr)) {
return -6;
}
return 0;
}
static int range_ok(u64 offset, u64 length, u64 size) {
if (offset > size) {
return 0;
}
if (length > size - offset) {
return 0;
}
return 1;
}
int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out) {
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
out->driver_desc = 0;
out->driver_init = 0;
out->driver_exit = 0;
if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) {
log_error("[DKM] ELF has no section table");
panic_full("DKM-E30 ELF NO SECTION TABLE",
"dkm_scan_symbols: driver ELF has no section table", 0);
return -1;
}
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
log_error("[DKM] ELF section table out of range");
panic_full("DKM-E31 ELF SECTION TABLE OUT OF RANGE",
"dkm_scan_symbols: section table out of file range", 0);
return -1;
}
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
const elf64_shdr *symtab = 0;
const elf64_shdr *strtab = 0;
for (u16 i = 0; i < ehdr->shnum; i++) {
const elf64_shdr *section = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
if (section->type == SHT_SYMTAB) {
symtab = section;
if (section->link < ehdr->shnum) {
strtab = (const elf64_shdr *)((const u8 *)sections + (u64)section->link * ehdr->shentsize);
}
break;
}
}
if (!symtab || !strtab || strtab->type != SHT_STRTAB) {
log_error("[DKM] missing symtab/strtab");
panic_full("DKM-E32 ELF MISSING SYMTAB",
"dkm_scan_symbols: symtab/strtab missing or invalid", 0);
return -1;
}
if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) {
log_error("[DKM] invalid symtab/strtab range");
panic_full("DKM-E33 ELF SYMTAB RANGE INVALID",
"dkm_scan_symbols: symtab/strtab out of file range", 0);
return -1;
}
log_info("[DKM] symtab found");
log_hex64("[DKM] symbol count=", symtab->size / symtab->entsize);
for (u64 off = 0; off + sizeof(elf64_sym) <= symtab->size; off += symtab->entsize) {
const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + symtab->offset + off);
if (sym->name >= strtab->size) {
continue;
}
const char *name = (const char *)address + strtab->offset + sym->name;
if (streq(name, "driver_desc")) {
out->driver_desc = sym;
log_info("[DKM] found driver_desc");
log_hex64("[DKM] driver_desc section=", sym->shndx);
log_hex64("[DKM] driver_desc value=", sym->value);
} else if (streq(name, "driver_init")) {
out->driver_init = sym;
log_info("[DKM] found driver_init");
log_hex64("[DKM] driver_init section=", sym->shndx);
log_hex64("[DKM] driver_init value=", sym->value);
} else if (streq(name, "driver_exit")) {
out->driver_exit = sym;
log_info("[DKM] found driver_exit");
log_hex64("[DKM] driver_exit section=", sym->shndx);
log_hex64("[DKM] driver_exit value=", sym->value);
}
}
if (out->driver_desc && out->driver_init && out->driver_exit) {
log_info("[DKM] required driver symbols ok");
return 0;
}
log_error("[DKM] required driver symbols missing");
panic_full("DKM-E34 REQUIRED DRIVER SYMBOLS MISSING",
"dkm_scan_symbols: driver_desc/init/exit symbol missing", 0);
return -1;
}
int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols) {
const elf64_ehdr *ehdr = (const elf64_ehdr *)address;
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
u64 image_offset[64];
/* Zero the entire array so non-ALLOC section indices yield offset 0
* instead of stack garbage. */
for (int zi = 0; zi < 64; zi++) image_offset[zi] = 0;
u64 total_size = 0;
u64 max_align = 16;
u16 alloc_count = 0;
for (u16 i = 1; i < ehdr->shnum && alloc_count < 64; i++) {
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
if (!(sh->flags & SHF_ALLOC)) {
continue;
}
if (sh->addralign > max_align) {
max_align = sh->addralign;
}
total_size = utsm_align_up_u64(total_size, sh->addralign);
image_offset[i] = total_size;
total_size += sh->size;
alloc_count++;
}
if (total_size == 0) {
log_error("[DKM] no allocatable sections");
panic_full("DKM-E35 ELF NO ALLOC SECTIONS",
"dkm_load_elf_rel: no allocatable sections", 0);
return -1;
}
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
if (!image) {
log_error("[DKM] failed to allocate module memory");
panic_full("DKM-E36 MODULE MEM ALLOC FAILED",
"dkm_load_elf_rel: module memory allocation failed", 0);
return -1;
}
memset(image, 0, total_size);
for (u16 i = 1; i < ehdr->shnum; i++) {
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
if (!(sh->flags & SHF_ALLOC)) {
continue;
}
if (sh->type == SHT_PROGBITS && sh->size > 0 && range_ok(sh->offset, sh->size, size)) {
memcpy(image + image_offset[i], (const u8 *)address + sh->offset, sh->size);
}
}
for (u16 i = 1; i < ehdr->shnum; i++) {
const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize);
if (sh->type != SHT_RELA) {
continue;
}
if (sh->entsize < sizeof(elf64_rela) || !range_ok(sh->offset, sh->size, size)) {
continue;
}
if (sh->info >= ehdr->shnum) {
continue;
}
const elf64_shdr *target = (const elf64_shdr *)((const u8 *)sections + (u64)sh->info * ehdr->shentsize);
if (!(target->flags & SHF_ALLOC)) {
continue;
}
u8 *target_base = image + image_offset[sh->info];
const elf64_shdr *rela_symtab = 0;
if (sh->link < ehdr->shnum) {
rela_symtab = (const elf64_shdr *)((const u8 *)sections + (u64)sh->link * ehdr->shentsize);
}
const elf64_shdr *rela_strtab = 0;
if (rela_symtab && rela_symtab->link < ehdr->shnum) {
rela_strtab = (const elf64_shdr *)((const u8 *)sections + (u64)rela_symtab->link * ehdr->shentsize);
}
(void)rela_strtab;
for (u64 off = 0; off + sizeof(elf64_rela) <= sh->size; off += sh->entsize) {
const elf64_rela *rela = (const elf64_rela *)((const u8 *)address + sh->offset + off);
u32 sym_idx = (u32)(rela->info >> 32);
u32 reloc_type = (u32)(rela->info & 0xffffffff);
u64 sym_value = 0;
u16 sym_shndx = 0;
if (rela_symtab && rela_strtab &&
rela_symtab->entsize >= sizeof(elf64_sym) &&
range_ok(rela_symtab->offset, rela_symtab->size, size)) {
u64 sym_entry_off = (u64)sym_idx * rela_symtab->entsize;
if (sym_entry_off + sizeof(elf64_sym) <= rela_symtab->size) {
const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + rela_symtab->offset + sym_entry_off);
sym_value = sym->value;
sym_shndx = sym->shndx;
}
}
u64 S = sym_value;
if (sym_shndx > 0 && sym_shndx < 64) {
S = (u64)image + image_offset[sym_shndx] + sym_value;
}
u64 P = (u64)image + image_offset[sh->info] + rela->offset;
u64 A = rela->addend;
u64 patch = 0;
if (reloc_type == R_X86_64_64) {
patch = S + A;
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
patch = (u64)((i64)(i32)(S + A));
} else if (reloc_type == R_X86_64_PC32 || reloc_type == R_X86_64_PLT32) {
patch = (u64)((i64)(i32)(S + A - P));
} else {
log_error("[DKM] unsupported relocation type");
log_hex64("[DKM] reloc type=", (u64)reloc_type);
panic_full("DKM-E37 UNSUPPORTED RELOCATION",
"dkm_load_elf_rel: unsupported relocation type", 0);
return -1;
}
/* Bounds check: use the actual write width, not always 8. */
u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4;
if (rela->offset + reloc_width <= target->size) {
if (reloc_type != R_X86_64_64) {
*(u32 *)(target_base + rela->offset) = (u32)patch;
} else {
*(u64 *)(target_base + rela->offset) = patch;
}
}
}
}
const elf64_sym *desc_sym = (const elf64_sym *)symbols->driver_desc;
const elf64_sym *init_sym = (const elf64_sym *)symbols->driver_init;
const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value);
if (desc->magic != DKM_DRIVER_MAGIC) {
log_error("[DKM] external driver_desc magic mismatch");
panic_full("DKM-E38 DRIVER DESC MAGIC MISMATCH",
"dkm_load_elf_rel: external driver_desc magic mismatch", 0);
return -1;
}
log_info("[DKM] external driver loaded");
log_hex64("[DKM] desc ptr=", (u64)desc);
log_hex64("[DKM] name ptr=", (u64)desc->name);
log_info(desc->name);
dkm_driver_init_fn init = (dkm_driver_init_fn)(image + image_offset[init_sym->shndx] + init_sym->value);
dkm_driver_handle handle;
handle.desc = desc;
handle.state = DKM_STATE_INITING;
handle.init_status = 0;
handle.load_stage = desc->stage;
handle.flags = desc->flags;
dkm_log_set_driver(desc->name); /* D4: 驱动自己的颜色 */
dkm_bus_set_current_provider(&handle); /* D6: 总线 provider 上下文 */
int result = init(dkm_get_kernel_api(), &handle);
dkm_bus_set_current_provider(0);
dkm_log_set_driver(0);
handle.init_status = result;
if (result != 0) {
handle.state = DKM_STATE_FAILED;
log_error("[DKM] external driver init failed");
panic_full("DKM-E39 EXTERNAL DRIVER INIT FAILED",
"dkm_load_elf_rel: external driver_init returned non-zero", 0);
return result;
}
handle.state = DKM_STATE_ACTIVE;
log_info("[DKM] external driver active");
return 0;
}
void dkm_scan_boot_modules(void) {
struct limine_module_response *response = g_module_request.response;
if (!response) {
log_warn("[DKM] no Limine module response");
panic_full("DKM-E40 NO LIMINE MODULE RESPONSE",
"dkm_scan_boot_modules: no Limine module response", 0);
return;
}
log_info("[DKM] boot modules scan begin");
log_hex64("[DKM] boot module count=", response->module_count);
for (u64 i = 0; i < response->module_count; i++) {
struct limine_file *file = response->modules[i];
if (!file) {
log_warn("[DKM] null module entry");
panic_full("DKM-E41 NULL MODULE ENTRY",
"dkm_scan_boot_modules: null boot module entry", 0);
continue;
}
log_info("[DKM] module path");
if (file->path) {
log_info(file->path);
}
if (file->cmdline) {
log_info(file->cmdline);
}
log_hex64("[DKM] module size=", file->size);
int status = dkm_check_elf64(file->address, file->size);
if (status == 0) {
log_info("[DKM] module ELF64 x86_64 ok");
struct dkm_symbol_scan symbols;
if (dkm_scan_symbols(file->address, file->size, &symbols) == 0) {
log_info("[DKM] loading external ELF driver");
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
if (load_result != 0) {
log_error("[DKM] external driver load failed");
panic_full("DKM-E42 EXTERNAL DRIVER LOAD FAILED",
"dkm_scan_boot_modules: external driver load failed", 0);
}
}
} else {
log_error("[DKM] module ELF64 rejected");
log_hex64("[DKM] elf status=", (u64)(i64)status);
panic_full("DKM-E43 MODULE ELF64 REJECTED",
"dkm_scan_boot_modules: boot module ELF64 check rejected", 0);
}
}
log_info("[DKM] boot modules scan end");
}
-318
View File
@@ -1,318 +0,0 @@
/* ===================================================================
* D6 万物互联:dkm_bus 双向服务总线
*
* "没有主内核,每个模块都是内核——总线就是万物的神经。"
*
* kapi(kernel_api.c)是内核→模块的单向只读导出表;本文件把它双向化:
* 任何 .drv 模块在 driver_init 里注册 srv.* 服务,任何模块按名消费。
* kern.* 仅内核可注册(provider==NULL 时),srv.* 仅模块可注册。
*
* 失败策略(铁律映射):
* register 名字非法/身份不符 → BUS-E01 panic(编程错误)
* register 重名 → BUS-E02 panic
* register 表满 → BUS-E03 panic
* lookup 未命中 → BUS-E04 panic(硬依赖未满足=编程错误)
* unregister 违规 → BUS-E05 panic
* probe 未命中 → 返回 -1(能力缺席=设计内双路径,白名单)
*
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/idt.h>
#include <utsm/mm.h>
#include <utsm/dma.h>
#include <utsm/drr.h>
#include <utsm/utrw.h>
#include <utsm/block.h>
#include <utsm/pe.h>
#include <utsm/linux_compat.h>
#include "../kernel/ini_parser.h"
/* manifest.c 提供;签名见 dkm.h 头注释(避免头依赖) */
void dsm_load_named(const ini_config *cfg, const char *name);
/* kernel/main.c 提供:FUCK 启动配置只读访问(g_boot_cfg) */
const ini_config *utsm_boot_cfg_get(void);
static dkm_bus_service g_services[DKM_BUS_MAX_SERVICES];
static u32 g_service_count;
static struct dkm_driver_handle *g_current_provider; /* NULL = 内核 */
/* ---- 名字工具 ---- */
static int bus_streq(const char *a, const char *b) {
usize i = 0;
while (a[i] && b[i]) {
if (a[i] != b[i]) return 0;
i++;
}
return a[i] == b[i];
}
static int bus_prefix_of(const char *s, const char *prefix) {
usize i = 0;
while (prefix[i]) {
if (s[i] != prefix[i]) return 0;
i++;
}
return 1;
}
static int bus_find(const char *name) {
for (u32 i = 0; i < g_service_count; i++) {
if (bus_streq(g_services[i].name, name)) return (int)i;
}
return -1;
}
/* ---- 总线原语(BUS-E 全部 panic,零降级) ---- */
static int bus_register_impl(const char *name, u32 version, void *fn) {
if (!name || !fn || !name[0]) {
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: null/empty service name or fn", 0);
return -1;
}
usize len = 0;
while (name[len]) {
if (len >= DKM_BUS_MAX_NAME) {
log_error("[BUS] name too long: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: service name exceeds DKM_BUS_MAX_NAME", 0);
return -1;
}
len++;
}
int is_kern = bus_prefix_of(name, "kern.");
int is_srv = bus_prefix_of(name, "srv.");
if (!is_kern && !is_srv) {
log_error("[BUS] name without namespace prefix: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: service name must start with kern. or srv.", 0);
return -1;
}
if (is_kern && g_current_provider != 0) {
log_error("[BUS] module tried to register kern.*: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: only the kernel may register kern.* services", 0);
return -1;
}
if (is_srv && g_current_provider == 0) {
log_error("[BUS] kernel tried to register srv.*: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: kernel services must use the kern.* namespace", 0);
return -1;
}
if (bus_find(name) >= 0) {
log_error("[BUS] duplicate service: ");
log_info(name);
panic_full("BUS-E02 DUPLICATE SERVICE",
"dkm_bus: service name already registered", 0);
return -1;
}
if (g_service_count >= DKM_BUS_MAX_SERVICES) {
panic_full("BUS-E03 SERVICE TABLE FULL",
"dkm_bus: g_services overflow (DKM_BUS_MAX_SERVICES)", 0);
return -1;
}
dkm_bus_service *s = &g_services[g_service_count];
for (usize i = 0; i < len; i++) s->name[i] = name[i];
s->name[len] = 0;
s->version = version;
s->fn = fn;
/* 存 desc 而非 handle:loader 的 handle 是栈局部(init 返回即悬垂),
* desc 在驱动镜像内存里持久;注销校验按 desc 指针判身份。 */
s->provider = g_current_provider ? g_current_provider->desc : 0;
if (is_kern) {
s->flags = DKM_BUS_F_KERNEL;
} else if (g_current_provider && (g_current_provider->flags & DKM_F_NO_UNLOAD)) {
s->flags = DKM_BUS_F_NO_UNLOAD;
} else {
s->flags = 0;
}
g_service_count++;
log_info("[BUS] service registered: ");
log_info(name);
return 0;
}
static int bus_unregister_impl(const char *name) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) {
log_error("[BUS] unregister unknown service: ");
log_info(name ? name : "(null)");
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: unregister of unknown service", 0);
return -1;
}
dkm_bus_service *s = &g_services[idx];
if (s->flags & (DKM_BUS_F_KERNEL | DKM_BUS_F_NO_UNLOAD)) {
log_error("[BUS] unregister of permanent service: ");
log_info(name);
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: F_KERNEL/F_NO_UNLOAD service cannot be unregistered", 0);
return -1;
}
if (s->provider != (g_current_provider ? g_current_provider->desc : 0)) {
log_error("[BUS] unregister by non-provider: ");
log_info(name);
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: only the provider may unregister its service", 0);
return -1;
}
/* 紧缩(顺序无关紧要,表小) */
for (u32 i = (u32)idx; i + 1 < g_service_count; i++) {
g_services[i] = g_services[i + 1];
}
g_service_count--;
log_info("[BUS] service unregistered: ");
log_info(name);
return 0;
}
static int bus_lookup_impl(const char *name, void **out_fn, u32 *out_version) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) {
log_error("[BUS] lookup miss: ");
log_info(name ? name : "(null)");
panic_full("BUS-E04 LOOKUP MISS",
"dkm_bus: required service not registered (hard dependency)", 0);
return -1;
}
if (out_fn) *out_fn = g_services[idx].fn;
if (out_version) *out_version = g_services[idx].version;
return 0;
}
static int bus_probe_impl(const char *name, void **out_fn, u32 *out_version) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) return -1;
if (out_fn) *out_fn = g_services[idx].fn;
if (out_version) *out_version = g_services[idx].version;
return 0;
}
static u32 bus_count_impl(void) {
return g_service_count;
}
static const dkm_bus_service *bus_at_impl(u32 index) {
if (index >= g_service_count) return 0;
return &g_services[index];
}
static const dkm_bus_api g_bus_api = {
.magic = DKM_BUS_MAGIC,
.version = DKM_BUS_VERSION,
.register_service = bus_register_impl,
.unregister_service = bus_unregister_impl,
.lookup = bus_lookup_impl,
.probe = bus_probe_impl,
.count = bus_count_impl,
.at = bus_at_impl,
};
const dkm_bus_api *dkm_bus_get_api(void) {
return &g_bus_api;
}
void dkm_bus_set_current_provider(struct dkm_driver_handle *h) {
g_current_provider = h;
}
int dkm_bus_prefix_registered(const char *prefix) {
if (!prefix) return 0;
usize len = 0;
while (prefix[len]) len++;
for (u32 i = 0; i < g_service_count; i++) {
const char *n = g_services[i].name;
if (bus_streq(n, prefix)) return 1;
/* 前缀命中:n == "prefix.xxx" */
if (bus_prefix_of(n, prefix) && n[len] == '.') return 1;
}
return 0;
}
/* ===================================================================
* kern.* 播种
*
* kapi 14 条(log.* dma/irq/mm/apic/drr/utrw)以 kern. 前缀重新播种
* (旧无前缀 kapi 名冻结不动,兼容层);另加 block/pe/lxc/probe/config/
* load_by_name。dsk_boot_context.reserved[] 的逐槽替代服务(srv.*)随
* P2-P4 各模块落地注册,本表不抢跑。
* kern.probe.info 随 P4b(shell 消费方)落地;kern.bootlog.milestone
* 随 P2(fat32.drv 实装)落地。
* =================================================================== */
static void bus_kern_panic(const char *msg) {
panic_full("DKM-PANIC", msg, 0);
}
static int bus_kern_load_by_name(const char *name) {
/* P3 接线后带 FUCK cfg 过滤;当前调用方(内部)走 manifest 语义。 */
dsm_load_named(0, name);
return 0;
}
static const char *bus_kern_config_get(const char *section, const char *key) {
const ini_config *cfg = utsm_boot_cfg_get();
if (!cfg) return 0;
return ini_get(cfg, section, key);
}
static int bus_seed(const char *name, void *fn) {
return bus_register_impl(name, 1u, fn);
}
void dkm_bus_init(void) {
static int s_inited = 0;
if (s_inited) return;
s_inited = 1;
g_service_count = 0;
g_current_provider = 0;
/* log(kapi 4 条迁移) */
bus_seed("kern.log.info", (void *)log_info);
bus_seed("kern.log.warn", (void *)log_warn);
bus_seed("kern.log.error", (void *)log_error);
bus_seed("kern.log.panic", (void *)bus_kern_panic);
/* dma / irq / mm / apic(kapi 迁移 + P6 真 IRQ 输入新增 unmask/mask) */
bus_seed("kern.dma.alloc_pages", (void *)dma_alloc_pages);
bus_seed("kern.irq.register", (void *)irq_register);
bus_seed("kern.irq.vector_alloc", (void *)irq_vector_alloc);
bus_seed("kern.irq.vector_free", (void *)irq_vector_free);
bus_seed("kern.irq.unmask", (void *)pic_irq_unmask);
bus_seed("kern.irq.mask", (void *)pic_irq_mask);
bus_seed("kern.mm.map_mmio", (void *)mm_map_mmio);
bus_seed("kern.mm.unmap_mmio", (void *)mm_unmap_mmio);
bus_seed("kern.apic.register_eoi", (void *)idt_register_apic_eoi);
/* drr / utrw(kapi 迁移) */
bus_seed("kern.drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty);
bus_seed("kern.drr.rollback_pages", (void *)drr_rollback_pages);
bus_seed("kern.utrw.debug_dump", (void *)utrw_debug_dump_page);
/* block registry 前端(替代 desktop 的 api+0xA8 裸偏移读) */
{
const dkm_block_api *b = block_get_api();
bus_seed("kern.block.read", (void *)b->read);
bus_seed("kern.block.write", (void *)b->write);
}
/* PE / Linux 兼容层服务(替代 reserved[4]/reserved[5]) */
bus_seed("kern.pe.service", (void *)pe_get_service);
bus_seed("kern.lxc.service", (void *)linux_compat_get_service);
/* 按名加载(session.drv 的排序拉手,P3 接 FUCK 过滤) */
bus_seed("kern.dkm.load_by_name", (void *)bus_kern_load_by_name);
/* FUCK 配置只读访问(替代 reserved[] 内散落的配置回传) */
bus_seed("kern.config.get", (void *)bus_kern_config_get);
log_info("[BUS] ready services=");
log_hex64("[BUS] count=", g_service_count);
}
-47
View File
@@ -1,47 +0,0 @@
#include <utsm/dkm.h>
static const char *const console_early_provides[] = {
"console",
"early-log"
};
static const dkm_driver_desc console_early_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(dkm_driver_desc),
.name = "console_early",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = DKM_CLASS_CONSOLE,
.stage = 0,
.flags = DKM_F_REQUIRED | DKM_F_BOOT_MODULE | DKM_F_NO_UNLOAD | DKM_F_EARLY_LOG,
.priority = 0,
.depends = 0,
.depends_count = 0,
.provides = console_early_provides,
.provides_count = 2,
.min_kernel_abi = DKM_KERNEL_API_VERSION,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
static int console_early_init(const dkm_kernel_api *api, dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log || !api->log->info) {
return -1;
}
api->log->info("[DKM:console_early] serial console online");
return 0;
}
static int console_early_exit(dkm_driver_handle *handle) {
(void)handle;
return 0;
}
const dkm_builtin_driver dkm_builtin_console_early = {
.desc = &console_early_desc,
.init = console_early_init,
.exit = console_early_exit
};
-78
View File
@@ -1,78 +0,0 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
static int dkm_validate_desc(const dkm_driver_desc *desc) {
if (!desc) {
return -1;
}
if (desc->magic != DKM_DRIVER_MAGIC) {
return -2;
}
if (desc->abi_version != DKM_ABI_VERSION) {
return -3;
}
if (desc->desc_size < sizeof(dkm_driver_desc)) {
return -4;
}
if (!desc->name || !desc->version) {
return -5;
}
return 0;
}
void dkm_init(void) {
log_info("[DKM] init begin");
int status = dkm_load_builtin(&dkm_builtin_console_early);
if (status == 0) {
log_info("[DKM] init ok");
} else {
log_error("[DKM] init failed");
panic_full("DKM-E50 BUILTIN INIT FAILED",
"dkm_init: builtin console_early load failed", 0);
}
}
int dkm_load_builtin(const dkm_builtin_driver *driver) {
if (!driver || !driver->desc || !driver->init) {
log_error("[DKM] invalid builtin driver");
panic_full("DKM-E51 INVALID BUILTIN DRIVER",
"dkm_load_builtin: builtin driver descriptor invalid", 0);
return -1;
}
int status = dkm_validate_desc(driver->desc);
if (status != 0) {
log_error("[DKM] driver_desc rejected");
panic_full("DKM-E52 DRIVER DESC REJECTED",
"dkm_load_builtin: builtin driver_desc validation failed", 0);
return status;
}
dkm_driver_handle handle;
handle.desc = driver->desc;
handle.state = DKM_STATE_ABI_CHECKED;
handle.init_status = 0;
handle.load_stage = driver->desc->stage;
handle.flags = driver->desc->flags;
log_info("[DKM] builtin driver init begin");
log_info(driver->desc->name);
dkm_log_set_driver(driver->desc->name); /* D4: 驱动自己的颜色 */
handle.state = DKM_STATE_INITING;
status = driver->init(dkm_get_kernel_api(), &handle);
dkm_log_set_driver(0);
handle.init_status = status;
if (status != 0) {
handle.state = DKM_STATE_FAILED;
log_error("[DKM] builtin driver init failed");
panic_full("DKM-E53 BUILTIN DRIVER INIT FAILED",
"dkm_load_builtin: builtin driver_init returned non-zero", 0);
return status;
}
handle.state = DKM_STATE_ACTIVE;
log_info("[DKM] builtin driver active");
return 0;
}
-213
View File
@@ -1,213 +0,0 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/idt.h>
#include <utsm/net.h>
#include <utsm/mm.h>
#include <utsm/dma.h>
#include <utsm/drr.h>
#include <utsm/utrw.h>
#include <utsm/panic.h>
static void dkm_api_info(const char *msg) {
log_info(msg);
}
static void dkm_api_warn(const char *msg) {
log_warn(msg);
}
static void dkm_api_error(const char *msg) {
log_error(msg);
}
static void dkm_api_panic(const char *msg) {
/* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲)。
* fallback 模式下返回驱动,由驱动调用点走其备用路径。 */
panic_full("DKM-PANIC", msg, 0);
}
static const dkm_log_api g_log_api = {
.info = dkm_api_info,
.warn = dkm_api_warn,
.error = dkm_api_error,
.panic = dkm_api_panic
};
/* ---- D4: DKM 彩色日志——每个驱动有自己的脾气 ----
* 加载器在 driver_init 前调用 dkm_log_set_driver(desc->name):
* 驱动名 FNV-1a hash → 8 色调色板(明亮色系,串口可读),
* 同名驱动颜色确定性可复现。init 返回后由调用方清上下文。 */
void dkm_log_set_driver(const char *name) {
static const char *palette[8] = {
"\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[35m",
"\x1b[96m", "\x1b[92m", "\x1b[93m", "\x1b[95m"
};
if (!name || !*name) {
log_set_color(0);
return;
}
u32 h = 2166136261u;
while (*name) {
h ^= (u8)*name++;
h *= 16777619u;
}
log_set_color(palette[h & 7]);
}
static dkm_kernel_api g_kernel_api = {
.version = DKM_KERNEL_API_VERSION,
.size = sizeof(dkm_kernel_api),
.feature_bits = 0,
.log = &g_log_api,
.mem = 0,
.utsm = 0,
.irq = 0,
.pci = 0,
.dma = 0,
.vfs = 0,
.net = 0,
.timer = 0,
.drr = 0,
.rsdp_address = 0,
.fb_address = 0,
.fb_width = 0,
.fb_height = 0,
.fb_pitch = 0,
.fb_bpp = 0,
.boot_modules_response = 0,
.irq_register = 0,
.hhdm_offset = 0,
.block = 0,
.mmio = 0,
.mm_map_mmio = 0,
.mm_unmap_mmio = 0,
.register_apic_eoi = 0,
.sched = 0,
.kapi = 0,
.bus = 0
};
const dkm_kernel_api *dkm_get_kernel_api(void) {
return &g_kernel_api;
}
/* ===================================================================
* D3: 按名导出表(kapi)——自由也要有地图
* =================================================================== */
static const dkm_kapi_entry g_kapi_entries[] = {
{ "log.info", (void *)dkm_api_info },
{ "log.warn", (void *)dkm_api_warn },
{ "log.error", (void *)dkm_api_error },
{ "log.panic", (void *)dkm_api_panic },
{ "dma.alloc_pages", (void *)dma_alloc_pages },
{ "irq.register", (void *)irq_register },
{ "irq.vector_alloc", (void *)irq_vector_alloc },
{ "irq.vector_free", (void *)irq_vector_free },
{ "mm.map_mmio", (void *)mm_map_mmio },
{ "mm.unmap_mmio", (void *)mm_unmap_mmio },
{ "apic.register_eoi", (void *)idt_register_apic_eoi },
{ "drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty },
{ "drr.rollback_pages", (void *)drr_rollback_pages },
{ "utrw.debug_dump", (void *)utrw_debug_dump_page },
};
static int kapi_lookup(const char *name, void **out_fn) {
if (!name) return -1;
for (u32 i = 0; i < sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]); i++) {
const dkm_kapi_entry *e = &g_kapi_entries[i];
const char *a = e->name, *b = name;
while (*a && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
if (out_fn) *out_fn = e->fn;
return 0;
}
}
return -1;
}
static const dkm_kapi_table g_kapi_table = {
.magic = DKM_KAPI_MAGIC,
.version = DKM_KAPI_VERSION,
.count = sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]),
.entries = g_kapi_entries,
.lookup = kapi_lookup,
};
#include "../arch/x86_64/limine.h"
extern volatile struct limine_rsdp_request g_rsdp_request;
extern volatile struct limine_framebuffer_request g_fb_request;
extern volatile struct limine_module_request g_module_request;
extern volatile struct limine_hhdm_request g_hhdm_request;
void dkm_fill_platform_info(void) {
/* D6 万物互联:总线先播种再挂载(必须先于一切驱动加载——
* driver_init 里即可 api->bus->register_service)。 */
dkm_bus_init();
g_kernel_api.bus = dkm_bus_get_api();
g_kernel_api.dma = dma_get_api();
g_kernel_api.net = net_get_api();
g_kernel_api.block = block_get_api();
g_kernel_api.mmio = paging_get_api();
/* M4/Phase7-8: 调度器与恢复根服务表(drr 为原占位字段,此处激活) */
g_kernel_api.sched = utsm_sched_get_api();
g_kernel_api.drr = drr_get_recovery_api();
if (g_rsdp_request.response && g_rsdp_request.response->address) {
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
}
if (g_fb_request.response && g_fb_request.response->framebuffer_count > 0) {
struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0];
g_kernel_api.fb_address = fb->address;
g_kernel_api.fb_width = fb->width;
g_kernel_api.fb_height = fb->height;
g_kernel_api.fb_pitch = fb->pitch;
g_kernel_api.fb_bpp = fb->bpp;
}
if (g_module_request.response) {
g_kernel_api.boot_modules_response = g_module_request.response;
}
g_kernel_api.irq_register = (int (*)(u8,void*))((u64)irq_register);
if (g_hhdm_request.response) {
g_kernel_api.hhdm_offset = g_hhdm_request.response->offset;
}
g_kernel_api.mm_map_mmio = mm_map_mmio;
g_kernel_api.mm_unmap_mmio = mm_unmap_mmio;
/* B7 阶段2: 暴露 LAPIC EOI 钩子注册入口(apic.drv LAPIC 接管时调用) */
g_kernel_api.register_apic_eoi = idt_register_apic_eoi;
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
g_kernel_api.irq_vector_free = irq_vector_free;
/* D3: 按名导出表挂入 kernel_api(尾部追加,ABI 兼容) */
g_kernel_api.kapi = &g_kapi_table;
}
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DKM 能力,建立代码骨架,
* 调用即莲花 panic(DKM-E 系列)。当前无调用方(骨架)。
* =================================================================== */
/* D1: DKM 驱动热卸载(引用计数)。driver_desc 已有 NO_UNLOAD 标志位,
* 但卸载路径本身(refcount + driver_exit + 段回收)未实现。 */
void dkm_unload_driver(u32 driver_index) {
(void)driver_index;
panic_full("DKM-E01 DRIVER HOT UNLOAD NOT IMPLEMENTED",
"refcount unload path: designed (D1), not implemented", 0);
}
/* D2: 裸机器码加载器(无 ELF 头,直接映射 .text blob 到可执行页跳转)。
* SAS-R0 单地址空间下合法;加载器未实现。 */
void *dkm_load_bare_code(const void *blob, u64 size) {
(void)blob; (void)size;
panic_full("DKM-E02 BARE CODE LOADER NOT IMPLEMENTED",
"headerless machine-code loader: designed (D2), not implemented", 0);
return 0;
}
/* 多核:SIPI 唤醒 AP(apic.drv 注释预留)。BSP-only 是当前既定边界,
* 一旦被调用即 panic 而非静默单核继续。 */
void dkm_sipi_wake_ap(u32 lapic_id, u64 entry_eip) {
(void)lapic_id; (void)entry_eip;
panic_full("DKM-E03 SIPI AP WAKE NOT IMPLEMENTED",
"AP startup framework: designed, not implemented (BSP-only)", 0);
}
-681
View File
@@ -1,681 +0,0 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/types.h>
#include "../arch/x86_64/limine.h"
#include "../kernel/ini_parser.h"
/* 前向声明:带 FUCK [drivers] 过滤的加载入口(定义见下文) */
void dsm_load_by_manifest_ex(const ini_config *cfg);
#define DSM_MAX_DRIVERS_PER_STAGE 8
#define DSM_MAX_STAGES 4
#define DSM_MAX_DEPENDS 4
#define DSM_MAX_PROVIDES 4
#define DSM_MAX_CAPS 128
typedef struct dsm_driver_entry {
char name[32];
char path[64];
u32 class_id;
u32 required;
char depends[DSM_MAX_DEPENDS][32];
u32 depend_count;
char provides[DSM_MAX_PROVIDES][32];
u32 provide_count;
struct limine_file *boot_module;
int loaded;
/* P0(万物互联):终态标志——已加载/被 FUCK 关闭/模块缺失/加载失败,
* 不再是本轮不动点迭代的候选。resolved 但 !loaded 的驱动不登记 provides。 */
int resolved;
} dsm_driver_entry;
typedef struct dsm_stage_entry {
u32 id;
char name[16];
u32 required;
dsm_driver_entry drivers[DSM_MAX_DRIVERS_PER_STAGE];
u32 driver_count;
} dsm_stage_entry;
static dsm_stage_entry g_stages[DSM_MAX_STAGES];
static u32 g_stage_count;
static int streq_static(const char *a, const char *b);
/* ===================================================================
* P0(万物互联):DSM 依赖图强制
*
* capability 集合 = 已成功加载驱动的 provides token 并集。
* 加载前校验 depends 全满足(miss → DKM-E44);
* 同 stage 内按依赖不动点迭代(环 → DKM-E47)。
* 性能不是目标(多趟扫描),正确性第一——设计定稿见
* docs/RE/万物互联与服务总线设计.md §1.5。
* =================================================================== */
static char g_caps[DSM_MAX_CAPS][32];
static u32 g_cap_count;
static int dsm_capability_satisfied(const char *token) {
for (u32 i = 0; i < g_cap_count; i++) {
if (streq_static(g_caps[i], token)) return 1;
}
return 0;
}
static void dsm_capability_add(const char *token) {
if (dsm_capability_satisfied(token)) return;
if (g_cap_count >= DSM_MAX_CAPS) {
panic_full("DKM-E48 CAPABILITY TABLE FULL",
"P0 dependency graph: g_caps overflow (DSM_MAX_CAPS)", 0);
return;
}
for (u32 i = 0; i + 1 < 32 && token[i]; i++) {
g_caps[g_cap_count][i] = token[i];
}
g_caps[g_cap_count][31] = 0;
g_cap_count++;
}
static void dsm_register_provides(const dsm_driver_entry *drv) {
for (u32 i = 0; i < drv->provide_count; i++) {
dsm_capability_add(drv->provides[i]);
}
}
/* P1(万物互联):stage>=4 的 provides 总线契约校验——driver_init 成功后,
* 每个 provides token T 必须已有 "srv.T" 或 "srv.T.*" 服务注册到总线,
* 否则 DKM-E45(零降级:provides 是对消费者/排序者的承诺,注册不到即违约)。
* stage0-3 老 14 驱动豁免:其 provides 由内核注册表兑现(ahci→block
* registry 等),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目。 */
static void dsm_verify_provides(const dsm_stage_entry *stage, const dsm_driver_entry *drv) {
if (stage->id < 4) return;
for (u32 i = 0; i < drv->provide_count; i++) {
char srv_name[40];
const char *token = drv->provides[i];
srv_name[0] = 's'; srv_name[1] = 'r'; srv_name[2] = 'v'; srv_name[3] = '.';
u32 p = 4;
while (token[p - 4] && p + 1 < sizeof(srv_name)) {
srv_name[p] = token[p - 4];
p++;
}
srv_name[p] = 0;
if (!dkm_bus_prefix_registered(srv_name)) {
log_error("[DSM] provides token not registered on bus: ");
log_info(drv->name);
log_info(token);
panic_full("DKM-E45 PROVIDES NOT REGISTERED",
"stage>=4 driver provides token has no matching srv.* bus service", 0);
}
}
}
/* depends 全满足返回 1;否则返回 0 并(可选)写出第一个未满足 token 的下标 */
static int dsm_deps_satisfied(const dsm_driver_entry *drv, u32 *missing_idx) {
for (u32 i = 0; i < drv->depend_count; i++) {
if (!dsm_capability_satisfied(drv->depends[i])) {
if (missing_idx) *missing_idx = i;
return 0;
}
}
return 1;
}
/* token 是否由本 stage 尚未 resolved 的候选提供(用于区分缺依赖 vs 成环) */
static int dsm_dep_pending_in_stage(const dsm_stage_entry *stage, const char *token) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->provide_count; i++) {
if (streq_static(drv->provides[i], token)) return 1;
}
}
return 0;
}
/* 不动点停滞:先判真缺依赖(DKM-E44),全在剩余候选里则判环(DKM-E47)。
* 零降级:两者都是编程/manifest 错误,莲花屏。 */
static void dsm_report_dependency_stall(const dsm_stage_entry *stage) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->depend_count; i++) {
const char *dep = drv->depends[i];
if (!dsm_capability_satisfied(dep) && !dsm_dep_pending_in_stage(stage, dep)) {
log_error("[DSM] dependency not satisfied: ");
log_info(drv->name);
log_info(dep);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"manifest depends token not provided by any loaded/pending driver", 0);
return;
}
}
}
panic_full("DKM-E47 DEPENDENCY CYCLE",
"manifest stage dependency graph has a cycle (no progress)", 0);
}
extern volatile struct limine_module_request g_module_request;
static int dsm_skip_ws(const char **p) {
while (**p == ' ' || **p == '\t' || **p == '\n' || **p == '\r') { (*p)++; }
return 0;
}
static int dsm_expect(const char **p, char c) {
dsm_skip_ws(p);
if (**p == c) { (*p)++; return 0; }
return -1;
}
static int dsm_read_string(const char **p, char *out, u32 max_len) {
dsm_skip_ws(p);
if (**p != '"') return -1;
(*p)++;
u32 i = 0;
while (**p && **p != '"' && i + 1 < max_len) {
out[i++] = **p;
(*p)++;
}
out[i] = 0;
if (**p != '"') return -1;
(*p)++;
return 0;
}
static int dsm_skip_value(const char **p) {
dsm_skip_ws(p);
if (**p == '"') {
char tmp[256];
return dsm_read_string(p, tmp, sizeof(tmp));
}
if (**p == '{') {
(*p)++;
int depth = 1;
while (depth > 0 && **p) {
if (**p == '{') depth++;
else if (**p == '}') depth--;
else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; }
if (depth > 0) (*p)++;
}
return 0;
}
if (**p == '[') {
(*p)++;
int depth = 1;
while (depth > 0 && **p) {
if (**p == '[') depth++;
else if (**p == ']') depth--;
else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; }
if (depth > 0) (*p)++;
}
return 0;
}
while (**p && **p != ',' && **p != '}' && **p != ']') (*p)++;
return 0;
}
static struct limine_file *dsm_find_boot_module(const char *path) {
struct limine_module_response *rsp = g_module_request.response;
if (!rsp) return 0;
for (u64 i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (f && f->path) {
const char *fp = f->path;
while (*fp == '/') fp++;
usize j = 0;
usize k = 0;
while (fp[j]) j++;
while (path[k]) k++;
if (j == k) {
int match = 1;
for (usize x = 0; x < j; x++) {
if (fp[x] != path[x]) { match = 0; break; }
}
if (match) return f;
}
}
}
return 0;
}
static int streq_static(const char *a, const char *b) {
usize i = 0;
while (a[i] && b[i]) {
if (a[i] != b[i]) return 0;
i++;
}
return a[i] == b[i];
}
static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) {
drv->depend_count = 0;
drv->provide_count = 0;
drv->required = 1;
drv->boot_module = 0;
drv->loaded = 0;
drv->resolved = 0;
drv->class_id = 0;
if (dsm_expect(p, '{') != 0) return;
while (**p && **p != '}') {
dsm_skip_ws(p);
if (**p == '}') break;
char key[32];
if (dsm_read_string(p, key, sizeof(key)) != 0) return;
dsm_expect(p, ':');
if (streq_static(key, "name")) {
dsm_read_string(p, drv->name, sizeof(drv->name));
} else if (streq_static(key, "path")) {
dsm_read_string(p, drv->path, sizeof(drv->path));
} else if (streq_static(key, "class")) {
char cls[32];
dsm_read_string(p, cls, sizeof(cls));
if (streq_static(cls, "platform")) drv->class_id = 1;
else if (streq_static(cls, "bus")) drv->class_id = 2;
else if (streq_static(cls, "interrupt")) drv->class_id = 3;
else if (streq_static(cls, "timer")) drv->class_id = 4;
else if (streq_static(cls, "console")) drv->class_id = 5;
else if (streq_static(cls, "storage")) drv->class_id = 6;
else if (streq_static(cls, "fs")) drv->class_id = 7;
else if (streq_static(cls, "fs-core")) drv->class_id = 8;
else if (streq_static(cls, "net")) drv->class_id = 9;
else if (streq_static(cls, "input")) drv->class_id = 10;
} else if (streq_static(key, "required")) {
dsm_skip_ws(p);
if (**p == 't') { drv->required = 1; (*p) += 4; }
else if (**p == 'f') { drv->required = 0; (*p) += 5; }
} else if (streq_static(key, "depends")) {
if (dsm_expect(p, '[') != 0) return;
while (**p && **p != ']') {
dsm_skip_ws(p);
if (**p == ']') break;
if (drv->depend_count < DSM_MAX_DEPENDS) {
dsm_read_string(p, drv->depends[drv->depend_count], 32);
drv->depend_count++;
} else { dsm_skip_value(p); }
dsm_skip_ws(p);
if (**p == ',') { (*p)++; continue; }
if (**p == ']') break;
}
dsm_expect(p, ']');
} else if (streq_static(key, "provides")) {
if (dsm_expect(p, '[') != 0) return;
while (**p && **p != ']') {
dsm_skip_ws(p);
if (**p == ']') break;
if (drv->provide_count < DSM_MAX_PROVIDES) {
dsm_read_string(p, drv->provides[drv->provide_count], 32);
drv->provide_count++;
} else { dsm_skip_value(p); }
dsm_skip_ws(p);
if (**p == ',') { (*p)++; continue; }
if (**p == ']') break;
}
dsm_expect(p, ']');
} else {
dsm_skip_value(p);
}
dsm_skip_ws(p);
if (**p == ',') (*p)++;
}
dsm_expect(p, '}');
}
static void dsm_parse_stage(const char **p, dsm_stage_entry *stage) {
stage->driver_count = 0;
if (dsm_expect(p, '{') != 0) return;
while (**p && **p != '}') {
dsm_skip_ws(p);
if (**p == '}') break;
char key[32];
if (dsm_read_string(p, key, sizeof(key)) != 0) return;
dsm_expect(p, ':');
if (streq_static(key, "id")) {
dsm_skip_ws(p);
u32 val = 0;
while (**p >= '0' && **p <= '9') { val = val * 10 + (u32)(**p - '0'); (*p)++; }
stage->id = val;
} else if (streq_static(key, "name")) {
dsm_read_string(p, stage->name, sizeof(stage->name));
} else if (streq_static(key, "required")) {
dsm_skip_ws(p);
if (**p == 't') { stage->required = 1; (*p) += 4; }
else if (**p == 'f') { stage->required = 0; (*p) += 5; }
} else if (streq_static(key, "drivers")) {
if (dsm_expect(p, '[') != 0) return;
while (**p && **p != ']') {
dsm_skip_ws(p);
if (**p == ']') break;
if (stage->driver_count < DSM_MAX_DRIVERS_PER_STAGE) {
dsm_parse_driver(p, &stage->drivers[stage->driver_count]);
stage->driver_count++;
} else {
dsm_skip_value(p);
}
dsm_skip_ws(p);
if (**p == ',') { (*p)++; continue; }
if (**p == ']') break;
}
dsm_expect(p, ']');
} else {
dsm_skip_value(p);
}
dsm_skip_ws(p);
if (**p == ',') (*p)++;
}
dsm_expect(p, '}');
}
static int dsm_parse_manifest(const char *text, u64 size) {
g_stage_count = 0;
const char *p = text;
const char *end = text + size;
while (p < end && *p) {
dsm_skip_ws(&p);
if (p >= end || !*p) break;
if (*p == '{' || *p == '}') { p++; continue; }
if (*p == ',' || *p == ':') { p++; continue; }
if (*p == '[' || *p == ']') { p++; continue; }
char key[32];
if (dsm_read_string(&p, key, sizeof(key)) != 0) break;
dsm_expect(&p, ':');
if (streq_static(key, "stages")) {
if (dsm_expect(&p, '[') != 0) {
/* 严格错误策略插桩:stage 解析失败零降级(DKM-E08) */
panic_full("DKM-E08 MANIFEST STAGES PARSE FAILED",
"dsm_parse_manifest: stages array malformed", 0);
return -1;
}
while (p < end && *p) {
dsm_skip_ws(&p);
if (*p == ']') { p++; break; }
if (g_stage_count < DSM_MAX_STAGES) {
dsm_parse_stage(&p, &g_stages[g_stage_count]);
g_stage_count++;
}
dsm_skip_ws(&p);
if (*p == ',') p++;
}
} else {
dsm_skip_value(&p);
}
dsm_skip_ws(&p);
if (*p == ',' || *p == '}' || *p == ']') p++;
}
return 0;
}
void dsm_load_by_manifest(void) {
dsm_load_by_manifest_ex(NULL);
}
static int dsm_ensure_manifest(void) {
struct limine_module_response *rsp;
struct limine_file *manifest_file;
u64 i;
if (g_stage_count) return 0;
rsp = g_module_request.response;
if (!rsp) {
log_warn("[DSM] no Limine module response");
/* 严格错误策略插桩:manifest 缺失零降级(DKM-E09) */
panic_full("DKM-E09 NO LIMINE MODULE RESPONSE",
"dsm_ensure_manifest: no Limine module response", 0);
return -1;
}
manifest_file = 0;
for (i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
manifest_file = f;
break;
}
}
if (!manifest_file) {
log_warn("[DSM] manifest not found in boot modules");
/* 严格错误策略插桩:manifest 缺失零降级(DKM-E10) */
panic_full("DKM-E10 MANIFEST NOT FOUND",
"dsm_ensure_manifest: dkm:manifest boot module missing", 0);
return -1;
}
log_info("[DSM] parsing manifest");
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
log_info("[DSM] manifest parsed");
log_hex64("[DSM] stages=", (u64)g_stage_count);
return 0;
}
static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max_stage_id) {
if (dsm_ensure_manifest() != 0) return;
for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si];
if (limit_stage && stage->id > max_stage_id) continue;
log_info("[DSM] stage begin");
log_info(stage->name);
log_hex64("[DSM] driver count=", stage->driver_count);
/* P0:同 stage 内按依赖不动点迭代——每趟加载 deps 已满足的候选,
* 直到全部 resolved;停滞即 DKM-E44/E47(见 dsm_report_dependency_stall)。
* 多趟扫描 O(n²·趟数),性能刻意不管,正确性第一。 */
u32 pending = 0;
for (u32 di = 0; di < stage->driver_count; di++) {
if (!stage->drivers[di].resolved) pending++;
}
while (pending > 0) {
u32 pending_before = pending;
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (drv->resolved) continue;
/* === FUCK [drivers] 驱动开关检查 ===
* 关闭即退出候选(provides 不登记,依赖它的模块将 DKM-E44)。 */
if (cfg) {
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
if (!enabled) {
log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name);
drv->resolved = 1;
pending--;
continue;
}
}
/* P0:depends 校验——暂不满足先跳过,等本 stage 后续候选登记
* provides 后下一趟重试;整体停滞时统一判罚(零降级)。 */
if (!dsm_deps_satisfied(drv, 0)) continue;
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_info("[DSM] module not found");
log_info(drv->name);
drv->resolved = 1;
pending--;
if (drv->required && stage->required) {
/* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */
panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING",
"required driver boot module not found in manifest stage", 0);
break;
}
continue;
}
f = drv->boot_module;
log_info("[DSM] loading driver");
log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
if (drv->required && stage->required) {
panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID",
"required driver boot module failed ELF64 check", 0);
}
drv->resolved = 1;
pending--;
continue;
}
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
if (drv->required && stage->required) {
panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED",
"required driver symbol scan failed", 0);
}
drv->resolved = 1;
pending--;
continue;
}
result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
if (drv->required && stage->required) {
/* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */
panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED",
"required driver relocation/load failed", 0);
}
drv->resolved = 1;
pending--;
} else {
drv->loaded = 1;
drv->resolved = 1;
pending--;
/* P0:加载成功才登记 provides(disabled/missing/failed 一律不登记) */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
}
}
if (pending > 0 && pending == pending_before) {
/* 一整趟零进展:真缺依赖(DKM-E44)或依赖环(DKM-E47) */
dsm_report_dependency_stall(stage);
/* fallback:停滞的候选全部放弃(不登记 provides),结束本 stage */
for (u32 di = 0; di < stage->driver_count; di++) {
if (stage->drivers[di].resolved) continue;
log_warn("[DSM] fallback: driver skipped (unresolved deps): ");
log_info(stage->drivers[di].name);
stage->drivers[di].resolved = 1;
}
pending = 0;
}
}
log_info("[DSM] stage end");
}
}
void dsm_load_by_manifest_upto(const ini_config *cfg, u32 max_stage_id) {
dsm_load_stage_range(cfg, 1, max_stage_id);
}
/* 按名加载公共实现。strict_deps=1:depends 校验强制(DKM-E44)——供
* kern.dkm.load_by_name(万物互联 P3)等常规路径使用;
* strict_deps=0:顺序豁免版——唯一调用方是 kernel/main.c VMM 路径的
* pci+ahci 预加载(必须先于 apic/stage0,否则 guest 陷入 IDTR=0
* VM-exit 风暴,见 main.c 注释)。设计内顺序豁免,登记于设计文档 §6。 */
static void dsm_load_named_impl(const ini_config *cfg, const char *name, int strict_deps) {
if (!name || dsm_ensure_manifest() != 0) {
/* 严格错误策略插桩:按名加载 manifest 缺失零降级(DKM-E11) */
panic_full("DKM-E11 NAMED LOAD MANIFEST MISSING",
"dsm_load_named: null name or manifest unavailable", 0);
return;
}
for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si];
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (!streq_static(drv->name, name)) continue;
if (drv->loaded) return;
if (cfg && !ini_get_bool(cfg, "drivers", drv->name, 1)) {
log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name);
return;
}
/* P0:按名加载同样强制依赖(此时无同 stage 重试机会,miss 即判罚) */
if (strict_deps && !dsm_deps_satisfied(drv, 0)) {
log_error("[DSM] named driver dependency not satisfied: ");
log_info(drv->name);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"dsm_load_named: depends token not provided by any loaded driver", 0);
return;
}
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_warn("[DSM] named module not found");
log_info(name);
/* 严格错误策略插桩:named 驱动缺模块零降级(DKM-E12) */
panic_full("DKM-E12 NAMED DRIVER MODULE MISSING",
"dsm_load_named: named driver boot module not found", 0);
return;
}
f = drv->boot_module;
log_info("[DSM] loading named driver");
log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
/* 严格错误策略插桩:named 驱动 ELF 校验失败零降级(DKM-E13) */
panic_full("DKM-E13 NAMED DRIVER ELF INVALID",
"dsm_load_named: named driver failed ELF64 check", 0);
return;
}
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
/* 严格错误策略插桩:named 驱动符号扫描失败零降级(DKM-E14) */
panic_full("DKM-E14 NAMED DRIVER SYMBOL SCAN FAILED",
"dsm_load_named: named driver symbol scan failed", 0);
return;
}
result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
/* 严格错误策略插桩:named 驱动加载失败零降级(DKM-E15) */
panic_full("DKM-E15 NAMED DRIVER LOAD FAILED",
"dsm_load_named: named driver relocation/load failed", 0);
return;
}
drv->loaded = 1;
drv->resolved = 1;
/* P0:按名加载成功同样登记 provides */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
return;
}
}
log_warn("[DSM] named driver not in manifest");
log_info(name);
/* 严格错误策略插桩:named 驱动不在 manifest 零降级(DKM-E16) */
panic_full("DKM-E16 NAMED DRIVER NOT IN MANIFEST",
"dsm_load_named: named driver not found in manifest", 0);
}
void dsm_load_named(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 1);
}
void dsm_load_named_boot(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 0);
}
void dsm_load_by_manifest_ex(const ini_config *cfg) {
dsm_load_stage_range(cfg, 0, 0);
}
-837
View File
@@ -1,837 +0,0 @@
/* drr_core.c — DRR Dedicated Recovery Root 核心实现
*
* 替换原 drr_stub.c,提供完整的:
* - Emergency Pool(独立于普通堆分配器)
* - Recovery Log(write intent/commit 追踪)
* - Checkpoint A/B 双槽(CRC 校验、原子 active slot 切换)
* - Root key 管理
* - Heartbeat
*/
#include <utsm/drr.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/dma.h>
#include <utsm/segment.h>
#include <utsm/config.h>
#include <utsm/dkm.h>
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
extern void outb(u16 port, u8 value);
/* ---- 内部内存操作(不依赖 libc) ---- */
static void drr_memzero(void *dst, u64 n) {
u8 *d = (u8 *)dst;
while (n--) *d++ = 0;
}
static void drr_memcpy(void *dst, const void *src, u64 n) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
while (n--) *d++ = *s++;
}
/* ---- CRC64-ECMA ---- */
static const u64 crc64_table[256] = {
0x0000000000000000ULL, 0x42F0E1EBA5EA001AULL, 0x85E1C3D753D46D34ULL, 0xC711223CF63E6D2EULL,
0x493366450E42ECDFULL, 0x0BC387AEABA8ECC5ULL, 0xCCD2A5925D9681EBULL, 0x8E224479F87C81F1ULL,
0x9266CC8A1C85D9BEULL, 0xD0962D61B96FD9A4ULL, 0x17870F5D4F51B48AULL, 0x5577EEB6EABBB490ULL,
0xDB55AACF12C73561ULL, 0x99A54B24B72D357BULL, 0x5EB4691841135855ULL, 0x1C4488F3E4F9584FULL,
0x2644C6D0383BF54AULL, 0x64B4273B9DD1F550ULL, 0xA3A505076BEF987EULL, 0xE155E4ECCE059864ULL,
0x6F77A09536791995ULL, 0x2D87417E9393198FULL, 0xEA96634265AD74A1ULL, 0xA86682A9C04774BBULL,
0xB4220A5A24BE2CF4ULL, 0xF6D2EBB181542CEEULL, 0x31C3C98D776A41C0ULL, 0x73332866D28041DAULL,
0xFD116C1F2AFCC02BULL, 0xBFE18DF48F16C031ULL, 0x78F0AFC87928AD1FULL, 0x3A004E23DCC2AD05ULL,
0x4C898D607077A834ULL, 0x0E796C8BD19DA82EULL, 0xC9684EB723A3C500ULL, 0x8B98AF5C8649C51AULL,
0x05BAEB257E3544EBULL, 0x474A0ACEDBDF44F1ULL, 0x805B28F22DE129DFULL, 0xC2ABC919880B29C5ULL,
0xDEEF41EA6CF2718AULL, 0x9C1FA001C9187190ULL, 0x5B0E823D3F261CBEULL, 0x19FE63D69ACC1CA4ULL,
0x97DC27AF62B09D55ULL, 0xD52CC644C75A9D4FULL, 0x123DE4783164F061ULL, 0x50CD0593948EF07BULL,
0x6ACD4BB0484C5D7EULL, 0x283DAA5BECA65D64ULL, 0xEF2C88671B98304AULL, 0xADDC698CBE723050ULL,
0x23FE2DF5460EB1A1ULL, 0x610ECC1EE3E4B1BBULL, 0xA61FEE2215DADC95ULL, 0xE4EF0FC9B030DC8FULL,
0xF8AB873A54C984C0ULL, 0xBA5B66D1F12384DAULL, 0x7D4A44ED071DE9F4ULL, 0x3FBAA506A2F7E9EEULL,
0xB198E17F5A8B681FULL, 0xF3680094FF616805ULL, 0x347922A8095F052BULL, 0x7689C343ACB50531ULL,
0x98131AE0E0ED2F68ULL, 0xDAE3FB0B45072F72ULL, 0x1DF2D937B339425CULL, 0x5F0238DC16D34246ULL,
0xD1207CA5EEAFC3B7ULL, 0x93D09D4E4B45C3ADULL, 0x54C1B172BD7BAE83ULL, 0x163150991891AE99ULL,
0x0A75D66AFC68F6D6ULL, 0x488537815982F6CCULL, 0x8F9415BDAFBC9BE2ULL, 0xCD64F4560A569BF8ULL,
0x4346B02FF22A1A09ULL, 0x01B651C457C01A13ULL, 0xC6A773F8A1FE773DULL, 0x8457921304147727ULL,
0xBE57DC30D8D6DA22ULL, 0xFCA73DDB7D3CDA38ULL, 0x3BB61FE78B02B716ULL, 0x7946FE0C2EE8B70CULL,
0xF764BA75D69436FDULL, 0xB5945B9E737E36E7ULL, 0x728579A285405BC9ULL, 0x3075984920AA5BD3ULL,
0x2C3110BAC453039CULL, 0x6EC1F15161B90386ULL, 0xA9D0D36D97876EA8ULL, 0xEB203286326D6EB2ULL,
0x650276FFCA11EF43ULL, 0x27F297146FFBEF59ULL, 0xE0E3B52899C58277ULL, 0xA21354C33C2F826DULL,
0xD49A9780909A875CULL, 0x966A766B35708746ULL, 0x517B5457C34EEA68ULL, 0x138BB5BC66A4EA72ULL,
0x9DA9F1C59ED86B83ULL, 0xDF59102E3B326B99ULL, 0x18483212CD0C06B7ULL, 0x5AB8D3F968E606ADULL,
0x46FC5B0A8C1F5EE2ULL, 0x040CBA4129F55EF8ULL, 0xC31D98DDDFCB33D6ULL, 0x81ED79367A2133CCULL,
0x0FCF3D4F825DB23DULL, 0x4D3FDCA427B7B227ULL, 0x8A2EFE98D189DF09ULL, 0xC8DE1F737463DF13ULL,
0xF2DE5150A8A17216ULL, 0xB02EB0BB0D4B720CULL, 0x773F9287FB751F22ULL, 0x35CF736C5E9F1F38ULL,
0xBBED3715A6E39EC9ULL, 0xF91DD6FE03099ED3ULL, 0x3E0CF4C2F537F3FDULL, 0x7CFC152950DDF3E7ULL,
0x60B89DDAB424ABA8ULL, 0x22487C3111CEABB2ULL, 0xE5595E0DE7F0C69CULL, 0xA7A9BFE6421AC686ULL,
0x298BFB9FBA664777ULL, 0x6B7B1A741F8C476DULL, 0xAC6A3848E9B22A43ULL, 0xEE9AD9A34C582A59ULL,
};
u64 drr_crc64(const void *data, u64 len) {
const u8 *p = (const u8 *)data;
u64 crc = 0xFFFFFFFFFFFFFFFFULL;
for (u64 i = 0; i < len; i++) {
crc = crc64_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFFFFFFFFFFULL;
}
/* ---- 全局 DRR 状态 ---- */
static drr_state g_drr;
/* ---- Phase 8: 快照区 / 看门狗 / 恢复行为控制 ---- */
#define DRR_SNAP_MAGIC 0x445252534E415021ULL /* "DRRSNAP!" */
#define DRR_SNAP_VERSION 1u
typedef struct {
u64 magic;
u32 version;
u32 capacity; /* 条目容量(page0 空间决定) */
u32 used;
u32 generation;
u64 crc;
} drr_snap_header;
static dkm_dma_buffer g_snap_region;
static int g_snap_ok = 0;
static u32 g_snap_capacity = 0;
static int g_reboot_enabled = 1;
static void (*g_sched_fault_cb)(u32 task_slot) = 0;
typedef struct {
u32 task_slot; /* 0xFFFFFFFF = 空闲 */
u32 timeout_ticks;
u64 last_kick_tick;
} drr_watchdog_entry;
static drr_watchdog_entry g_drr_wd[DRR_WATCHDOG_MAX];
static drr_snap_header *snap_header(void) {
return (drr_snap_header *)g_snap_region.virt;
}
static drr_ckpt_page_entry *snap_entries(void) {
return (drr_ckpt_page_entry *)((u8 *)g_snap_region.virt + 64);
}
static u8 *snap_page_ptr(u32 idx) {
return (u8 *)g_snap_region.virt + 4096ULL * (1ULL + (u64)idx);
}
/* ---- Root key(Phase 9: RDRAND + TSC jitter 混合,boot 生成一次)
* 生成一次后固化:drr_init 可重入(emergency reset),root key 不换,
* 否则旧 segment key 全部失效。持久化(sealed disk region)留待后续。 ---- */
static u64 g_root_key_storage[4];
static int g_root_key_ready = 0;
static const u64 g_root_key_fallback[4] = {
0x4452525554534D31ULL, /* "DRRUTSM1" */
0xFEEDFACECAFEF00DULL,
0x0123456789ABCDEFULL,
0xFEDCBA9876543210ULL
};
static u64 drr_tsc(void) {
u64 v;
__asm__ volatile("rdtsc" : "=A"(v));
return v;
}
static int drr_have_rdrand(void) {
u32 a, b, c, d;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(1u), "c"(0u));
return (int)((c >> 30) & 1u);
}
static int drr_rdrand64(u64 *out) {
u8 ok;
u64 v;
__asm__ volatile("rdrand %0; setc %1" : "=r"(v), "=qm"(ok) :: "cc");
if (!ok) return 0;
*out = v;
return 1;
}
static void drr_root_key_generate(void) {
if (g_root_key_ready) {
g_drr.root_key = g_root_key_storage;
return;
}
int rdrand_ok = 1;
if (drr_have_rdrand()) {
for (int i = 0; i < 4; i++) {
if (!drr_rdrand64(&g_root_key_storage[i])) {
rdrand_ok = 0;
break;
}
}
} else {
rdrand_ok = 0;
}
if (rdrand_ok) {
/* 纵深:混入 TSC jitter,防 RDRAND 单点失效 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] ^= drr_tsc() * 0x9e3779b97f4a7c15ULL;
}
} else {
/* 无 RDRAND:常量基底 + 多轮 TSC jitter 混合 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] = g_root_key_fallback[i];
}
for (int r = 0; r < 16; r++) {
u64 j = drr_tsc();
for (int i = 0; i < 4; i++) {
j = (j ^ drr_tsc()) * 0x9e3779b97f4a7c15ULL;
g_root_key_storage[i] ^= j;
for (volatile int d = 0; d < 50; d++) { }
}
}
}
g_root_key_ready = 1;
g_drr.root_key = g_root_key_storage;
log_info("[DRR] root key generated");
log_hex64("[DRR] rdrand=", (u64)rdrand_ok);
}
/* ---- Emergency Pool 内部分配 ---- */
static void *pool_alloc_internal(u64 size, u64 alignment) {
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
u64 aligned = utsm_align_up_u64(current, alignment);
u64 new_offset = (aligned - (u64)&g_drr.pool[0]) + size;
if (new_offset > g_drr.pool_limit) {
return NULL;
}
g_drr.pool_offset = new_offset;
return (void *)aligned;
}
void *drr_emergency_alloc(u64 size, u64 alignment) {
if (!g_drr.initialized) return NULL;
return pool_alloc_internal(size, alignment);
}
void drr_emergency_reset(void) {
if (!g_drr.initialized) return;
/* 保留已分配的子缓冲区,仅重置池偏移到子缓冲区之后 */
/* 注意:此函数仅在 recovery 路径紧急情况下使用 */
g_drr.pool_offset = 0;
/* 重新分配子缓冲区 */
drr_init();
}
/* ---- R3b: FUCK 配置接线(drr_apply_config 设置,drr_init 消费) ---- */
static u64 g_cfg_pool_bytes; /* 0 = 编译期默认 */
static u64 g_cfg_log_size; /* 0 = 编译期默认 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size) {
g_cfg_pool_bytes = pool_kb ? pool_kb * 1024ULL : 0;
g_cfg_log_size = recovery_log_size;
if (g_cfg_pool_bytes > DRR_EMERGENCY_POOL_SIZE) {
log_warn("[DRR] pool_kb exceeds compile-time pool, clamped");
log_hex64("[DRR] compile-time=", DRR_EMERGENCY_POOL_SIZE);
g_cfg_pool_bytes = DRR_EMERGENCY_POOL_SIZE;
}
}
/* ---- 初始化 ---- */
void drr_init(void) {
log_info("[DRR] init begin");
drr_memzero(&g_drr, sizeof(g_drr));
/* R3b: 池上限 = min(编译期池, FUCK 配置);小于最小子缓冲布局时回退默认 */
g_drr.pool_offset = 0;
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
{
const u64 MIN_LAYOUT =
DRR_EMERGENCY_STACK_SIZE + DRR_RECOVERY_LOG_SIZE +
DRR_CKPT_META_BUF_SIZE + DRR_CRASH_BUF_SIZE + DRR_META_SLAB_SIZE;
if (g_cfg_pool_bytes && g_cfg_pool_bytes >= MIN_LAYOUT) {
g_drr.pool_limit = g_cfg_pool_bytes;
} else if (g_cfg_pool_bytes) {
log_warn("[DRR] pool_kb below minimum sub-buffer layout, default kept");
log_hex64("[DRR] minimum=", MIN_LAYOUT);
}
}
drr_root_key_generate();
g_drr.recovery_generation = 1;
g_drr.heartbeat_interval = 1000;
g_drr.active_slot = 0; /* A */
/* 分配 Emergency Pool 子缓冲区(recovery log 大小可由 FUCK 配置) */
g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64);
g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE;
g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */
u64 log_size = DRR_RECOVERY_LOG_SIZE;
if (g_cfg_log_size && g_cfg_log_size >= sizeof(drr_log_entry) &&
g_cfg_log_size <= g_drr.pool_limit - g_drr.pool_offset) {
log_size = g_cfg_log_size;
} else if (g_cfg_log_size) {
log_warn("[DRR] recovery_log_size out of range, default kept");
}
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(log_size, 64);
g_drr.log_capacity = (u32)(log_size / sizeof(drr_log_entry));
g_drr.log_count = 0;
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
g_drr.crash_buf = (u8 *)pool_alloc_internal(DRR_CRASH_BUF_SIZE, 64);
g_drr.crash_buf_size = DRR_CRASH_BUF_SIZE;
g_drr.meta_slab = pool_alloc_internal(DRR_META_SLAB_SIZE, 64);
g_drr.meta_slab_offset = 0;
/* 初始化 A/B slot */
drr_memzero(&g_drr.ckpt_a, sizeof(drr_checkpoint_meta));
drr_memzero(&g_drr.ckpt_b, sizeof(drr_checkpoint_meta));
g_drr.ckpt_a.magic = DRR_CKPT_MAGIC;
g_drr.ckpt_a.version = DRR_CKPT_VERSION;
g_drr.ckpt_a.active = 1;
g_drr.ckpt_b.magic = DRR_CKPT_MAGIC;
g_drr.ckpt_b.version = DRR_CKPT_VERSION;
g_drr.initialized = 1;
log_hex64("[DRR] pool_limit=", g_drr.pool_limit);
log_hex64("[DRR] log_capacity=", (u64)g_drr.log_capacity);
/* ---- Phase 8: 快照区(dma 页,独立于普通堆;失败降级 metadata-only) ---- */
g_snap_ok = 0;
g_snap_capacity = 0;
g_reboot_enabled = 1;
g_sched_fault_cb = 0;
for (int wdi = 0; wdi < DRR_WATCHDOG_MAX; wdi++) {
g_drr_wd[wdi].task_slot = 0xFFFFFFFFu;
g_drr_wd[wdi].timeout_ticks = 0;
g_drr_wd[wdi].last_kick_tick = 0;
}
{
/* page0 = 头 + 条目数组,其后 128 页快照数据(共 129 页) */
dkm_dma_buffer buf;
if (dma_alloc_pages(1 + 128, 4096, 0xFFFFFFFFULL, &buf) == 0 && buf.virt) {
g_snap_region = buf;
u32 cap = (u32)((4096 - 64) / sizeof(drr_ckpt_page_entry));
drr_snap_header *h = (drr_snap_header *)buf.virt;
drr_memzero(h, 4096);
h->magic = DRR_SNAP_MAGIC;
h->version = DRR_SNAP_VERSION;
h->capacity = cap;
h->used = 0;
g_snap_capacity = cap;
g_snap_ok = 1;
log_info("[DRR] snapshot region ok");
log_hex64("[DRR] pages=", 1 + 128);
} else {
/* 严格错误策略插桩:快照区是 DRR 页回滚的前提,缺失即降级
* metadata-only——按零降级原则直接 panic(DRR-E01)。 */
panic_full("DRR-E01 SNAPSHOT REGION ALLOC FAILED",
"snapshot dma pages unavailable; metadata-only mode is forbidden", 0);
}
}
log_info("[DRR] init ok");
}
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DRR 能力,建立代码骨架,
* 调用即 panic(DRR-E2x 系列)。当前无调用方(骨架)。
* =================================================================== */
/* Root key 持久化(sealed disk region):drr_init 可重入要求 root key
* 跨 emergency reset 固化。当前实现走内存 fallback key(见
* drr_root_key_generate),sealed disk 尚未落盘。 */
void drr_root_key_seal_to_disk(void) {
panic_full("DRR-E02 ROOT KEY SEAL NOT IMPLEMENTED",
"sealed disk region for root key persistence: designed, not implemented", 0);
}
/* task_kill crypto erase:任务被杀时 key_epoch++ 并擦除其密钥材料。
* 当前 task_exit 只回收 slot,不做 crypto erase。 */
void drr_task_kill_crypto_erase(u32 task_slot) {
(void)task_slot;
panic_full("DRR-E03 TASK KILL CRYPTO ERASE NOT IMPLEMENTED",
"key_epoch++ erase on task kill: designed, not implemented", 0);
}
/* segment/system 级回滚入口(R4 遗留):页级回滚已实现,
* segment/system 级接口的调用方尚未接线。 */
void drr_rollback_segment(u32 seg_slot) {
(void)seg_slot;
panic_full("DRR-E04 SEGMENT ROLLBACK NOT IMPLEMENTED",
"segment-level rollback: designed (R4), not verified/implemented", 0);
}
void drr_rollback_system(void) {
panic_full("DRR-E05 SYSTEM ROLLBACK NOT IMPLEMENTED",
"system-level rollback: designed (R4), not verified/implemented", 0);
}
/* DKM 驱动 recovery ops(DKM README 设计:quiesce/reset/reinit/dump_state)。
* 驱动侧从未提供该回调表,DRR 亦未在恢复路径调用。 */
typedef struct {
u32 magic; /* 'D','R','O','P' */
u32 version;
void (*quiesce)(void *drv);
void (*reset)(void *drv);
void (*reinit)(void *drv);
void (*dump_state)(void *drv);
} dkm_driver_recovery_ops;
void drr_driver_recovery_dispatch(const dkm_driver_recovery_ops *ops, void *drv) {
(void)drv;
if (!ops) {
panic_full("DRR-E06 DRIVER RECOVERY OPS MISSING",
"driver did not provide dkm_driver_recovery_ops", 0);
return;
}
panic_full("DRR-E07 DRIVER RECOVERY NOT IMPLEMENTED",
"quiesce/reset/reinit dispatch: designed (DKM README), not implemented", 0);
}
const u64 *drr_get_root_key(void) {
return g_drr.root_key;
}
u64 drr_recovery_generation(void) {
return g_drr.recovery_generation;
}
/* ---- Fault 报告 ---- */
void drr_report_fault(const char *reason) {
if (!g_drr.initialized) return;
log_error("[DRR] FAULT: ");
log_error(reason);
/* 记录到 crash buffer */
if (g_drr.crash_buf && g_drr.crash_buf_size > 0) {
/* 简单记录:写入 reason 的前 N 字节 */
u64 i = 0;
const char *s = reason;
while (*s && i < g_drr.crash_buf_size - 1) {
g_drr.crash_buf[i++] = (u8)*s++;
}
g_drr.crash_buf[i] = 0;
}
/* 增加 recovery generation */
g_drr.recovery_generation++;
}
/* ---- F1: 崩溃归档(panic_full 调用;crash_buf 写 symbol+msg+rip 简表) ----
* 返回 0=已归档;-1=DRR 未初始化(crash_buf 不可用)。 */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs) {
if (!g_drr.initialized || !g_drr.crash_buf || g_drr.crash_buf_size == 0) {
return -1;
}
u64 i = 0;
u8 *b = g_drr.crash_buf;
u64 cap = g_drr.crash_buf_size - 1;
/* 头部标记 */
const char *magic = "CRASH1";
for (const char *s = magic; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = symbol ? symbol : "UNKNOWN"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = msg ? msg : "-"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i] = 0;
/* recovery generation 前进(归档计数) */
g_drr.recovery_generation++;
log_error("[DRR] crash archived");
log_hex64("[DRR] generation=", g_drr.recovery_generation);
return 0;
}
/* ---- Recovery Log ---- */
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
if (!g_drr.initialized || !g_drr.log_base) return;
if (g_drr.log_count >= g_drr.log_capacity) {
/* 日志满,最简单的策略:丢弃最早的条目 */
for (u32 i = 1; i < g_drr.log_count; i++) {
g_drr.log_base[i - 1] = g_drr.log_base[i];
}
g_drr.log_count--;
}
drr_log_entry *e = &g_drr.log_base[g_drr.log_count++];
e->segment_slot = segment_slot;
e->flags = DRR_LOG_FLAG_INTENT;
e->offset = offset;
e->length = len;
e->timestamp = g_drr.heartbeat_counter;
}
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len) {
if (!g_drr.initialized || !g_drr.log_base) return;
if (g_drr.log_count >= g_drr.log_capacity) {
for (u32 i = 1; i < g_drr.log_count; i++) {
g_drr.log_base[i - 1] = g_drr.log_base[i];
}
g_drr.log_count--;
}
drr_log_entry *e = &g_drr.log_base[g_drr.log_count++];
e->segment_slot = segment_slot;
e->flags = DRR_LOG_FLAG_COMMIT;
e->offset = offset;
e->length = len;
e->timestamp = g_drr.heartbeat_counter;
}
/* ---- Checkpoint ---- */
int drr_checkpoint_begin(drr_checkpoint_type type) {
if (!g_drr.initialized) return -1;
/* 选择 inactive slot */
drr_checkpoint_meta *target;
if (g_drr.active_slot == 0) {
target = &g_drr.ckpt_b;
} else {
target = &g_drr.ckpt_a;
}
target->magic = DRR_CKPT_MAGIC;
target->version = DRR_CKPT_VERSION;
target->active = 0; /* 写入时为 inactive */
target->type = type;
target->global_epoch = g_drr.recovery_generation;
target->dirty_page_count = 0;
target->mac_root = 0;
target->segment_count = 0;
target->crc = 0;
return 0;
}
int drr_checkpoint_finish(void) {
if (!g_drr.initialized) return -1;
/* 写入 inactive slot 的 CRC */
drr_checkpoint_meta *target;
if (g_drr.active_slot == 0) {
target = &g_drr.ckpt_b;
} else {
target = &g_drr.ckpt_a;
}
/* 计算 CRC(不含 crc 字段本身) */
target->crc = drr_crc64(target,
sizeof(drr_checkpoint_meta) - sizeof(u64));
/* 原子切换 active slot */
target->active = 1;
/* 旧 slot 标记 inactive */
if (g_drr.active_slot == 0) {
g_drr.ckpt_a.active = 0;
g_drr.active_slot = 1;
} else {
g_drr.ckpt_b.active = 0;
g_drr.active_slot = 0;
}
/* 增加 recovery generation */
g_drr.recovery_generation++;
/* 清空 recovery log */
g_drr.log_count = 0;
return 0;
}
int drr_checkpoint_recover(drr_checkpoint_meta *out_meta) {
if (!g_drr.initialized || !out_meta) return -1;
/* 尝试 active slot */
drr_checkpoint_meta *active = (g_drr.active_slot == 0) ?
&g_drr.ckpt_a : &g_drr.ckpt_b;
drr_checkpoint_meta *fallback = (g_drr.active_slot == 0) ?
&g_drr.ckpt_b : &g_drr.ckpt_a;
/* 验证 active slot */
if (active->magic == DRR_CKPT_MAGIC && active->active == 1) {
u64 expected_crc = active->crc;
active->crc = 0;
u64 actual_crc = drr_crc64(active,
sizeof(drr_checkpoint_meta) - sizeof(u64));
active->crc = expected_crc;
if (actual_crc == expected_crc) {
drr_memcpy(out_meta, active, sizeof(drr_checkpoint_meta));
return 0;
}
}
/* active 无效,尝试 fallback */
if (fallback->magic == DRR_CKPT_MAGIC && fallback->active == 1) {
u64 expected_crc = fallback->crc;
fallback->crc = 0;
u64 actual_crc = drr_crc64(fallback,
sizeof(drr_checkpoint_meta) - sizeof(u64));
fallback->crc = expected_crc;
if (actual_crc == expected_crc) {
drr_memcpy(out_meta, fallback, sizeof(drr_checkpoint_meta));
return 0;
}
}
/* 双槽都无效 */
return -2;
}
/* ---- Heartbeat ---- */
void drr_heartbeat_tick(void) {
if (!g_drr.initialized) return;
g_drr.heartbeat_counter++;
}
u64 drr_heartbeat_get(void) {
return g_drr.heartbeat_counter;
}
/* ===================================================================
* Phase 8: 真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* =================================================================== */
int drr_ckpt_snapshot_dirty(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2; /* metadata-only 模式不支持页快照 */
drr_checkpoint_begin(DRR_CKPT_DIRTY_PAGE);
drr_snap_header *h = snap_header();
drr_ckpt_page_entry *entries = snap_entries();
u32 used = 0;
for (u32 slot = 0; slot < g_utsm_max_segments; slot++) {
utsm_segment_desc *desc = utsm_get_segment(slot);
if (!desc || desc->state != UTSM_SEG_ACTIVE) continue;
desc->state = UTSM_SEG_CHECKPOINTING;
for (u32 s = 0; s < desc->dirty_shard_count && desc->dirty_shards; s++) {
utsm_dirty_shard *shard = &desc->dirty_shards[s];
if (!shard->bitmap) continue;
u64 words = (shard->page_count + 63) / 64;
for (u64 w = 0; w < words && used < g_snap_capacity; w++) {
u64 word = shard->bitmap[w];
while (word && used < g_snap_capacity) {
u64 bit = (u64)__builtin_ctzll(word);
u64 page = w * 64 + bit;
u8 *src = desc->cipher_base + page * UTSM_PAGE_SIZE;
u8 *dst = snap_page_ptr(used);
drr_memcpy(dst, src, UTSM_PAGE_SIZE);
entries[used].seg_slot = slot;
entries[used].page_index = (u32)page;
entries[used].cipher_vaddr = (u64)src;
entries[used].crc = drr_crc64(dst, UTSM_PAGE_SIZE);
entries[used].key_epoch = desc->key_epoch;
entries[used].mac = utsm_page_mac(g_drr.root_key, slot, page,
desc->key_epoch, dst);
entries[used].region_idx = used;
entries[used].used = 1;
used++;
/* 快照成功才清脏位;区满时保留脏位(部分提交,下轮重试) */
shard->bitmap[w] &= ~(1ULL << bit);
if (shard->dirty_count > 0) shard->dirty_count--;
word &= ~(1ULL << bit);
}
}
shard->last_checkpoint_epoch = g_drr.recovery_generation;
}
desc->state = UTSM_SEG_ACTIVE;
desc->checkpoint_generation = g_drr.recovery_generation;
if (used >= g_snap_capacity) break;
}
/* 区域头(crc 不含自身字段) */
h->used = used;
h->generation = (u32)g_drr.recovery_generation;
h->crc = drr_crc64(h, sizeof(drr_snap_header) - sizeof(u64));
/* 元数据 + A/B 原子切换(begin 已选好 inactive slot,同一表达式取回) */
drr_checkpoint_meta *target = (g_drr.active_slot == 0) ? &g_drr.ckpt_b : &g_drr.ckpt_a;
target->dirty_page_count = used;
/* Phase 9: mac_root = keyed BLAKE2b 链(root_key 派生,绑定全部页 MAC) */
{
u8 kb[32];
u8 chain[64];
for (int i = 0; i < 4; i++) utsm_store64_le(kb + 8 * i, g_drr.root_key[i]);
utsm_blake2b_ctx mc;
utsm_blake2b_init(&mc, 64, kb, 32);
for (u32 i = 0; i < used; i++) {
u8 rec[8 + 8 + 8 + 8];
utsm_store64_le(rec + 0, 0x544F4F524D535455ULL); /* "UTSMROOT" LE */
utsm_store64_le(rec + 8, ((u64)entries[i].seg_slot) |
((u64)entries[i].page_index << 32));
utsm_store64_le(rec + 16, entries[i].key_epoch);
utsm_store64_le(rec + 24, entries[i].mac);
utsm_blake2b_update(&mc, rec, sizeof(rec));
}
utsm_blake2b_final(&mc, chain);
target->mac_root = utsm_load64_le(chain);
}
target->segment_count = 0;
drr_checkpoint_finish();
log_info("[DRR] ckpt snapshot ok");
log_hex64("[DRR] pages=", used);
return (int)used;
}
int drr_rollback_pages(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2;
drr_checkpoint_meta meta;
if (drr_checkpoint_recover(&meta) != 0) return -3;
if (meta.type != DRR_CKPT_DIRTY_PAGE) return -4;
drr_snap_header *h = snap_header();
if (h->magic != DRR_SNAP_MAGIC || h->version != DRR_SNAP_VERSION) return -5;
u32 used = h->used;
if (used > h->capacity) used = h->capacity;
drr_ckpt_page_entry *entries = snap_entries();
/* 触碰段先置 RECOVERING */
for (u32 i = 0; i < used; i++) {
utsm_segment_desc *d = utsm_get_segment(entries[i].seg_slot);
if (d && d->state == UTSM_SEG_ACTIVE) d->state = UTSM_SEG_RECOVERING;
}
int failed = 0;
u32 restored = 0;
for (u32 i = 0; i < used; i++) {
drr_ckpt_page_entry *e = &entries[i];
u8 *snap = snap_page_ptr(e->region_idx);
utsm_segment_desc *d = utsm_get_segment(e->seg_slot);
if (drr_crc64(snap, UTSM_PAGE_SIZE) != e->crc) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback crc fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
/* Phase 9: 页 MAC 校验(伪造/跨纪元重放/区域篡改在此被拒) */
u64 mac = utsm_page_mac(g_drr.root_key, e->seg_slot, e->page_index,
e->key_epoch, snap);
if (mac != e->mac) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback mac fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
drr_memcpy((void *)e->cipher_vaddr, snap, UTSM_PAGE_SIZE);
restored++;
/* 清除该页脏位(P2 之类的回写不再被误判为未 checkpoint) */
if (d && d->dirty_shard_count > 0 && d->dirty_shards && d->dirty_shards[0].bitmap) {
utsm_dirty_shard *sh = &d->dirty_shards[0];
u64 page = e->page_index;
if (page < sh->page_count && (sh->bitmap[page / 64] & (1ULL << (page % 64)))) {
sh->bitmap[page / 64] &= ~(1ULL << (page % 64));
if (sh->dirty_count > 0) sh->dirty_count--;
}
}
if (d && d->state == UTSM_SEG_RECOVERING) d->state = UTSM_SEG_ACTIVE;
}
log_info("[DRR] rollback done");
log_hex64("[DRR] pages=", restored);
return failed ? -2 : (int)restored;
}
void drr_system_rollback(const char *reason) {
log_error("[DRR] SYSTEM ROLLBACK");
if (reason) log_error(reason);
log_hex64("[DRR] generation=", g_drr.recovery_generation);
log_hex64("[DRR] heartbeat=", g_drr.heartbeat_counter);
log_error("[DRR] rebooting (8042 reset)");
for (;;) {
__asm__ volatile("cli");
outb(0x64, 0xFE); /* 8042 复位(A20 gate 已无关紧要) */
for (volatile u32 i = 0; i < 1000000u; i++) { }
__asm__ volatile("hlt");
}
}
void drr_handle_task_fault(u32 task_slot, const char *reason) {
log_error("[DRR] FAULT");
if (reason) log_error(reason);
log_hex64("[DRR] task_slot=", task_slot);
drr_report_fault(reason ? reason : "task fault");
drr_log_write_intent(task_slot, 0, 0);
int pages = drr_rollback_pages();
log_hex64("[DRR] rollback_pages=", (u64)(i64)pages);
if (g_sched_fault_cb) {
g_sched_fault_cb(task_slot); /* 可能不返回(当前任务被杀+切换) */
drr_log_write_commit(task_slot, 0, 0);
return;
}
if (g_reboot_enabled) {
drr_system_rollback(reason ? reason : "task fault (no sched cb)");
}
/* reboot disabled(selftest):仅留日志 */
}
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks) {
if (!g_drr.initialized || timeout_ticks == 0) return -1;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == 0xFFFFFFFFu) {
g_drr_wd[i].task_slot = task_slot;
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
return -2;
}
int drr_watchdog_unregister(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].task_slot = 0xFFFFFFFFu;
return 0;
}
}
return -1;
}
void drr_watchdog_kick(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return;
}
}
}
void drr_watchdog_check(void) {
if (!g_drr.initialized) return;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
u32 slot = g_drr_wd[i].task_slot;
if (slot == 0xFFFFFFFFu) continue;
u64 now = g_drr.heartbeat_counter;
if (now - g_drr_wd[i].last_kick_tick > g_drr_wd[i].timeout_ticks) {
g_drr_wd[i].last_kick_tick = now; /* 防重复触发 */
drr_handle_task_fault(slot, "watchdog timeout");
}
}
}
void drr_set_reboot_enabled(int enabled) {
g_reboot_enabled = enabled;
}
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot)) {
g_sched_fault_cb = cb;
}
/* kernel_api.drr 服务表(dkm_fill_platform_info 挂入) */
static const drr_recovery_api g_drr_api = {
.report_fault = drr_report_fault,
.heartbeat_get = drr_heartbeat_get,
.emergency_alloc = drr_emergency_alloc,
.ckpt_snapshot_dirty = drr_ckpt_snapshot_dirty,
.rollback_pages = drr_rollback_pages
};
const drr_recovery_api *drr_get_recovery_api(void) {
return &g_drr_api;
}
-13
View File
@@ -1,13 +0,0 @@
#ifndef UTSM_ARENA_H
#define UTSM_ARENA_H
#include <utsm/types.h>
void arena_init(void);
void *kmem_alloc(u64 size);
void *kmem_alloc_aligned(u64 size, u64 alignment);
void *kmem_alloc_page(u64 page_count);
u64 kmem_arena_virtual_base(void);
u64 kmem_arena_virtual_end(void);
#endif
-9
View File
@@ -1,9 +0,0 @@
#ifndef UTSM_ASSERT_H
#define UTSM_ASSERT_H
#include <utsm/log.h>
#include <utsm/panic.h>
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
#endif
-29
View File
@@ -1,29 +0,0 @@
#ifndef UTSM_ATTEST_H
#define UTSM_ATTEST_H
#include <utsm/types.h>
/* ============================================================
* SAS-R0 启动期能力证明(boot-time feature attestation)
*
* 哲学:设计已定的能力,"没好"不允许带病启动——
* 各子系统就绪时 attest_report() 打点;内核交接 DSK 前
* attest_check() 统一核查。FUCK [attest] require_<name>=1
* (默认 1)而能力未就绪 → 莲花屏 panic 带该能力的错误码。
* ============================================================ */
/* 就绪打点。errcode 为该能力"未就绪"时的 panic 错误码。
* 同名重复 report 覆盖 ready 标志(后到的真实探测为准)。 */
void attest_report(const char *name, const char *errcode, int ready);
/* 便捷封装:就绪打点 */
void attest_report_ready(const char *name, const char *errcode);
/* 交接 DSK 前核查:缺一项即 panic(先串口列出全部缺失项)。
* cfg = FUCK ini_config*(用 void* 规避 ini_config 前向声明问题)。 */
void attest_check(const void *cfg);
/* 当前缺失项计数(调试/日志用) */
u32 attest_missing_count(void);
#endif /* UTSM_ATTEST_H */
-48
View File
@@ -1,48 +0,0 @@
#ifndef UTSM_BLAKE2B_H
#define UTSM_BLAKE2B_H
#include <utsm/types.h>
/* ===================================================================
* BLAKE2b(RFC 7693)— Phase 9 真实加密原语
*
* 纯 64-bit 整数实现(无 SSE/FPU 依赖,-mno-sse 下可用)。
* 支持 1-64B 可变输出与 keyed 模式(key 0-64B)。
* 角色:KDF(root→segment key)、页 MAC、mac_root 链。
* =================================================================== */
#define UTSM_BLAKE2B_BLOCKBYTES 128u
#define UTSM_BLAKE2B_MAXOUT 64u
typedef struct {
u64 h[8]; /* 链值 */
u64 t[2]; /* 已处理字节数(128 位计数器) */
u8 buf[128]; /* 待压缩块缓冲 */
u32 buf_len; /* buf 内有效字节数 */
u32 out_len; /* 摘要输出长度 1-64 */
u32 last_block; /* 最后一块标志 */
} utsm_blake2b_ctx;
void utsm_blake2b_init(utsm_blake2b_ctx *ctx, u32 out_len, const void *key, u32 key_len);
void utsm_blake2b_update(utsm_blake2b_ctx *ctx, const void *data, u64 len);
void utsm_blake2b_final(utsm_blake2b_ctx *ctx, void *out);
/* one-shot:out_len 1-64,key_len 0-64(key=NULL 表示无 key) */
void utsm_blake2b(const void *data, u64 len, const void *key, u32 key_len, void *out, u32 out_len);
/* ---- 小端序列化辅助(crypto 各处共用) ---- */
static inline void utsm_store32_le(u8 *p, u32 v) {
p[0] = (u8)(v); p[1] = (u8)(v >> 8); p[2] = (u8)(v >> 16); p[3] = (u8)(v >> 24);
}
static inline u32 utsm_load32_le(const u8 *p) {
return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24);
}
static inline void utsm_store64_le(u8 *p, u64 v) {
utsm_store32_le(p, (u32)v);
utsm_store32_le(p + 4, (u32)(v >> 32));
}
static inline u64 utsm_load64_le(const u8 *p) {
return (u64)utsm_load32_le(p) | ((u64)utsm_load32_le(p + 4) << 32);
}
#endif /* UTSM_BLAKE2B_H */
-34
View File
@@ -1,34 +0,0 @@
#ifndef UTSM_BLOCK_H
#define UTSM_BLOCK_H
#include <utsm/types.h>
#define DKM_BLOCK_MAX_DEVICES 8u
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
typedef int (*dkm_block_write_fn)(void *ctx, u64 lba, u32 count, const void *buffer);
typedef struct dkm_block_device_desc {
const char *name;
u64 sector_size;
u64 sector_count;
void *ctx;
dkm_block_read_fn read;
dkm_block_write_fn write;
} dkm_block_device_desc;
typedef struct dkm_block_api {
int (*register_device)(const dkm_block_device_desc *desc);
u32 (*device_count)(void);
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
u64 (*sector_size)(u32 index);
const char *(*device_name)(u32 index);
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
u64 (*sector_count)(u32 index); /* 0x38: tail-append, 老内核该槽为 0 表示未知 */
} dkm_block_api;
void block_init(void);
const dkm_block_api *block_get_api(void);
#endif
-26
View File
@@ -1,26 +0,0 @@
#ifndef UTSM_CAPABILITY_H
#define UTSM_CAPABILITY_H
#include <utsm/types.h>
#include <utsm/status.h>
#define UTSM_RIGHT_READ (1U << 0)
#define UTSM_RIGHT_WRITE (1U << 1)
#define UTSM_RIGHT_EXEC (1U << 2)
#define UTSM_RIGHT_SHARE (1U << 3)
#define UTSM_RIGHT_DMA (1U << 4)
struct utsm_segment_desc;
typedef struct {
u32 segment_slot;
u32 generation;
u32 rights;
u32 epoch;
u64 uuid_digest;
u64 auth_tag;
} utsm_capability;
int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, struct utsm_segment_desc **out_desc);
#endif
-32
View File
@@ -1,32 +0,0 @@
#ifndef UTSM_CONFIG_H
#define UTSM_CONFIG_H
#include <utsm/types.h>
/* ---- 编译期固定常量(不暴露给 FUCK 配置) ---- */
#define UTSM_CACHE_LINE_SIZE 64ULL
#define UTSM_PAGE_SIZE 4096ULL
#define UTSM_KERNEL_STACK_SIZE 16384ULL
/* ---- 编译期默认值宏(用于静态数组大小上限) ---- */
#define UTSM_MAX_SEGMENTS_DEFAULT 1024U
#define UTSM_MAX_CAPABILITIES_DEFAULT 256U
#define UTSM_MAX_PCKC_KEYS_DEFAULT 8U
#define UTSM_ARENA_SIZE_DEFAULT (64ULL * 1024ULL * 1024ULL)
#define UTSM_DIRTY_SHARD_PAGES_DEFAULT 1024ULL
/* ---- 运行期配置变量(由 kernel_main 从 FUCK 文件初始化) ---- */
extern u32 g_utsm_max_segments;
extern u32 g_utsm_max_capabilities;
extern u32 g_utsm_max_pckc_keys;
extern u64 g_utsm_arena_size;
extern u64 g_utsm_dirty_shard_pages;
/* ---- 保留旧宏名(兼容性),映射到默认值 ---- */
#define UTSM_MAX_SEGMENTS UTSM_MAX_SEGMENTS_DEFAULT
#define UTSM_MAX_CAPABILITIES UTSM_MAX_CAPABILITIES_DEFAULT
#define UTSM_MAX_PCKC_KEYS UTSM_MAX_PCKC_KEYS_DEFAULT
#define UTSM_ARENA_SIZE UTSM_ARENA_SIZE_DEFAULT
#define UTSM_DIRTY_SHARD_PAGES UTSM_DIRTY_SHARD_PAGES_DEFAULT
#endif
-30
View File
@@ -1,30 +0,0 @@
#ifndef UTSM_CRYPTO_H
#define UTSM_CRYPTO_H
#include <utsm/types.h>
#include <utsm/uuid.h>
struct utsm_segment_desc;
typedef struct {
u64 words[4];
} utsm_key_material;
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key);
u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch);
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out);
/* ===================================================================
* Phase 9 扩展:真实加密原语服务(尾部追加,ABI 兼容)
* stream = ChaCha20 block(RFC 8439),hash/KDF/MAC = BLAKE2b(RFC 7693)
* =================================================================== */
/* ChaCha20 block function:64B keystream。
* counter=0 用法下 nonce=tweak 即可保证 (key,nonce) 唯一。 */
void utsm_chacha20_block(const u32 key_words[8], u64 nonce, u64 counter, u8 out[64]);
/* 4KB 密文页 MAC:keyed BLAKE2b(key=root_key, domain||seg_slot||page||epoch||data),
* 截断 64 位。root_key 为 4×u64(DRR root key)。 */
u64 utsm_page_mac(const u64 *root_key, u32 seg_slot, u64 page_index, u64 key_epoch, const void *page);
#endif
-956
View File
@@ -1,956 +0,0 @@
/* deshab_ui.h — Deshab 视觉风格系统
*
* "Sealed Arc" 设计语言:以 UTSM 封缄内存为灵感的统一视觉体系。
*
* 核心设计隐喻:
* Arc(弧) — 封缄/解封的视觉表达,圆角、弧形边框、弧线动画
* Seal(封) — 密封容器的安全感,卡片容器、发光边缘、透明覆盖
* Flow(流) — 数据流的连续性,渐变背景、平滑过渡、流式布局
*
* 设计令牌分层:
* L0 — 原始色值(Deshab Palette)
* L1 — 语义色值(Semantic Colors,引用 L0)
* L2 — 组件令牌(Component Tokens,引用 L1)
*
* 所有 UI 组件必须通过本头文件获取视觉参数,禁止硬编码颜色/尺寸。
*/
#ifndef DESHAB_UI_H
#define DESHAB_UI_H
/* ===================================================================
* 基础类型(自包含,不依赖 utsm/types.h 以便 DSK/Shell 独立使用)
* =================================================================== */
typedef unsigned char du_u8;
typedef unsigned short du_u16;
typedef unsigned int du_u32;
typedef unsigned long long du_u64;
typedef long long du_i64;
/* ===================================================================
* L0 — Deshab 原始色板 (Deshab Palette)
*
* 色彩命名规则:{色调}_{明度}
* 格式:0xAARRGGBB(与 framebuffer 一致,小端 x86 上 BB 置低位)
* =================================================================== */
/* --- 主色调:深海蓝 (Abyss Blue) --- */
#define DP_ABYSS_900 0xFF0A1428u /* 最深:终端背景、底色 */
#define DP_ABYSS_800 0xFF0F1E38u /* 深层:暗卡片背景 */
#define DP_ABYSS_700 0xFF162848u /* 暗面:悬停态背景 */
#define DP_ABYSS_600 0xFF1E3868u /* 中暗:输入框背景 */
#define DP_ABYSS_500 0xFF284888u /* 中间:次级强调 */
#define DP_ABYSS_400 0xFF3868B0u /* 中亮:默认强调 */
#define DP_ABYSS_300 0xFF5088D0u /* 亮面:链接色 */
#define DP_ABYSS_200 0xFF78A8E8u /* 高亮:焦点指示 */
#define DP_ABYSS_100 0xFFA8CCF4u /* 最亮:浅色辅助 */
/* --- 强调色:封缄青 (Seal Cyan) — 主要操作/交互 --- */
#define DP_SEAL_900 0xFF004858u
#define DP_SEAL_700 0xFF007898u
#define DP_SEAL_500 0xFF00A8CCu /* 标准强调:按钮、选中态 */
#define DP_SEAL_300 0xFF44CCF0u /* 高亮强调:焦点环 */
#define DP_SEAL_100 0xFF99E8FFu /* 亮强调:发光边缘 */
/* --- 辅助色:弧光紫 (Arc Violet) — 次级/装饰 --- */
#define DP_ARC_900 0xFF2A1040u
#define DP_ARC_700 0xFF5A2080u
#define DP_ARC_500 0xFF8844CCu /* 装饰强调:渐变终点、图标 */
#define DP_ARC_300 0xFFB888E8u
#define DP_ARC_100 0xFFE0C8F8u
/* --- 中性色 (Neutral) --- */
#define DP_NEUTRAL_0 0xFF000000u /* 纯黑 */
#define DP_NEUTRAL_50 0xFF181820u /* 接近黑 */
#define DP_NEUTRAL_100 0xFF2A2A38u /* 暗灰 */
#define DP_NEUTRAL_200 0xFF484860u /* 中暗灰 */
#define DP_NEUTRAL_300 0xFF6A6A88u /* 中灰 */
#define DP_NEUTRAL_400 0xFF9090A8u /* 中亮灰 */
#define DP_NEUTRAL_500 0xFFB0B0C0u /* 标准灰 */
#define DP_NEUTRAL_600 0xFFD0D0DCu /* 亮灰 */
#define DP_NEUTRAL_700 0xFFE8E8F0u /* 浅灰 */
#define DP_NEUTRAL_800 0xFFF0F0F8u /* 接近白 */
#define DP_NEUTRAL_900 0xFFFFFFFFu /* 纯白 */
/* --- 语义辅助色 --- */
#define DP_SUCCESS 0xFF40C880u /* 成功:绿色 */
#define DP_WARNING 0xFFF0A030u /* 警告:琥珀色 */
#define DP_ERROR 0xFFFF4466u /* 错误:红色 */
#define DP_INFO 0xFF4499FFu /* 信息:蓝色 */
/* --- 渐变色 — DSK 加载器 / FirstInit 背景专用 --- */
#define DP_GRAD_TOP 0xFFC8E0F0u /* 淡蓝顶 */
#define DP_GRAD_MID 0xFF8888B0u /* 紫灰中 */
#define DP_GRAD_BOT 0xFF49306Fu /* 深紫底 */
/* --- 欢迎页专用 --- */
#define DP_WELCOME_FG 0xFFE8F2FCu /* 近白浅蓝(欢迎文字) */
/* ===================================================================
* L1 — 语义色值 (Semantic Colors)
*
* 暗色主题(Dark Theme)— Shell / 终端
* 亮色主题(Light Theme)— FirstInit / DSK
* =================================================================== */
/* --- 暗色主题 --- */
#define DS_DARK_BG_PRIMARY DP_ABYSS_900 /* 主背景 */
#define DS_DARK_BG_SECONDARY DP_ABYSS_800 /* 卡片/面板背景 */
#define DS_DARK_BG_TERTIARY DP_ABYSS_700 /* 悬停/输入框 */
#define DS_DARK_BG_INSET DP_ABYSS_600 /* 凹陷区域 */
#define DS_DARK_TEXT_PRIMARY DP_NEUTRAL_700 /* 主文字 */
#define DS_DARK_TEXT_SECONDARY DP_NEUTRAL_500 /* 次级文字 */
#define DS_DARK_TEXT_DIM DP_NEUTRAL_300 /* 注释/禁用 */
#define DS_DARK_TEXT_INVERT DP_ABYSS_900 /* 反色文字(亮背景上) */
#define DS_DARK_ACCENT DP_SEAL_500 /* 主强调 */
#define DS_DARK_ACCENT_LIGHT DP_SEAL_300 /* 高亮强调 */
#define DS_DARK_ACCENT_GLOW DP_SEAL_100 /* 发光 */
#define DS_DARK_DECORATIVE DP_ARC_500 /* 装饰色 */
#define DS_DARK_DECORATIVE_L DP_ARC_300 /* 浅装饰 */
#define DS_DARK_BORDER DP_ABYSS_500 /* 边框 */
#define DS_DARK_BORDER_FOCUS DP_SEAL_300 /* 焦点边框 */
#define DS_DARK_DIVIDER DP_ABYSS_600 /* 分隔线 */
#define DS_DARK_CURSOR DP_SEAL_500 /* 光标 */
#define DS_DARK_PROMPT DP_SEAL_300 /* 提示符 */
#define DS_DARK_SELECTION_BG DP_SEAL_900 /* 选中背景 */
/* --- 亮色主题 --- */
#define DS_LIGHT_BG_PRIMARY DP_NEUTRAL_900 /* 主背景(白) */
#define DS_LIGHT_BG_SECONDARY DP_NEUTRAL_700 /* 卡片背景 */
#define DS_LIGHT_BG_TERTIARY DP_NEUTRAL_600 /* 悬停 */
#define DS_LIGHT_BG_INSET DP_NEUTRAL_500 /* 输入框 */
#define DS_LIGHT_TEXT_PRIMARY DP_ABYSS_900 /* 主文字 */
#define DS_LIGHT_TEXT_SECONDARY DP_ABYSS_500 /* 次级 */
#define DS_LIGHT_TEXT_DIM DP_NEUTRAL_300 /* 注释 */
#define DS_LIGHT_TEXT_INVERT DP_NEUTRAL_900 /* 反色 */
#define DS_LIGHT_ACCENT DP_SEAL_700 /* 主强调(亮色上更深) */
#define DS_LIGHT_ACCENT_LIGHT DP_SEAL_500
#define DS_LIGHT_DECORATIVE DP_ARC_700
#define DS_LIGHT_BORDER DP_NEUTRAL_400
#define DS_LIGHT_BORDER_FOCUS DP_SEAL_500
/* ===================================================================
* 排版规范 (Typography)
* =================================================================== */
/* 字体尺寸族 */
#define DU_FONT_SIZE_XS 10u /* 辅助标注 */
#define DU_FONT_SIZE_SM 14u /* 次要说明 */
#define DU_FONT_SIZE_MD 18u /* 正文(Consolas 18px) */
#define DU_FONT_SIZE_LG 24u /* 小标题 */
#define DU_FONT_SIZE_XL 32u /* 标题 */
#define DU_FONT_SIZE_2XL 48u /* 大标题 / 欢迎文字 */
/* ASCII 位图字体参数(与 ascii_bitmaps.c 对应) */
#define DU_ASCII_CELL_W 11u
#define DU_ASCII_CELL_H 18u
#define DU_ASCII_STEP 12u /* 字符间距(含 1px gap) */
#define DU_ASCII_LINE_H 20u /* 行高(含 2px 行间距) */
/* ===================================================================
* 间距体系 (Spacing)
* =================================================================== */
#define DU_SPACE_XXS 2u
#define DU_SPACE_XS 4u
#define DU_SPACE_SM 8u
#define DU_SPACE_MD 16u
#define DU_SPACE_LG 24u
#define DU_SPACE_XL 32u
#define DU_SPACE_2XL 48u
#define DU_SPACE_3XL 64u
/* ===================================================================
* 圆角体系 (Border Radius) — "Sealed Arc" 签名元素
* =================================================================== */
#define DU_RADIUS_NONE 0u
#define DU_RADIUS_SM 4u /* 小按钮、标签 */
#define DU_RADIUS_MD 0u /* 卡片、输入框(性能优化:改为直角) */
#define DU_RADIUS_LG 0u /* 大卡片、面板(性能优化:改为直角) */
#define DU_RADIUS_XL 16u /* 模态框 */
#define DU_RADIUS_FULL 9999u /* 完全圆角(药丸形) */
/* ===================================================================
* 边框体系 (Border)
* =================================================================== */
#define DU_BORDER_THIN 1u
#define DU_BORDER_STD 2u
#define DU_BORDER_THICK 3u
/* ===================================================================
* 动画参数 (Animation)
* =================================================================== */
/* 帧延迟(busy-wait 循环次数 ≈ 毫秒级,需根据 CPU 速度校准) */
#define DU_ANIM_DELAY_SLOW 200000u /* 慢速过渡 */
#define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */
#define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */
/* 弧线动画参数(DSK spinner 专用 — Sealed Arc Comet 设计) */
#define DU_SPINNER_RADIUS 48u
#define DU_SPINNER_THICKNESS 5u
#define DU_SPINNER_HEAD_WIDTH 40u /* 弧头宽度(度) */
#define DU_SPINNER_TAIL_LEN 220u /* 拖尾长度(度) */
#define DU_SPINNER_SPEED 4u /* 每帧旋转度数 */
#define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */
/* 渐变动画步进 */
#define DU_FADE_STEPS 32u /* 淡入/淡出步数 */
#define DU_FADE_STEP_DELAY 8000u /* 每步延迟 */
/* ===================================================================
* 组件尺寸 (Component Sizes)
* =================================================================== */
#define DU_BUTTON_HEIGHT 36u
#define DU_INPUT_HEIGHT 32u
#define DU_CARD_PADDING_X 24u
#define DU_CARD_PADDING_Y 16u
#define DU_CARD_MIN_WIDTH 200u
#define DU_CARD_MAX_WIDTH 480u
#define DU_CURSOR_SIZE 24u /* 鼠标光标边长 */
#define DU_NOTIFICATION_H 40u
/* ===================================================================
* 渲染上下文 (Render Context)
*
* 所有绘制函数通过此上下文访问 framebuffer,避免全局状态。
* =================================================================== */
typedef struct {
du_u32 *fb; /* framebuffer 基址 */
du_u64 width; /* 像素宽 */
du_u64 height; /* 像素高 */
du_u64 pitch; /* 字节行宽 */
} du_context;
/* 初始化渲染上下文 */
static inline void du_context_init(du_context *ctx, du_u64 fb_addr,
du_u64 w, du_u64 h, du_u64 pitch) {
ctx->fb = (du_u32 *)(du_u64)fb_addr;
ctx->width = w;
ctx->height = h;
ctx->pitch = pitch;
}
/* ===================================================================
* L2 — 绘制原语 (Rendering Primitives)
* =================================================================== */
/* 颜色混合:alpha 范围 0–255 */
static inline du_u32 du_blend(du_u32 bg, du_u32 fg, du_u32 alpha) {
du_u32 na = 256 - alpha;
du_u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
du_u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
du_u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
return 0xFF000000u | (b << 16) | (g << 8) | r;
}
/* 线性插值两色 */
static inline du_u32 du_lerp_color(du_u32 c1, du_u32 c2, du_u32 t) {
return du_blend(c1, c2, t);
}
/* 渐变背景:顶色 → 底色,按 Y 坐标插值 */
static inline du_u32 du_gradient_v(du_u32 top_color, du_u32 bot_color,
du_i64 y, du_i64 total_h) {
if (total_h <= 1) return top_color;
if (y < 0) y = 0;
if (y >= total_h) y = total_h - 1;
du_u32 alpha = (du_u32)(((du_u64)y * 255ULL) / ((du_u64)total_h - 1));
return du_blend(top_color, bot_color, alpha);
}
/* 单像素写(带边界检查) */
static inline void du_pixel(du_context *ctx, du_i64 x, du_i64 y, du_u32 color) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return;
((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x] = color;
}
/* 读取像素(带边界检查) */
static inline du_u32 du_pixel_read(du_context *ctx, du_i64 x, du_i64 y) {
if (x < 0 || (du_u64)x >= ctx->width || y < 0 || (du_u64)y >= ctx->height) return 0;
return ((du_u32 *)((du_u8 *)ctx->fb + (du_u64)y * ctx->pitch))[(du_u64)x];
}
/* 矩形填充 */
static inline void du_fill_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 渐变矩形填充(垂直渐变) */
static inline void du_fill_rect_gradient(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 top_color, du_u32 bot_color) {
for (du_i64 r = 0; r < h; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 color = du_gradient_v(top_color, bot_color, r, h);
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < w; c++) {
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = color;
}
}
}
/* 全屏渐变背景 */
static inline void du_fill_bg_gradient(du_context *ctx,
du_u32 top_color, du_u32 bot_color) {
du_fill_rect_gradient(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height,
top_color, bot_color);
}
/* 全屏纯色背景 */
static inline void du_fill_bg_solid(du_context *ctx, du_u32 color) {
du_fill_rect(ctx, 0, 0, (du_i64)ctx->width, (du_i64)ctx->height, color);
}
/* ===================================================================
* 圆角矩形 (Rounded Rectangle) — "Sealed Arc" 核心形状
* =================================================================== */
/* 绘制圆角矩形边框(单像素宽) */
static inline void du_rect_outline(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
/* 四个角的圆弧 + 四条直线 */
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 顶线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y, color);
}
/* 底线 */
for (du_i64 c = r; c < w - r; c++) {
du_pixel(ctx, x + c, y + h - 1, color);
}
/* 左线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x, y + rr, color);
}
/* 右线 */
for (du_i64 rr = r; rr < h - r; rr++) {
du_pixel(ctx, x + w - 1, y + rr, color);
}
/* 四个圆角 */
for (du_i64 dy = 0; dy <= r; dy++) {
for (du_i64 dx = 0; dx <= r; dx++) {
du_i64 d2 = dx * dx + dy * dy;
if (d2 <= r * r && d2 > (r - 1) * (r - 1)) {
/* 左上 */
du_pixel(ctx, x + r - dx, y + r - dy, color);
/* 右上 */
du_pixel(ctx, x + w - 1 - r + dx, y + r - dy, color);
/* 左下 */
du_pixel(ctx, x + r - dx, y + h - 1 - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - 1 - r + dx, y + h - 1 - r + dy, color);
}
}
}
}
/* 填充圆角矩形 */
static inline void du_fill_rounded_rect(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 color, du_u32 radius) {
du_i64 r = (du_i64)radius;
if (r > w / 2) r = w / 2;
if (r > h / 2) r = h / 2;
/* 中间区域(不含角) */
du_fill_rect(ctx, x + r, y, w - 2 * r, h, color);
/* 左侧矩形 */
du_fill_rect(ctx, x, y + r, r, h - 2 * r, color);
/* 右侧矩形 */
du_fill_rect(ctx, x + w - r, y + r, r, h - 2 * r, color);
/* 四个圆角填充 */
for (du_i64 dy = 0; dy < r; dy++) {
for (du_i64 dx = 0; dx < r; dx++) {
if (dx * dx + dy * dy <= r * r) {
/* 左上 */
du_pixel(ctx, x + r - 1 - dx, y + r - 1 - dy, color);
/* 右上 */
du_pixel(ctx, x + w - r + dx, y + r - 1 - dy, color);
/* 左下 */
du_pixel(ctx, x + r - 1 - dx, y + h - r + dy, color);
/* 右下 */
du_pixel(ctx, x + w - r + dx, y + h - r + dy, color);
}
}
}
}
/* 带发光效果的圆角矩形边框(模拟 UTSM 封缄发光) */
static inline void du_rect_glow(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 glow_color, du_u32 radius,
du_i64 glow_width) {
/* 从外到内逐层降低透明度 */
for (du_i64 i = glow_width; i >= 1; i--) {
du_u32 alpha = (du_u32)(40 + (glow_width - i) * 50 / glow_width);
du_u32 color = du_blend(DP_ABYSS_900, glow_color, alpha);
du_rect_outline(ctx, x - i, y - i, w + 2 * i, h + 2 * i, color, radius + (du_u32)i);
}
/* 核心边框 */
du_rect_outline(ctx, x, y, w, h, glow_color, radius);
}
/* ===================================================================
* 分隔线 (Divider)
* =================================================================== */
static inline void du_divider_h(du_context *ctx, du_i64 x, du_i64 y,
du_i64 width, du_u32 color) {
du_fill_rect(ctx, x, y, width, 1, color);
}
static inline void du_divider_v(du_context *ctx, du_i64 x, du_i64 y,
du_i64 height, du_u32 color) {
du_fill_rect(ctx, x, y, 1, height, color);
}
/* ===================================================================
* ASCII 字符渲染(需要外部提供 g_ascii 位图数组)
*
* 使用方法:包含 ascii_bitmaps.c 后调用 du_draw_char / du_draw_string
* =================================================================== */
/* ASCII 位图数组 — 由 ascii_bitmaps.c 定义(static),在包含该 .c 的编译单元内可见 */
/* 渲染单个 ASCII 字符(灰度位图 + 前景色 alpha 混合) */
static inline void du_draw_char(du_context *ctx, char ch,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg) {
du_u32 idx = (du_u32)(ch - ' ');
if (idx > 94) idx = 0;
const du_u8 *glyph = g_ascii[idx];
du_i64 gw = g_ascii_w;
du_i64 gh = g_ascii_h;
for (du_i64 r = 0; r < gh; r++) {
du_i64 yy = y + r;
if (yy < 0 || (du_u64)yy >= ctx->height) continue;
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 c = 0; c < gw; c++) {
du_u8 a = glyph[r * gw + c];
if (a == 0) continue;
du_i64 xx = x + c;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
line[(du_u64)xx] = (a == 255) ? fg : du_blend(bg, fg, a);
}
}
}
/* 渲染字符串(不换行,无自动折行) */
static inline void du_draw_string(du_context *ctx, const char *s,
du_i64 x, du_i64 y,
du_u32 fg, du_u32 bg,
du_u32 step) {
du_i64 cx = x;
while (*s) {
du_draw_char(ctx, *s, cx, y, fg, bg);
cx += (du_i64)step;
s++;
}
}
/* ===================================================================
* 组件令牌 — 按钮 (Button)
* =================================================================== */
typedef struct {
du_u32 bg_normal; /* 正常背景 */
du_u32 bg_hover; /* 悬停背景 */
du_u32 bg_active; /* 按下背景 */
du_u32 fg; /* 文字色 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 radius; /* 圆角半径 */
du_u32 height; /* 高度 */
du_u32 padding_x; /* 水平内边距 */
} du_button_style;
/* 暗色主题按钮样式 */
#define DU_BTN_DARK_PRIMARY { \
DS_DARK_ACCENT, DS_DARK_ACCENT_LIGHT, DP_SEAL_700, \
DP_NEUTRAL_900, DP_SEAL_300, DP_SEAL_100, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
#define DU_BTN_DARK_SECONDARY { \
DS_DARK_BG_TERTIARY, DS_DARK_BG_INSET, DS_DARK_BG_INSET, \
DS_DARK_TEXT_PRIMARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DU_RADIUS_MD, DU_BUTTON_HEIGHT, DU_SPACE_LG \
}
/* ===================================================================
* 组件令牌 — 卡片 (Card)
* =================================================================== */
typedef struct {
du_u32 bg; /* 卡片背景 */
du_u32 border; /* 边框色 */
du_u32 border_focus; /* 焦点边框色 */
du_u32 shadow_color; /* 阴影色 */
du_u32 radius; /* 圆角半径 */
du_u32 padding_x; /* 水平内边距 */
du_u32 padding_y; /* 垂直内边距 */
du_u32 border_width; /* 边框宽度 */
} du_card_style;
#define DU_CARD_DARK { \
DS_DARK_BG_SECONDARY, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DP_ABYSS_900, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
#define DU_CARD_LIGHT { \
DS_LIGHT_BG_SECONDARY, DS_LIGHT_BORDER, DS_LIGHT_BORDER_FOCUS, \
DP_NEUTRAL_300, DU_RADIUS_LG, DU_CARD_PADDING_X, DU_CARD_PADDING_Y, DU_BORDER_STD \
}
/* ===================================================================
* 组件令牌 — 输入框 (Input)
* =================================================================== */
typedef struct {
du_u32 bg;
du_u32 bg_focus;
du_u32 fg;
du_u32 placeholder;
du_u32 border;
du_u32 border_focus;
du_u32 cursor;
du_u32 radius;
du_u32 height;
} du_input_style;
#define DU_INPUT_DARK { \
DS_DARK_BG_INSET, DS_DARK_BG_TERTIARY, DS_DARK_TEXT_PRIMARY, \
DS_DARK_TEXT_DIM, DS_DARK_BORDER, DS_DARK_BORDER_FOCUS, \
DS_DARK_CURSOR, DU_RADIUS_MD, DU_INPUT_HEIGHT \
}
/* ===================================================================
* 组件令牌 — 通知栏 (Notification)
* =================================================================== */
typedef enum {
DU_NOTIFY_INFO,
DU_NOTIFY_SUCCESS,
DU_NOTIFY_WARNING,
DU_NOTIFY_ERROR
} du_notify_type;
static inline du_u32 du_notify_color(du_notify_type type) {
switch (type) {
case DU_NOTIFY_SUCCESS: return DP_SUCCESS;
case DU_NOTIFY_WARNING: return DP_WARNING;
case DU_NOTIFY_ERROR: return DP_ERROR;
default: return DP_INFO;
}
}
/* ===================================================================
* 组件绘制函数 (Component Drawing)
* =================================================================== */
/* 绘制按钮 */
static inline void du_draw_button(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_button_style *style,
const char *label, int focused) {
du_u32 bg = style->bg_normal;
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
}
/* 填充圆角矩形背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
/* 边框 */
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字居中 */
du_i64 label_len = 0;
while (label[label_len]) label_len++;
du_i64 text_w = label_len * (du_i64)DU_ASCII_STEP;
du_i64 text_x = x + (w - text_w) / 2;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, label, text_x, text_y, style->fg, bg, DU_ASCII_STEP);
}
/* 绘制卡片 */
static inline void du_draw_card(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
const du_card_style *style, int focused) {
du_u32 border = style->border;
if (focused) {
border = style->border_focus;
/* 焦点发光效果 */
du_rect_glow(ctx, x - 2, y - 2, w + 4, h + 4,
border, style->radius + 2, 3);
}
du_fill_rounded_rect(ctx, x, y, w, h, style->bg, style->radius);
du_rect_outline(ctx, x, y, w, h, border, style->radius);
}
/* 绘制输入框 */
static inline void du_draw_input(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
const du_input_style *style,
const char *text, int cursor_pos,
int focused) {
du_u32 bg = focused ? style->bg_focus : style->bg;
du_u32 border = focused ? style->border_focus : style->border;
du_fill_rounded_rect(ctx, x, y, w, (du_i64)style->height, bg, style->radius);
du_rect_outline(ctx, x, y, w, (du_i64)style->height, border, style->radius);
/* 文字 */
du_i64 text_x = x + (du_i64)DU_SPACE_SM;
du_i64 text_y = y + ((du_i64)style->height - (du_i64)DU_ASCII_CELL_H) / 2;
du_u32 fg = text[0] ? style->fg : style->placeholder;
du_draw_string(ctx, text, text_x, text_y, fg, bg, DU_ASCII_STEP);
/* 光标 */
if (focused && cursor_pos >= 0) {
du_i64 cx = text_x + (du_i64)cursor_pos * (du_i64)DU_ASCII_STEP;
du_fill_rect(ctx, cx, text_y, 2, (du_i64)DU_ASCII_CELL_H, style->cursor);
}
}
/* 绘制通知栏 */
static inline void du_draw_notification(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w,
du_notify_type type,
const char *message) {
du_u32 accent = du_notify_color(type);
/* 半透明背景 */
du_fill_rounded_rect(ctx, x, y, w, (du_i64)DU_NOTIFICATION_H,
DP_ABYSS_800, DU_RADIUS_MD);
/* 左侧色条 */
du_fill_rect(ctx, x, y + 4, 4, (du_i64)DU_NOTIFICATION_H - 8, accent);
/* 文字 */
du_i64 text_y = y + ((du_i64)DU_NOTIFICATION_H - (du_i64)DU_ASCII_CELL_H) / 2;
du_draw_string(ctx, message, x + (du_i64)DU_SPACE_MD, text_y,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* 绘制进度条 */
static inline void du_draw_progress(du_context *ctx,
du_i64 x, du_i64 y, du_i64 w, du_i64 h,
du_u32 progress_0_255) {
/* 背景 */
du_fill_rounded_rect(ctx, x, y, w, h, DP_ABYSS_700, DU_RADIUS_FULL);
/* 填充 */
du_i64 fill_w = (w * (du_i64)progress_0_255) / 255;
if (fill_w > 0) {
du_fill_rounded_rect(ctx, x, y, fill_w, h, DP_SEAL_500, DU_RADIUS_FULL);
}
}
/* ===================================================================
* Spinner(加载弧) — DSK 启动加载动画
*
* 使用 "Sealed Arc Comet" 单弧彗星尾设计:
* 一个明亮的弧头 + 渐变拖尾,颜色从弧头色过渡到拖尾色。
* =================================================================== */
/* 默认弧色:[0]=弧头色(亮蓝), [1]=拖尾色(紫) */
#define DU_SPINNER_ARCS { \
DP_SEAL_300, DP_SEAL_700, DP_SEAL_500, DP_SEAL_100 \
}
/* ===================================================================
* 主题切换辅助 (Theme Switch)
* =================================================================== */
typedef enum {
DU_THEME_DARK,
DU_THEME_LIGHT
} du_theme;
/* 根据主题获取对应的背景色 */
static inline du_u32 du_theme_bg(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_BG_PRIMARY : DS_LIGHT_BG_PRIMARY;
}
/* 根据主题获取对应的文字色 */
static inline du_u32 du_theme_text(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_TEXT_PRIMARY : DS_LIGHT_TEXT_PRIMARY;
}
/* 根据主题获取对应的强调色 */
static inline du_u32 du_theme_accent(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_ACCENT : DS_LIGHT_ACCENT;
}
/* ===================================================================
* L0+ — Winux-Kate 调色板 (eDEX-UI Neon Cyber Theme)
*
* 源自 D:\Code\Winux-Kate\src\styles\theme.css
* 风格:深蓝黑底 + 青色霓虹 + 绿色辅助 + 扫描线纹理
* =================================================================== */
/* --- 主色调:深空蓝黑 (Deep Space Black) --- */
#define DP_KATE_BG 0xFF02040Au /* --bg: #02040a */
#define DP_KATE_BG2 0xFF050A18u /* --bg2: #050a18 */
#define DP_KATE_PANEL 0xB20A1228u /* --panel: rgba(10,18,40,0.72) 半透明 */
#define DP_KATE_PANEL_SOLID 0xFF0A1228u /* 不透明版本(用于无 alpha 混合场景) */
/* --- 强调色:霓虹青 (Neon Cyan) --- */
#define DP_KATE_ACCENT 0xFF00E5FFu /* --accent: #00e5ff */
#define DP_KATE_ACCENT_DIM 0x2E00E5FFu /* --accent-dim: rgba(0,229,255,0.18) */
#define DP_KATE_ACCENT_DIM_SOLID 0xFF2A4A5Au /* 不透明近似(提升亮度改善对比度) */
#define DP_KATE_PANEL_BORDER 0x5900E5FFu /* --panel-border: rgba(0,229,255,0.35) */
#define DP_KATE_PANEL_BORDER_SOLID 0xFF2A6070u /* 不透明近似(提升亮度) */
/* --- 辅助色:霓虹绿 (Neon Green) --- */
#define DP_KATE_ACCENT2 0xFF39FF14u /* --accent2: #39ff14 */
/* --- 文本色 --- */
#define DP_KATE_TEXT 0xFFCFEFFBu /* --text: #cfeffb */
#define DP_KATE_TEXT_DIM 0xFF6F8AA8u /* --text-dim: #6f8aa8 */
/* --- 状态色 --- */
#define DP_KATE_WARN 0xFFFFCC00u /* --warn: #ffcc00 */
#define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
/* ===================================================================
* L1 — 桌面语义色值(白色调主题,去 winux-Kate 深色风)
* 底色白/浅灰,组件白卡片 + 浅灰描边,文字深灰,强调色深板岩。
* =================================================================== */
#define KS_BG_PRIMARY 0xFFF3F4F6u /* 页面底:浅灰 */
#define KS_BG_SECONDARY 0xFFFFFFFFu /* 面板:白 */
#define KS_BG_TERTIARY 0xFFE9ECF0u /* 三级底:稍深浅灰 */
#define KS_TEXT_PRIMARY 0xFF1A1A1Au /* 主文字:近黑 */
#define KS_TEXT_DIM 0xFF6B7280u /* 次文字:中灰 */
#define KS_TEXT_INVERT 0xFFFFFFFFu /* accent 上的文字:白 */
#define KS_ACCENT 0xFF111827u /* 强调:深板岩(白底上高对比) */
#define KS_ACCENT2 0xFF4B5563u /* 次强调:中板岩 */
#define KS_ACCENT_DIM 0xFFE5E7EBu /* 强调淡底:浅灰 */
#define KS_BORDER 0xFFD1D5DBu /* 边框:浅灰 */
#define KS_BORDER_FOCUS 0xFF111827u /* 聚焦边框:深板岩 */
#define KS_BORDER_DIM 0xFFE5E7EBu /* 暗边框:极浅灰 */
#define KS_DANGER 0xFFDC2626u /* 红 */
#define KS_WARN 0xFFD97706u /* 琥珀 */
/* ===================================================================
* Winux-Kate 布局常量(源自 theme.css / pages.css)
* =================================================================== */
#define KATE_TOPBAR_H 34 /* .topbar height */
#define KATE_STATUSBAR_H 32 /* .statusbar height */
#define KATE_TASKBAR_H 40 /* .taskbar height */
#define KATE_PANEL_HEADER_H 24 /* .panel-header */
#define KATE_TITLEBAR_H 28 /* 窗口标题栏 */
#define KATE_ICON_W 104 /* .desktop-icon width */
#define KATE_ICON_H 90 /* .desktop-icon height (含 label) */
#define KATE_ICON_IMG 40 /* .di-img / .di-fallback */
#define KATE_ICON_GAP 18 /* .desktop-grid gap */
#define KATE_ICON_PAD 24 /* .desktop-canvas padding */
#define KATE_TB_ITEM_MAX_W 200 /* .tb-item max-width */
#define KATE_PAGE_PAD 8 /* 页面 padding */
/* ===================================================================
* Winux-Kate 特效函数
* =================================================================== */
/* 绘制扫描线纹理(已禁用以提升性能)
* 原效果:在指定矩形区域内叠加半透明青色水平细线 */
static inline void du_kate_scanlines(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h) {
/* 性能优化:禁用扫描线纹理,减少约50%的像素处理开销 */
(void)ctx; (void)x; (void)y; (void)w; (void)h;
}
/* 绘制带光晕的矩形边框(box-shadow 效果)
* 外发光:在矩形外围绘制渐变青色光晕 */
static inline void du_kate_glow_border(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 glow_color) {
/* 2px 渐变光晕,alpha 混合到实际背景(逐像素读-混合-写) */
for (du_i64 i = 2; i >= 1; i--) {
du_u32 alpha = (i == 2) ? 50 : 100;
/* 上下边 */
for (du_i64 c = -i; c < w + i; c++) {
du_i64 xt = x + c;
du_pixel(ctx, xt, y - i,
du_blend(du_pixel_read(ctx, xt, y - i), glow_color, alpha));
du_pixel(ctx, xt, y + h + i - 1,
du_blend(du_pixel_read(ctx, xt, y + h + i - 1), glow_color, alpha));
}
/* 左右边 */
for (du_i64 r = -i; r < h + i; r++) {
du_i64 yt = y + r;
du_pixel(ctx, x - i, yt,
du_blend(du_pixel_read(ctx, x - i, yt), glow_color, alpha));
du_pixel(ctx, x + w + i - 1, yt,
du_blend(du_pixel_read(ctx, x + w + i - 1, yt), glow_color, alpha));
}
}
}
/* 绘制 Winux-Kate 面板(.panel 样式)
* - 半透明深蓝背景
* - 青色边框
* - 内外光晕
* - 扫描线纹理
* - 可选标题栏 */
static inline void du_kate_panel(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, const char *header) {
/* 1. 背景 */
du_fill_rounded_rect(ctx, x, y, w, h, KS_BG_SECONDARY, 2);
/* 2. 边框 */
du_rect_outline(ctx, x, y, w, h, KS_BORDER, 2);
/* 3. 外光晕 */
du_kate_glow_border(ctx, x, y, w, h, KS_ACCENT);
/* 4. 扫描线纹理 */
du_kate_scanlines(ctx, x + 1, y + 1, w - 2, h - 2);
/* 5. 标题栏 */
if (header) {
du_fill_rect(ctx, x, y, w, KATE_PANEL_HEADER_H, 0xFF062838u); /* 深青色标题栏 */
du_divider_h(ctx, x, y + KATE_PANEL_HEADER_H, w, KS_BORDER);
/* 标题文字:青色,大写,letter-spacing */
du_draw_string(ctx, header, x + 10, y + (KATE_PANEL_HEADER_H - DU_ASCII_LINE_H) / 2 + 2,
KS_ACCENT, 0xFF062838u, DU_ASCII_STEP);
/* 标题前的小绿点 */
du_fill_rect(ctx, x + 4, y + KATE_PANEL_HEADER_H / 2 - 3, 6, 6, KS_ACCENT2);
}
}
/* 绘制 Winux-Kate 顶部栏(.topbar 样式)
* - 34px 高
* - 渐变背景
* - 底部边框
* - 品牌名 + 时钟槽位 */
static inline void du_kate_topbar(du_context *ctx, du_i64 w,
const char *brand, const char *clock) {
/* 渐变背景:左青色微亮 → 右深色 */
du_fill_rect_gradient(ctx, 0, 0, w, KATE_TOPBAR_H,
0xFF0A3050u, 0xFF02040Au);
/* 底部分隔线 */
du_divider_h(ctx, 0, KATE_TOPBAR_H, w, KS_BORDER);
/* 品牌名(青色,加粗,letter-spacing 4px)*/
if (brand) {
du_draw_string(ctx, brand, 12, (KATE_TOPBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_ACCENT, 0, DU_ASCII_STEP);
}
/* 时钟(右侧,绿色)*/
if (clock) {
du_i64 cl = 0;
while (clock[cl]) cl++;
du_draw_string(ctx, clock, w - 12 - cl * DU_ASCII_STEP,
(KATE_TOPBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_ACCENT2, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 状态栏(.statusbar 样式)
* - 32px 高
* - 顶部边框
* - 半透明青色背景 */
static inline void du_kate_statusbar(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, const char *status_text) {
du_fill_rect(ctx, x, y, w, KATE_STATUSBAR_H, 0xFF051828u);
du_divider_h(ctx, x, y, w, KS_BORDER);
if (status_text) {
du_draw_string(ctx, status_text, x + 12,
y + (KATE_STATUSBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 桌面图标(.desktop-icon 样式)
* - 104px 宽
* - 40x40 图标区域
* - 下方文字标签
* - 选中态:青色边框 + 光晕 */
static inline void du_kate_desktop_icon(du_context *ctx, du_i64 x, du_i64 y,
const char *label, int selected) {
/* 悬停/选中背景 */
if (selected) {
du_fill_rect(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT_DIM);
du_rect_outline(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT, 1);
du_kate_glow_border(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT);
}
/* 图标占位区域(40x40 居中)*/
du_i64 ix = x + (KATE_ICON_W - KATE_ICON_IMG) / 2;
du_i64 iy = y + 10;
du_rect_outline(ctx, ix, iy, KATE_ICON_IMG, KATE_ICON_IMG, KS_BORDER, 1);
/* 标签 */
if (label) {
du_i64 ll = 0;
while (label[ll]) ll++;
du_i64 lx = x + (KATE_ICON_W - ll * DU_ASCII_STEP) / 2;
if (lx < x + 2) lx = x + 2;
du_draw_string(ctx, label, lx, y + KATE_ICON_IMG + 18,
KS_TEXT_PRIMARY, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 任务栏项(.tb-item 样式) */
static inline void du_kate_tb_item(du_context *ctx, du_i64 x, du_i64 y,
const char *title, int active) {
du_i64 tl = 0;
while (title[tl]) tl++;
du_i64 w = tl * DU_ASCII_STEP + 24;
if (w > KATE_TB_ITEM_MAX_W) w = KATE_TB_ITEM_MAX_W;
du_u32 fg = active ? KS_ACCENT : KS_TEXT_PRIMARY;
if (active) {
du_fill_rect(ctx, x, y, w, 28, KS_ACCENT_DIM);
}
du_rect_outline(ctx, x, y, w, 28, KS_BORDER, 1);
if (active) {
du_kate_glow_border(ctx, x, y, w, 28, KS_ACCENT);
}
du_draw_string(ctx, title, x + 8, y + (28 - DU_ASCII_LINE_H) / 2 + 1,
fg, 0, DU_ASCII_STEP);
}
/* 绘制 Winux-Kate 按钮 (.btn 样式) */
static inline void du_kate_button(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, const char *label,
int hover) {
du_u32 fg = KS_ACCENT;
if (hover) {
du_fill_rect(ctx, x, y, w, h, KS_ACCENT_DIM);
}
du_rect_outline(ctx, x, y, w, h, KS_BORDER, 1);
if (hover) {
du_kate_glow_border(ctx, x, y, w, h, KS_ACCENT);
}
du_i64 ll = 0;
while (label[ll]) ll++;
du_i64 lx = x + (w - ll * DU_ASCII_STEP) / 2;
du_draw_string(ctx, label, lx, y + (h - DU_ASCII_LINE_H) / 2 + 1,
fg, hover ? KS_ACCENT_DIM : KS_BG_SECONDARY, DU_ASCII_STEP);
}
#endif /* DESHAB_UI_H */
-20
View File
@@ -1,20 +0,0 @@
#ifndef UTSM_DIRTY_H
#define UTSM_DIRTY_H
#include <utsm/types.h>
typedef struct {
u32 dirty_count;
u32 shard_id;
u64 page_base;
u64 page_count;
u64 last_checkpoint_epoch;
u64 *bitmap;
} utsm_dirty_shard;
struct utsm_segment_desc;
void utsm_dirty_init(struct utsm_segment_desc *desc, u64 page_count);
void utsm_mark_dirty(struct utsm_segment_desc *desc, u64 offset, u64 len);
#endif
-282
View File
@@ -1,282 +0,0 @@
#ifndef UTSM_DKM_H
#define UTSM_DKM_H
#include <utsm/types.h>
#include <utsm/dma.h>
#include <utsm/block.h>
#include <utsm/net.h>
#include <utsm/paging.h>
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
#define DKM_KERNEL_API_VERSION 1u
#define DKM_CLASS_PLATFORM 1u
#define DKM_CLASS_BUS 2u
#define DKM_CLASS_INTERRUPT 3u
#define DKM_CLASS_TIMER 4u
#define DKM_CLASS_CONSOLE 5u
#define DKM_CLASS_STORAGE 6u
#define DKM_CLASS_FS 7u
#define DKM_CLASS_NET 8u
#define DKM_CLASS_INPUT 9u
#define DKM_CLASS_GPU 10u
#define DKM_CLASS_MISC 11u
#define DKM_F_REQUIRED (1u << 0)
#define DKM_F_BOOT_MODULE (1u << 1)
#define DKM_F_NO_UNLOAD (1u << 2)
#define DKM_F_STRONG_RECOVERY (1u << 3)
#define DKM_F_DMA_REQUIRED (1u << 4)
#define DKM_F_EARLY_LOG (1u << 5)
struct dkm_kernel_api;
struct dkm_driver_handle;
/* ===================================================================
* D3: 内核 API 按名导出表(kapi)
*
* "自由也要有地图。" 固定偏移 struct ABI 之外的按名查找层:
* 驱动/用户态可枚举内核服务并按名取函数指针。
* 通过 kernel_api 尾部追加的 ->kapi 指针访问(ABI 兼容)。
* =================================================================== */
typedef struct {
const char *name; /* 如 "log.info"、"dma.alloc_pages" */
void *fn; /* 服务函数指针(签名见对应服务头) */
} dkm_kapi_entry;
typedef struct {
u32 magic; /* DKM_KAPI_MAGIC */
u32 version; /* DKM_KAPI_VERSION */
u32 count; /* entries 数量 */
const dkm_kapi_entry *entries;
/* 按名查找:命中返回 0 并写 out_fn;未命中 -1 */
int (*lookup)(const char *name, void **out_fn);
} dkm_kapi_table;
#define DKM_KAPI_MAGIC 0x4b415049u /* "KAPI" */
#define DKM_KAPI_VERSION 1u
/* ===================================================================
* D6 万物互联:dkm_bus 双向服务总线
*
* kapi 是内核→模块的单向只读导出表;bus 把它双向化:
* 任何模块在 driver_init 里注册 srv.* 服务,任何模块按名消费。
* 命名空间:kern.* 仅内核可注册,srv.* 仅模块可注册(BUS-E01)。
* 失败策略:lookup miss = BUS-E04 panic(硬依赖未满足=编程错误);
* probe miss = 返回 -1(能力缺席=设计内双路径,白名单登记)。
* 生命周期:driver_init = 注册服务;"运行"也是服务(srv.<mod>.run)。
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#define DKM_BUS_MAGIC 0x53425553u /* "SBUS" */
#define DKM_BUS_VERSION 1u
#define DKM_BUS_MAX_SERVICES 128
#define DKM_BUS_MAX_NAME 48
#define DKM_BUS_F_KERNEL 1u /* 内核注册,不可注销 */
#define DKM_BUS_F_NO_UNLOAD 2u /* provider 声明 NO_UNLOAD,注销即 BUS-E05 */
typedef struct dkm_bus_service {
char name[DKM_BUS_MAX_NAME]; /* NUL 终止;必须带命名空间前缀 */
u32 version; /* 服务契约版本,消费方校验 */
u32 flags; /* DKM_BUS_F_* */
void *fn; /* 服务入口,签名由各服务契约定义 */
const struct dkm_driver_desc *provider; /* NULL = 内核;存 desc 而非
* handle——loader 的 handle 是栈
* 局部变量会悬垂,desc 在驱动
* 镜像内存里持久且每驱动唯一 */
} dkm_bus_service;
typedef struct dkm_bus_api {
u32 magic;
u32 version;
/* 注册:name 必须带命名空间前缀且与调用方身份匹配(BUS-E01)、
* 重名 BUS-E02、表满 BUS-E03——全部 panic,成功返回 0。 */
int (*register_service)(const char *name, u32 version, void *fn);
/* 注销:仅 provider 本人可注销;F_KERNEL/F_NO_UNLOAD 或不存在 → BUS-E05。 */
int (*unregister_service)(const char *name);
/* 必存在查找:未命中 → BUS-E04 panic,绝不返回 NULL 伪装成功。 */
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
/* 可选探测:未命中返回 -1;调用方必须显式处理缺席(白名单登记)。 */
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void); /* 诊断 / /dev/services */
const dkm_bus_service *(*at)(u32 index); /* 按 index 枚举 */
} dkm_bus_api;
typedef struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
} dkm_driver_desc;
typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle);
/* DRR 恢复根服务表(Phase8;驱动经 kernel_api->drr 使用) */
typedef struct drr_recovery_api {
void (*report_fault)(const char *reason);
u64 (*heartbeat_get)(void);
void *(*emergency_alloc)(u64 size, u64 alignment);
int (*ckpt_snapshot_dirty)(void);
int (*rollback_pages)(void);
} drr_recovery_api;
/* SAS-R0-PCQ 调度器服务表(Phase7;驱动经 kernel_api->sched 使用) */
typedef struct utsm_sched_api {
u64 (*uptime_ns)(void);
void (*yield)(void);
u32 (*current_task_slot)(void);
int (*task_create)(u32 process_slot, u32 priority, void (*entry)(void *), void *arg);
void (*sleep_ms)(u32 ms);
} utsm_sched_api;
typedef struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
} dkm_log_api;
typedef struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const dkm_dma_api *dma;
const void *vfs;
const dkm_net_api *net;
const void *timer;
const drr_recovery_api *drr; /* 恢复根服务(原占位 void* 改具体类型,布局不变) */
const void *rsdp_address; /* ACPI RSDP physical address */
const void *fb_address; /* framebuffer base */
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response; /* limine_module_response* */
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
u64 hhdm_offset; /* physical → virtual */
const dkm_block_api *block; /* block provider registry */
const dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(尾部追加,ABI 兼容) */
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
/* B7 阶段2: 驱动向 UTSM 注册 LAPIC EOI 钩子(写 LAPIC EOI 寄存器的函数指针)。
* apic.drv 在 LAPIC 接管时注册;idt_handler 的 IRQ 分发末尾调用该钩子,
* 使 vector 0x20-0x2F 的 legacy 线在 IOAPIC 路由下可持续投递。
* 未注册(PIC 路由)时钩子为 NULL,idt_handler 跳过,行为与旧版一致。 */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),尾部追加保持 ABI。
* 池范围 0x40-0xDF;alloc 返回向量号、失败 -1;free 幂等越界忽略。
* 驱动拿到向量后经 irq_register(vector, handler) 直接注册(现有
* 接口已接受向量命名空间,无需新注册 ABI)。 */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
/* M4/Phase7-8: 调度器服务(尾部追加,ABI 兼容;drr 服务见上方原占位字段)。
* 驱动可用:task_create/uptime/sleep/current_task_slot。
* DSK 以 api+0xA8 硬偏移读 block 的约定不受影响(仅尾部追加)。 */
const utsm_sched_api *sched;
/* D3: 按名导出表(尾部追加,ABI 兼容)。驱动/用户态枚举内核服务并
* 按名取函数指针;固定偏移 struct 字段不受影响。 */
const dkm_kapi_table *kapi;
/* D6 万物互联:双向服务总线(尾部追加,ABI 兼容)。kern.* 与 srv.* 按名
* 注册-消费;driver_init = 注册服务的地方。 */
const dkm_bus_api *bus;
} dkm_kernel_api;
typedef enum dkm_driver_state {
DKM_STATE_DISCOVERED = 0,
DKM_STATE_ABI_CHECKED,
DKM_STATE_INITING,
DKM_STATE_ACTIVE,
DKM_STATE_FAILED
} dkm_driver_state;
typedef struct dkm_driver_handle {
const dkm_driver_desc *desc;
dkm_driver_state state;
i32 init_status;
u32 load_stage;
u32 flags;
} dkm_driver_handle;
typedef struct dkm_builtin_driver {
const dkm_driver_desc *desc;
dkm_driver_init_fn init;
dkm_driver_exit_fn exit;
} dkm_builtin_driver;
void dkm_init(void);
/* D4: 设置当前驱动日志颜色上下文(driver_init 前设置,返回后清 0) */
void dkm_log_set_driver(const char *name);
int dkm_load_builtin(const dkm_builtin_driver *driver);
void dkm_scan_boot_modules(void);
void dsm_load_by_manifest(void);
/* P0(万物互联):按名加载。dsm_load_named 严格版(depends 校验强制,供
* kern.dkm.load_by_name 等常规路径);dsm_load_named_boot 顺序豁免版
* (唯一合法调用方 = kernel/main.c VMM 路径 pci+ahci 预加载,先于 apic,
* 否则 guest 陷入 IDTR=0 VM-exit 风暴——设计内豁免,见
* docs/RE/万物互联与服务总线设计.md §6)。
* 签名(ini_config 定义在 kernel/ini_parser.h,此处避免头依赖):
* void dsm_load_named(const ini_config *cfg, const char *name);
* void dsm_load_named_boot(const ini_config *cfg, const char *name);
* 调用方自行前向声明(同 kernel/main.c 既有模式)。 */
void dkm_fill_platform_info(void);
const dkm_kernel_api *dkm_get_kernel_api(void);
/* 服务表实现(drr_stub.c / sched/sched.c 提供,fill 时挂入 kernel_api) */
const struct drr_recovery_api *drr_get_recovery_api(void);
const struct utsm_sched_api *utsm_sched_get_api(void);
/* D6 万物互联:服务总线(dkm/bus.c 提供)。dkm_bus_init 播种 kern.*
* 并挂入 kernel_api(dkm_fill_platform_info 内调用,先于一切驱动加载);
* loader 在 driver_init 前后 set/clear provider 上下文(仿 D4 颜色上下文),
* kern.* 只能在 provider==NULL 时注册。 */
void dkm_bus_init(void);
const dkm_bus_api *dkm_bus_get_api(void);
void dkm_bus_set_current_provider(struct dkm_driver_handle *h);
/* loader 专用:token T 是否已有 "prefix" 或 "prefix.*" 服务注册
* (stage>=4 provides 校验用,DKM-E45)。命中返回 1。 */
int dkm_bus_prefix_registered(const char *prefix);
/* exposed for manifest parser */
struct dkm_symbol_scan {
const void *driver_desc;
const void *driver_init;
const void *driver_exit;
};
int dkm_check_elf64(const void *address, u64 size);
int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out);
int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols);
extern const dkm_builtin_driver dkm_builtin_console_early;
#endif
-20
View File
@@ -1,20 +0,0 @@
#ifndef UTSM_DMA_H
#define UTSM_DMA_H
#include <utsm/types.h>
typedef struct dkm_dma_buffer {
void *virt;
u64 phys;
u64 size;
} dkm_dma_buffer;
typedef struct dkm_dma_api {
int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out);
} dkm_dma_api;
void dma_init(void);
int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out);
const dkm_dma_api *dma_get_api(void);
#endif
-26
View File
@@ -1,26 +0,0 @@
#ifndef UTSM_DMP_H
#define UTSM_DMP_H
#include <utsm/types.h>
typedef struct {
u64 magic;
u32 version;
u32 segment_slot;
u64 fast_base;
u64 fast_limit;
u64 fast_tweak_seed;
u64 fast_key_epoch;
u64 dirty_bitmap_base;
u64 mac_table_base;
u64 last_writer_seq;
u32 last_cpu;
u32 flags;
u64 crc;
} utsm_dmp;
struct utsm_segment_desc;
void utsm_dmp_init(utsm_dmp *dmp, struct utsm_segment_desc *desc, u32 slot);
#endif
-184
View File
@@ -1,184 +0,0 @@
#ifndef UTSM_DRR_H
#define UTSM_DRR_H
#include <utsm/types.h>
#include <utsm/config.h>
/* ===================================================================
* DRR — Dedicated Recovery Root
*
* 专用恢复根,独立于普通调度器和堆分配器。
* 负责:root key、recovery log、checkpoint A/B、Emergency Pool。
*
* 约束:
* - DRR recovery 路径禁止依赖普通堆分配器
* - 普通 OOM 不影响 DRR recovery
* - Emergency Pool 耗尽时进入 system rollback
* =================================================================== */
/* ---- Checkpoint 类型 ---- */
typedef enum {
DRR_CKPT_LIGHT = 0, /* 控制面快照 */
DRR_CKPT_DIRTY_PAGE, /* dirty ciphertext page 快照 */
DRR_CKPT_BOOT /* 启动状态 + A/B slot */
} drr_checkpoint_type;
/* ---- Recovery Log 条目 ---- */
typedef struct {
u32 segment_slot;
u32 flags; /* 0=intent, 1=commit */
u64 offset;
u64 length;
u64 timestamp; /* writer_seq snapshot */
} drr_log_entry;
#define DRR_LOG_FLAG_INTENT 0u
#define DRR_LOG_FLAG_COMMIT 1u
/* ---- Checkpoint Metadata Slot ---- */
typedef struct {
u64 magic; /* DRR_CKPT_MAGIC */
u32 version;
u32 active; /* 0=inactive, 1=active */
drr_checkpoint_type type;
u64 global_epoch;
u64 dirty_page_count;
u64 mac_root; /* MAC root hash of all sealed pages */
u64 segment_count;
u64 crc; /* CRC64 of the above */
} drr_checkpoint_meta;
#define DRR_CKPT_MAGIC 0x445252434B505421ULL /* "DRRCKPT!" */
#define DRR_CKPT_VERSION 1u
/* ---- Emergency Pool ---- */
#define DRR_EMERGENCY_POOL_SIZE (64ULL * 1024ULL) /* 64KB 独立池 */
#define DRR_EMERGENCY_STACK_SIZE 4096ULL
#define DRR_RECOVERY_LOG_SIZE 4096ULL
#define DRR_CKPT_META_BUF_SIZE 4096ULL
#define DRR_CRASH_BUF_SIZE 2048ULL
#define DRR_META_SLAB_SIZE 2048ULL
#define DRR_EMERGENCY_PAGES 4
typedef struct {
u8 pool[DRR_EMERGENCY_POOL_SIZE] __attribute__((aligned(64)));
u64 pool_offset;
u64 pool_limit;
/* 子缓冲区指针(pool 内偏移分配) */
u8 *stack_base;
u64 stack_size;
u64 stack_ptr;
drr_log_entry *log_base;
u32 log_count;
u32 log_capacity;
drr_checkpoint_meta *ckpt_meta_buf;
u8 *crash_buf;
u64 crash_buf_size;
void *meta_slab;
u64 meta_slab_offset;
/* A/B checkpoint slots */
drr_checkpoint_meta ckpt_a;
drr_checkpoint_meta ckpt_b;
u32 active_slot; /* 0=A, 1=B */
/* Root key */
const u64 *root_key;
/* Recovery generation */
u64 recovery_generation;
/* Heartbeat */
u64 heartbeat_counter;
u64 heartbeat_interval;
/* 初始化标记 */
int initialized;
} drr_state;
/* ---- 公共接口 ---- */
void drr_init(void);
/* R3b: FUCK [utsm] 配置接线(须在 drr_init 前调用;0 = 用编译期默认)。
* pool_kb 钳位到编译期静态池上限(Emergency Pool 为编译期数组,运行期只
* 能缩小可用量);recovery_log_size 钳位到池内剩余容量。 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size);
const u64 *drr_get_root_key(void);
u64 drr_recovery_generation(void);
void drr_report_fault(const char *reason);
/* F1: 崩溃归档(crash_buf 写入;返回 0=已归档,-1=DRR 未初始化) */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs);
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len);
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
/* Checkpoint 操作 */
int drr_checkpoint_begin(drr_checkpoint_type type);
int drr_checkpoint_finish(void);
int drr_checkpoint_recover(drr_checkpoint_meta *out_meta);
/* Emergency Pool 分配 */
void *drr_emergency_alloc(u64 size, u64 alignment);
void drr_emergency_reset(void);
/* Heartbeat */
void drr_heartbeat_tick(void);
u64 drr_heartbeat_get(void);
/* CRC64 */
u64 drr_crc64(const void *data, u64 len);
/* ===================================================================
* Phase 8 扩展:真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* (尾部追加,不动既有结构与语义)
* =================================================================== */
/* 快照区条目(快照区第 0 页:头(64B) + 条目数组顺序存放) */
typedef struct {
u32 seg_slot; /* utsm_get_segment 的 slot */
u32 page_index; /* 段内页号 */
u64 cipher_vaddr; /* 段内目标页虚拟地址(HHDM,回滚目标) */
u64 crc; /* 快照数据 CRC64(drr_crc64,区域完整性) */
u32 region_idx; /* 快照数据所在页序号(区域第 1+idx 页) */
u32 used;
/* Phase 9: 真实 MAC(keyed BLAKE2b,绑定 seg/page/epoch) */
u64 key_epoch; /* 快照时刻段 key_epoch(MAC 上下文 + 防跨纪元重放) */
u64 mac; /* 页 MAC(utsm_page_mac,截断 64 位) */
} drr_ckpt_page_entry;
/* 真实脏页快照:扫描全部 ACTIVE 段的 dirty shard 位图,逐脏页复制到
* 快照区(先快照后清位;区满则部分提交且不清位),随后走 A/B checkpoint
* 原子切换。O(dirty_shards + dirty_pages)。
* 返回快照页数(>=0);负数失败(未初始化/快照区不可用)。 */
int drr_ckpt_snapshot_dirty(void);
/* 页级回滚:按 active slot 元数据 + 快照区逐页 CRC 校验后恢复密文页
* 并清除对应脏位;触碰段状态 RECOVERING → ACTIVE,CRC 失败段 POISONED。
* 返回恢复页数(>=0);失败 -2(任一页 CRC 不符)。 */
int drr_rollback_pages(void);
/* 系统级回滚:crash 留痕 + 8042 复位(不返回)。
* Emergency Pool 耗尽 / 双槽皆坏 / 无任务可切换时调用。 */
void drr_system_rollback(const char *reason);
/* 统一 fault 入口:report_fault → 页级回滚 → sched fault 回调(可能不
* 返回);回调缺失且 reboot 使能时 → system rollback。 */
void drr_handle_task_fault(u32 task_slot, const char *reason);
/* 看门狗(独立于调度器:由 tick handler 直接调用 check,非调度任务) */
#define DRR_WATCHDOG_MAX 8
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks);
int drr_watchdog_unregister(u32 task_slot);
void drr_watchdog_kick(u32 task_slot);
void drr_watchdog_check(void);
/* 恢复行为控制:关闭后 fault 只留日志不真重启(selftest 用) */
void drr_set_reboot_enabled(int enabled);
/* 调度器 fault 回调(sched enable 时注入;缺失时 fault → system rollback)。
* DRR 独立性:对调度器只依赖这一个函数指针。 */
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot));
#endif /* UTSM_DRR_H */
-88
View File
@@ -1,88 +0,0 @@
#ifndef UTSM_DSK_H
#define UTSM_DSK_H
#include <utsm/types.h>
#define DSK_BOOT_MAGIC 0x44534B31424F4F54ULL /* "DSK1BOOT" */
#define DSK_BOOT_ABI_VERSION 1u
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
/* FUCK [panic] mode=fallback:DSK/desktop/login/FirstInit 的 *_fatal 记录后返回,
* 调用点走备用路径(见 utsm/panic.h)。未置位 = strict。 */
#define DSK_BOOT_FLAG_PANIC_FALLBACK (1ULL << 4)
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
/* ---- 网络租约信息(netman 写入,DSK 持有存储,经 reserved[7] 指针传递) ----
* reserved[0..6] 已被占用(user.conf/login/dev_mode/pe/lxc/probe),
* 仅 reserved[7] 空闲,故以其存放指向本结构的指针。 */
#define NET_LEASE_MAGIC 0x4E45544Cu /* "NETL" */
#define NET_LEASE_F_VALID (1u << 0) /* ip/gateway/dns 有效 */
#define NET_LEASE_F_STATIC (1u << 1) /* 静态配置(非 DHCP) */
typedef struct net_lease_info {
u32 magic; /* NET_LEASE_MAGIC */
u32 flags; /* NET_LEASE_F_* */
u32 ip; /* guest IP(网络序) */
u32 gateway; /* 网关(网络序) */
u32 dns; /* DNS(网络序) */
u32 server; /* DHCP server(网络序,调试) */
u32 reserved[2];
} net_lease_info;
typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC */
u32 version; /* ABI 版本,当前 1 */
u64 probe_buf_addr; /* probe_entry 缓冲基地址(物理/HHDM 地址) */
u32 probe_cap; /* 缓冲容量(条目数) */
u32 probe_head; /* 当前写入位置(递增取模) */
u32 probe_count; /* 已写入总条目数 */
u32 probe_dropped; /* 因缓冲满而覆盖的条目数 */
u32 instr_enabled; /* g_instr_enabled 当前值 */
u32 instr_probe_enable; /* g_instr_probe_enable 当前值 */
u64 reserved[4];
} probe_info;
typedef struct dsk_boot_context {
u64 magic;
u32 abi_version;
u32 size;
u64 flags;
u64 hhdm_offset;
u64 rsdp_address;
u64 framebuffer_address;
u64 framebuffer_width;
u64 framebuffer_height;
u64 framebuffer_pitch;
u32 framebuffer_bpp;
u32 reserved_fb;
u64 boot_modules_response;
u64 dkm_kernel_api;
u64 dkm_driver_table;
u64 dkm_driver_count;
u64 utsm_state;
u64 drr_state;
u64 memory_map;
u64 memory_map_count;
u64 memory_map_entry_size;
u64 kernel_stack_top;
u64 reserved[8];
} dsk_boot_context;
typedef void (*dsk_entry_fn)(const dsk_boot_context *context);
int dsk_load_and_jump(void);
#endif
-57
View File
@@ -1,57 +0,0 @@
#ifndef UTSM_EPT_H
#define UTSM_EPT_H
#include <utsm/types.h>
/* EPT 页表结构:4 级页表(PML4 / PDPT / PD / PT),每级 512 项,每页 4KB。 */
#define EPT_PAGE_SIZE 4096ULL
#define EPT_ENTRIES 512ULL
/* EPTP 构造:bits [2:0]=memory type, bits [5:3]=walk length-1, bit 6=AD, bits [51:12]=PML4 HPA
* P8.4 Fix: walk length was at bits [7:6] (wrong), should be bits [5:3] per Intel SDM. */
#define EPTP_WALK_LEN_4 (3ULL << 3) /* 4 级页表 (3 = 4-1) at bits [5:3] */
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
int ept_init(void);
/* 构造 EPTP 值(用于 VMCS EPT_POINTER 字段)。 */
u64 ept_get_eptp(void);
/* 在 EPT 中映射一段 GPA→HPA 区间,权限由 flags 决定(EPT_READ/WRITE/EXECUTE)。
* 自动按 4KB 分配中间页表页。返回 0 成功。 */
int ept_map_range(u64 gpa, u64 hpa, u64 size, u64 flags);
/* 便利函数:1:1 映射(GPA==HPA)。 */
int ept_identity_map(u64 gpa, u64 size, u64 flags);
/* Walk the EPT to translate a GPA to its mapped HPA.
* Returns the HPA on success, 0 if the GPA is not mapped.
* Handles 4KB pages and 2MB large pages. */
u64 ept_gpa_to_hpa(u64 gpa);
/* Rewrite an existing EPT leaf (4K or 2MB) as RWX+WB+Ignore PAT.
* Returns 0 if a leaf was found and rewritten, -1 if unmapped.
* Used to recover from nested-KVM EPT misconfig (guest PAT vs EPT type). */
int ept_repair_leaf(u64 gpa, u64 flags);
/* Log PML4/PDPT/PD/PT entries for a GPA (misconfig diagnostics). */
void ept_log_walk(u64 gpa);
/* Map a 2MB large page in EPT (PD-level direct mapping).
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
* Required for real hardware MTRR compliance. */
int ept_map_2m_page(u64 gpa, u64 hpa, u64 flags, u64 memtype);
/* Check INVVPID/INVEPT support from IA32_VMX_EPT_VPID_CAP MSR.
* Call once after VMX init. */
void ept_check_vpid_support(void);
/* Flush EPT TLB entries (INVEPT). Required after EPT mapping changes on real hardware. */
void ept_flush_ept(void);
/* Flush VPID TLB entries (INVVPID). Required after VPID changes on real hardware. */
void ept_flush_vpid(u16 vpid);
#endif
-122
View File
@@ -1,122 +0,0 @@
#ifndef UTSM_FBCON_H
#define UTSM_FBCON_H
#include <utsm/types.h>
#include "../arch/x86_64/limine.h"
/* boot_modules.c 提供(Limine framebuffer 请求) */
extern volatile struct limine_framebuffer_request g_fb_request;
/* ===================================================================
* F2: 统一帧缓冲控制台抽象(fbcon)
*
* 让"疯要有画面"不绑定任何一个驱动:
* - 崩溃路径(panic.c):驱动可能已挂,直接用 Limine fb
* - 早期控制台 / 任何需要直绘的 UTSM 内部路径
*
* 32bpp XRGB,全部带边界检查,无堆依赖。
* 文本渲染需要先 include 字库(g_ascii/g_ascii_w/g_ascii_h,
* 见 kernel/panic_font.inc)。
* =================================================================== */
typedef struct {
u32 *fb;
u64 w, h, pitch;
} fbc_ctx;
/* 从 Limine framebuffer 初始化;失败 -1(无 fb / 非 32bpp) */
static inline int fbc_init(fbc_ctx *c) {
if (!g_fb_request.response || g_fb_request.response->framebuffer_count == 0)
return -1;
struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0];
if (fb->bpp < 32 || !fb->address) return -1;
c->fb = (u32 *)fb->address;
c->w = fb->width;
c->h = fb->height;
c->pitch = fb->pitch;
return 0;
}
static inline void fbc_pixel(fbc_ctx *c, i64 x, i64 y, u32 col) {
if (x < 0 || (u64)x >= c->w || y < 0 || (u64)y >= c->h) return;
*(u32 *)((u8 *)c->fb + (u64)y * c->pitch + (u64)x * 4) = col;
}
static inline u32 fbc_pixel_read(fbc_ctx *c, i64 x, i64 y) {
if (x < 0 || (u64)x >= c->w || y < 0 || (u64)y >= c->h) return 0;
return *(u32 *)((u8 *)c->fb + (u64)y * c->pitch + (u64)x * 4);
}
/* alpha 混合(0–255) */
static inline u32 fbc_blend(u32 bg, u32 fg, u32 alpha) {
u32 na = 256 - alpha;
u32 r = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
u32 b = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
return 0xFF000000u | (r << 16) | (g << 8) | b;
}
static inline void fbc_fill_rect(fbc_ctx *c, i64 x, i64 y, i64 w, i64 h, u32 col) {
for (i64 r = 0; r < h; r++)
for (i64 cx = 0; cx < w; cx++)
fbc_pixel(c, x + cx, y + r, col);
}
/* 全屏纯色(背景色填充) */
static inline void fbc_fill_bg(fbc_ctx *c, u32 col) {
for (u64 y = 0; y < c->h; y++) {
u32 *line = (u32 *)((u8 *)c->fb + y * c->pitch);
for (u64 x = 0; x < c->w; x++) line[x] = col;
}
}
/* RGBA8888 图像绘制(半透明像素与已绘背景混合) */
static inline void fbc_draw_rgba(fbc_ctx *c, const unsigned char *d,
i64 w, i64 h, i64 x, i64 y) {
for (i64 r = 0; r < h; r++) {
for (i64 cx = 0; cx < w; cx++) {
const unsigned char *px4 = d + ((u64)r * w + (u64)cx) * 4;
u32 a = px4[3];
if (a < 8) continue;
u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2];
if (a < 250) col = fbc_blend(fbc_pixel_read(c, x + cx, y + r), col, a);
fbc_pixel(c, x + cx, y + r, col);
}
}
}
/* ---- 文本 ----
* 字库(g_ascii / g_ascii_w / g_ascii_h)由使用方先 include(如
* kernel/panic_font.inc),并定义 FBCON_HAVE_FONT 后再包含本头文件。 */
#ifdef FBCON_HAVE_FONT
static inline void fbc_char(fbc_ctx *c, char ch, i64 x, i64 y, u32 fg) {
u32 idx = (u32)ch - 32;
if (idx > 94) idx = 0;
const u8 *g = g_ascii[idx];
for (i64 r = 0; r < g_ascii_h; r++)
for (i64 cc = 0; cc < g_ascii_w; cc++) {
u8 a = g[r * g_ascii_w + cc];
if (a < 24) continue;
fbc_pixel(c, x + cc, y + r, fg);
}
}
static inline void fbc_string(fbc_ctx *c, const char *s, i64 x, i64 y, u32 fg) {
i64 cx = x;
while (*s) {
fbc_char(c, *s, cx, y, fg);
cx += g_ascii_w;
s++;
}
}
static inline void fbc_string_center(fbc_ctx *c, const char *s, i64 y, u32 fg) {
i64 w = 0;
for (const char *q = s; *q; q++) w += g_ascii_w;
fbc_string(c, s, ((i64)c->w - w) / 2, y, fg);
}
#endif /* FBCON_HAVE_FONT */
#endif /* UTSM_FBCON_H */
-648
View File
@@ -1,648 +0,0 @@
/* fluent_ui.h — Deshab "Win11 Fluent" 视觉组件库
*
* 设计目标:商业化 Windows 11 风格 —— 居中悬浮圆角任务栏、亚克力半透明
* 弹层、圆角窗口、浅色/深色主题(见桌面重构计划第二节)。
*
* 分层:
* L1 wf_theme 主题令牌(色板 + 圆角),g_wf_light / g_wf_dark 预设
* L2 原语 du_wf_acrylic(读背景 alpha 混合圆角面板)、du_wf_shadow、
* du_wf_line(Bresenham 带宽线)、du_wf_ring(圆环/半弧)
* L3 组件 按钮/图标钮/开关/滑块/列表项/卡片/文本框/滚动条/提示 +
* 复选框/进度条/标签页/分隔线/应用瓦片/菜单行/徽标(M3 开始菜单批次)
* L4 图标 du_wf_icon 矢量线性图标(24 网格设计,2px 笔触)
*
* 约定(与 deshab_ui.h 相同):包含本头前,编译单元须已定义 g_ascii
* (include ascii_font.h 或 ascii_bitmaps.c)。所有组件为纯绘制函数,
* hover/press 态由调用方检测并传入。
*
* 中文文本:本库组件标签为 ASCII;中文请调用方用 zh_draw_string +
* 主题令牌色绘制(zhfont.h 在 CODE/desktop/,不进 UTSM 公共头)。
*/
#ifndef DESHAB_FLUENT_UI_H
#define DESHAB_FLUENT_UI_H
#include <utsm/deshab_ui.h>
/* ===================================================================
* L1 — 主题令牌 (wf_theme)
* =================================================================== */
typedef struct {
/* 亚克力面板 */
du_u32 acrylic_tint; /* 混入背景的 tint 色 */
du_u32 acrylic_stroke; /* 1px 边线 */
du_u32 acrylic_solid; /* 不透明回退底色(无 alpha 场景) */
/* 文本 */
du_u32 text_primary;
du_u32 text_secondary;
du_u32 text_dim;
/* 强调色(Win11 accent) */
du_u32 accent;
du_u32 accent_hover;
du_u32 accent_press;
du_u32 on_accent; /* accent 上的文字 */
/* 控件 */
du_u32 control_stroke;
du_u32 window_border;
du_u32 divider;
du_u32 shadow;
/* 语义色 */
du_u32 success, warning, error;
du_u32 close_hover; /* 窗口关闭钮 hover 红 #E81123 */
/* 圆角 */
int radius_sm; /* 4 按钮/输入框 */
int radius_md; /* 8 卡片/窗口 */
int radius_lg; /* 12 任务栏/开始菜单/弹层 */
} wf_theme;
/* 浅色主题(Win11 Light:底 #F3F3F3,accent #0067C0) */
static const wf_theme g_wf_light = {
0xFFF3F3F3u, /* acrylic_tint */
0xFFEDEDEDu, /* acrylic_stroke */
0xFFF6F6F6u, /* acrylic_solid */
0xFF1A1A1Au, 0xFF5D5D5Du, 0xFF8A8A8Au,
0xFF0067C0u, 0xFF0078D4u, 0xFF005A9Eu, 0xFFFFFFFFu,
0xFFD6D6D6u, /* control_stroke */
0xFFE5E5E5u, /* window_border */
0xFFEDEDEDu, /* divider */
0x66000000u, /* shadow */
0xFF0F7B0Fu, 0xFF9D5D00u, 0xFFC42B1Cu,
0xFFC42B1Cu,
4, 8, 12,
};
/* 深色主题(Win11 Dark:底 #202020,accent #4CC2FF) */
static const wf_theme g_wf_dark = {
0xFF202020u,
0xFF3C3C3Cu, /* acrylic_stroke */
0xFF2B2B2Bu,
0xFFFFFFFFu, 0xFFC8C8C8u, 0xFF9A9A9Au,
0xFF4CC2FFu, 0xFF62CBFFu, 0xFF29A8E0u, 0xFF00293Cu,
0xFF3C3C3Cu, /* control_stroke */
0xFF3F3F3Fu, /* window_border */
0xFF3A3A3Au, /* divider */
0x99000000u, /* shadow */
0xFF6CCB5Fu, 0xFFFCE100u, 0xFFFF99A4u,
0xFFC42B1Cu,
4, 8, 12,
};
/* ===================================================================
* L2 — 原语
* =================================================================== */
/* 亚克力面板:逐像素读背景 → alpha 混合 tint → 圆角掩码。
* 真实 backdrop blur 不可行,靠"高透 tint + 1px 边 + 壁纸层透出"模拟。
* 仅用于弹层/任务栏(面积 <40% 屏,性能预算见计划第七节)。 */
static inline void du_wf_acrylic(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h,
du_u32 alpha, du_u32 radius,
const wf_theme *t) {
du_i64 r = (du_i64)radius;
if (r * 2 > w) r = w / 2;
if (r * 2 > h) r = h / 2;
for (du_i64 row = 0; row < h; row++) {
for (du_i64 col = 0; col < w; col++) {
du_i64 dx = 0, dy = 0;
int corner = 0;
if (col < r && row < r) { dx = r - 1 - col; dy = r - 1 - row; corner = 1; }
else if (col >= w - r && row < r) { dx = col - (w - r); dy = r - 1 - row; corner = 1; }
else if (col < r && row >= h - r) { dx = r - 1 - col; dy = row - (h - r); corner = 1; }
else if (col >= w - r && row >= h - r) { dx = col - (w - r); dy = row - (h - r); corner = 1; }
if (corner && dx * dx + dy * dy > r * r) continue;
du_u32 bg = du_pixel_read(ctx, x + col, y + row);
du_pixel(ctx, x + col, y + row, du_blend(bg, t->acrylic_tint, alpha));
}
}
du_rect_outline(ctx, x, y, w, h, t->acrylic_stroke, (du_u32)r);
}
/* 投影:外扩 2 圈递减 alpha 描边(比逐像素卷积便宜 100 倍) */
static inline void du_wf_shadow(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 radius,
const wf_theme *t) {
du_u32 base = du_pixel_read(ctx, x, y + h + 2);
for (int i = 2; i >= 1; i--) {
du_u32 alpha = (i == 2) ? 40 : 70;
du_rect_outline(ctx, x - i, y - i, w + 2 * i, h + 2 * i,
du_blend(base, t->shadow, alpha),
radius + (du_u32)i);
}
}
/* 半透明控件蒙层(hover/press 态) */
static inline void du_wf_overlay(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 overlay) {
du_u32 alpha = overlay >> 24;
if (!alpha) return;
for (du_i64 row = 0; row < h; row++)
for (du_i64 col = 0; col < w; col++)
du_pixel(ctx, x + col, y + row,
du_blend(du_pixel_read(ctx, x + col, y + row),
overlay, alpha));
}
/* Bresenham 直线(带厚度 w 像素) */
static inline void du_wf_line(du_context *ctx, du_i64 x0, du_i64 y0,
du_i64 x1, du_i64 y1, du_i64 w, du_u32 color) {
du_i64 dx = (x1 > x0) ? x1 - x0 : x0 - x1;
du_i64 dy = (y1 > y0) ? y1 - y0 : y0 - y1;
du_i64 sx = (x0 < x1) ? 1 : -1;
du_i64 sy = (y0 < y1) ? 1 : -1;
du_i64 err = dx - dy;
du_i64 half = w / 2;
for (;;) {
du_fill_rect(ctx, x0 - half, y0 - half, w, w, color);
if (x0 == x1 && y0 == y1) break;
du_i64 e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
/* 圆环:中心 (cx,cy),外半径 r,厚度 s。
* half : 0 全环,1 仅上半(dy<=0),-1 仅下半 */
static inline void du_wf_ring(du_context *ctx, du_i64 cx, du_i64 cy,
du_i64 r, du_i64 s, int half, du_u32 color) {
for (du_i64 dy = -r; dy <= r; dy++) {
if (half == 1 && dy > 0) continue;
if (half == -1 && dy < 0) continue;
for (du_i64 dx = -r; dx <= r; dx++) {
du_i64 d2 = dx * dx + dy * dy;
if (d2 <= r * r && d2 > (r - s) * (r - s))
du_pixel(ctx, cx + dx, cy + dy, color);
}
}
}
/* ===================================================================
* L3 — 组件
* =================================================================== */
/* 按钮态 */
typedef enum { WF_BTN_NORMAL = 0, WF_BTN_HOVER, WF_BTN_PRESS } wf_btn_state;
/* 标准按钮:圆角 4,三态底色,ASCII 标签居中(accent=1 为强调按钮) */
static inline void du_wf_button(du_context *ctx, du_i64 x, du_i64 y, du_i64 w,
const char *label, const wf_theme *t,
wf_btn_state st, int accent) {
du_i64 h = 32;
du_u32 bg = accent ? t->accent : t->acrylic_solid;
du_u32 fg = accent ? t->on_accent : t->text_primary;
if (accent) {
du_fill_rounded_rect(ctx, x, y, w, h, bg, 4);
if (st != WF_BTN_NORMAL)
du_wf_overlay(ctx, x + 1, y + 1, w - 2, h - 2,
(st == WF_BTN_PRESS) ? 0x44FFFFFFu : 0x22FFFFFFu);
} else {
du_fill_rounded_rect(ctx, x, y, w, h, bg, 4);
du_rect_outline(ctx, x, y, w, h, t->control_stroke, 4);
if (st != WF_BTN_NORMAL)
du_wf_overlay(ctx, x + 1, y + 1, w - 2, h - 2,
(st == WF_BTN_PRESS) ? 0x33000000u : 0x22000000u);
}
du_i64 len = 0;
while (label[len]) len++;
du_draw_string(ctx, label,
x + (w - len * (du_i64)DU_ASCII_STEP) / 2,
y + (h - (du_i64)DU_ASCII_CELL_H) / 2,
fg, bg, DU_ASCII_STEP);
}
/* 图标按钮:圆角 4 悬停/按下蒙层(图标由调用方叠加绘制) */
static inline void du_wf_icon_button(du_context *ctx, du_i64 x, du_i64 y,
du_i64 size, const wf_theme *t,
wf_btn_state st) {
(void)t;
if (st == WF_BTN_NORMAL) return;
du_wf_overlay(ctx, x, y, size, size,
(st == WF_BTN_PRESS) ? 0x33000000u : 0x22000000u);
du_rect_outline(ctx, x, y, size, size, 0x33000000u, 4);
}
/* 开关:轨道 40×20 圆角 full + 滑块 */
static inline void du_wf_toggle(du_context *ctx, du_i64 x, du_i64 y,
int on, const wf_theme *t) {
du_u32 track = on ? t->accent : 0xFF8A8A8Au;
du_fill_rounded_rect(ctx, x, y, 40, 20, track, 10);
if (on) du_fill_rounded_rect(ctx, x + 20, y + 2, 16, 16, 0xFFFFFFFFu, 8);
else du_fill_rounded_rect(ctx, x + 4, y + 2, 16, 16, 0xFFFFFFFFu, 8);
}
/* 滑块:细轨 4px 圆头 + 圆形拇指。rect_out 返回可拖拽轨道命中区。 */
static inline void du_wf_slider(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, int val, const wf_theme *t,
int rect_out[4]) {
du_i64 ty = y + 8;
du_fill_rounded_rect(ctx, x, ty, w, 4, t->control_stroke, 2);
du_i64 fw = w * val / 100;
if (fw > 2) du_fill_rounded_rect(ctx, x, ty, fw, 4, t->accent, 2);
du_i64 kx = x + fw - 8;
if (kx < x) kx = x;
if (kx > x + w - 16) kx = x + w - 16;
du_fill_rounded_rect(ctx, kx, y, 16, 16, t->accent, 8);
if (rect_out) {
rect_out[0] = (int)x; rect_out[1] = (int)y - 2;
rect_out[2] = (int)w; rect_out[3] = 20;
}
}
/* 列表项:32px 行高,hover/selected 蒙层 + 左侧 3px accent 条 */
static inline void du_wf_list_item(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, const char *label,
const wf_theme *t, int hover, int selected) {
if (selected) du_wf_overlay(ctx, x, y, w, 32, 0x28000000u);
else if (hover) du_wf_overlay(ctx, x, y, w, 32, 0x1A000000u);
if (selected) du_fill_rect(ctx, x, y + 8, 3, 16, t->accent);
du_draw_string(ctx, label, x + 12,
y + (32 - (du_i64)DU_ASCII_CELL_H) / 2,
selected ? t->text_primary : t->text_secondary,
0, DU_ASCII_STEP);
}
/* 卡片:圆角 8 + 投影 + 亚克力 */
static inline void du_wf_card(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 alpha,
const wf_theme *t) {
du_wf_shadow(ctx, x, y, w, h, (du_u32)t->radius_md, t);
du_wf_acrylic(ctx, x, y, w, h, alpha, (du_u32)t->radius_md, t);
}
/* 文本框(搜索框):圆角 4,聚焦时 accent 底边线 + 光标 */
static inline void du_wf_textbox(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, const char *text, int cursor_blink,
const wf_theme *t, int focused) {
du_i64 h = 36;
du_fill_rounded_rect(ctx, x, y, w, h, t->acrylic_solid, 4);
du_rect_outline(ctx, x, y, w, h, focused ? t->accent : t->control_stroke, 4);
if (focused) du_fill_rect(ctx, x + 4, y + h - 2, w - 8, 2, t->accent);
du_u32 fg = text[0] ? t->text_primary : t->text_dim;
du_draw_string(ctx, text, x + 12, y + (h - (du_i64)DU_ASCII_CELL_H) / 2,
fg, t->acrylic_solid, DU_ASCII_STEP);
if (focused && cursor_blink) {
du_i64 len = 0;
while (text[len]) len++;
du_fill_rect(ctx, x + 12 + len * (du_i64)DU_ASCII_STEP + 2,
y + 9, 2, (du_i64)DU_ASCII_CELL_H, t->accent);
}
}
/* 滚动条:4px 覆盖式 */
static inline void du_wf_scrollbar(du_context *ctx, du_i64 x, du_i64 y,
du_i64 h, int pos_0_100, int ratio_0_100,
const wf_theme *t) {
if (ratio_0_100 >= 100) return;
du_i64 thumb_h = h * ratio_0_100 / 100;
if (thumb_h < 24) thumb_h = 24;
du_i64 max_travel = h - thumb_h;
du_i64 ty = y + max_travel * pos_0_100 / 100;
du_fill_rounded_rect(ctx, x, ty, 4, thumb_h, t->text_dim, 2);
}
/* 提示(tooltip):小圆角不透明底 */
static inline void du_wf_tooltip(du_context *ctx, du_i64 x, du_i64 y,
const char *label, const wf_theme *t) {
du_i64 len = 0;
while (label[len]) len++;
du_i64 w = len * (du_i64)DU_ASCII_STEP + 16;
du_fill_rounded_rect(ctx, x, y, w, 28, t->acrylic_solid, 4);
du_rect_outline(ctx, x, y, w, 28, t->control_stroke, 4);
du_draw_string(ctx, label, x + 8, y + (28 - (du_i64)DU_ASCII_CELL_H) / 2,
t->text_primary, t->acrylic_solid, DU_ASCII_STEP);
}
/* ============ M3 新增组件(开始菜单/快速设置批次)============ */
/* 复选框:20×20 圆角 4 方框,选中画 accent 对勾 + accent 底 */
static inline void du_wf_checkbox(du_context *ctx, du_i64 x, du_i64 y,
int checked, const wf_theme *t) {
du_fill_rounded_rect(ctx, x, y, 20, 20,
checked ? t->accent : t->acrylic_solid, 4);
du_rect_outline(ctx, x, y, 20, 20,
checked ? t->accent : t->control_stroke, 4);
if (checked) {
du_wf_line(ctx, x + 4, y + 10, x + 8, y + 15, 2, t->on_accent);
du_wf_line(ctx, x + 8, y + 15, x + 16, y + 4, 2, t->on_accent);
}
}
/* 进度条:6px 圆角轨道 + accent 填充(val 0..100) */
static inline void du_wf_progress(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, int val, const wf_theme *t) {
if (val < 0) val = 0;
if (val > 100) val = 100;
du_fill_rounded_rect(ctx, x, y, w, 6, t->control_stroke, 3);
du_i64 fw = w * val / 100;
if (fw < 4) fw = 4;
if (fw > w) fw = w;
du_fill_rounded_rect(ctx, x, y, fw, 6, t->accent, 3);
}
/* 标签页(pill):圆角 full,active 填 accent_secondary 底 */
static inline void du_wf_pill(du_context *ctx, du_i64 x, du_i64 y,
const char *label, const wf_theme *t, int active) {
du_i64 len = 0;
while (label[len]) len++;
du_i64 w = len * (du_i64)DU_ASCII_STEP + 20;
du_fill_rounded_rect(ctx, x, y, w, 26,
active ? t->accent : t->acrylic_solid, 13);
du_draw_string(ctx, label, x + 10, y + (26 - (du_i64)DU_ASCII_CELL_H) / 2,
active ? t->on_accent : t->text_secondary,
active ? t->accent : t->acrylic_solid, DU_ASCII_STEP);
}
/* 分隔线:1px divider */
static inline void du_wf_separator(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, const wf_theme *t) {
du_fill_rect(ctx, x, y, w, 1, t->divider);
}
/* 应用瓦片:圆角 8 悬浮底 + 居中图标 + 底部标签区(中文标签由调用方
* zh_draw_string 绘制,ASCII 图标由本组件画)。hover/selected 蒙层。 */
static inline void du_wf_app_tile(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, wf_icon_id icon,
const wf_theme *t, wf_btn_state st,
int selected) {
if (st == WF_BTN_HOVER)
du_wf_overlay(ctx, x, y, w, h, 0x14000000u);
if (selected)
du_wf_overlay(ctx, x, y, w, h, 0x1E000000u);
du_i64 isz = h / 3;
if (isz < 24) isz = 24;
if (isz > 36) isz = 36;
du_wf_icon(ctx, icon, x + (w - isz) / 2, y + (h - isz) / 2 - 8, isz,
selected ? t->accent : t->text_primary);
/* 选中态:底部 3px accent 指示条 */
if (selected)
du_fill_rounded_rect(ctx, x + w / 2 - 8, y + h - 6, 16, 3, t->accent, 1);
}
/* 菜单行:36px 行高 + 左侧图标 + ASCII 标签(右箭头区由调用方补充) */
static inline void du_wf_menu_item(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, wf_icon_id icon, const char *label,
const wf_theme *t, wf_btn_state st) {
if (st == WF_BTN_HOVER)
du_wf_overlay(ctx, x, y, w, 36, 0x14000000u);
du_wf_icon(ctx, icon, x + 10, y + 6, 22, t->text_primary);
du_draw_string(ctx, label, x + 44, y + (36 - (du_i64)DU_ASCII_CELL_H) / 2,
t->text_primary, 0, DU_ASCII_STEP);
}
/* 徽标:8px 圆点(通知/在线状态角标) */
static inline void du_wf_badge(du_context *ctx, du_i64 cx, du_i64 cy,
du_u32 color) {
for (du_i64 dy = -4; dy <= 4; dy++)
for (du_i64 dx = -4; dx <= 4; dx++)
if (dx * dx + dy * dy <= 16)
du_pixel(ctx, cx + dx, cy + dy, color);
}
/* ===================================================================
* L4 — 矢量图标(24 网格设计,线性 2px 笔触,Win11 Segoe 风格)
*
* 所有图标以 (x, y) 为左上角、size 为边长绘制,按 u = size/24 缩放
* (u 为最小单位,笔触 s = 2u)。
* =================================================================== */
typedef enum {
WF_ICON_START = 1, /* 四方块(开始) */
WF_ICON_SEARCH, /* 放大镜 */
WF_ICON_NETWORK, /* WiFi 扇形 */
WF_ICON_VOLUME, /* 喇叭 */
WF_ICON_POWER, /* 电源 */
WF_ICON_TASKMGR, /* 折线图 */
WF_ICON_SETTINGS, /* 齿轮(简化) */
WF_ICON_FOLDER, /* 文件夹 */
WF_ICON_TERMINAL, /* 终端 >_ */
WF_ICON_EDITOR, /* 文档横线 */
WF_ICON_CALC, /* 计算器格子 */
WF_ICON_BROWSER, /* 地球 */
WF_ICON_TASKVIEW, /* 双矩形 */
WF_ICON_WIDGETS, /* 四小件 */
WF_ICON_NOTIFY, /* 铃铛 */
WF_ICON_CLOSE, /* × */
WF_ICON_MIN, /* – */
WF_ICON_MAX, /* □ */
WF_ICON_RESTORE, /* 双层 □ */
WF_ICON_USER, /* 人形 */
/* M3 开始菜单/系统工具批次 */
WF_ICON_APPS, /* 3×3 点阵(应用列表) */
WF_ICON_CLOCK, /* 圆 + 指针 */
WF_ICON_INFO, /* 圆 + i */
WF_ICON_DISK, /* 圆柱(磁盘) */
WF_ICON_PICTURE, /* 山 + 太阳(画板/图片) */
WF_ICON_MUSIC, /* 音符 */
WF_ICON_GAME, /* 手柄 */
WF_ICON_CHIP, /* CPU 芯片 */
WF_ICON_REFRESH, /* 环形箭头 */
WF_ICON_LOCK, /* 挂锁 */
WF_ICON_TRASH, /* 垃圾桶 */
} wf_icon_id;
static inline void du_wf_icon(du_context *ctx, wf_icon_id id,
du_i64 x, du_i64 y, du_i64 size, du_u32 color) {
du_i64 u = size / 24;
if (u < 1) u = 1;
du_i64 s = u * 2; /* 笔触宽 */
switch (id) {
case WF_ICON_START:
du_fill_rounded_rect(ctx, x + 3 * u, y + 3 * u, 8 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 13 * u, y + 3 * u, 8 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 3 * u, y + 13 * u, 8 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 13 * u, y + 13 * u, 8 * u, 8 * u, color, (du_u32)u);
break;
case WF_ICON_SEARCH:
du_wf_ring(ctx, x + 10 * u, y + 10 * u, 7 * u, s, 0, color);
du_wf_line(ctx, x + 15 * u, y + 15 * u, x + 20 * u, y + 20 * u, s, color);
break;
case WF_ICON_NETWORK:
du_fill_rounded_rect(ctx, x + 10 * u, y + 17 * u, 3 * u, 3 * u, color, (du_u32)u);
du_wf_ring(ctx, x + 11 * u, y + 19 * u, 6 * u, s, 1, color);
du_wf_ring(ctx, x + 11 * u, y + 21 * u, 10 * u, s, 1, color);
break;
case WF_ICON_VOLUME:
/* 喇叭主体:梯形 */
du_fill_rect(ctx, x + 3 * u, y + 9 * u, 4 * u, 6 * u, color);
du_wf_line(ctx, x + 7 * u, y + 9 * u, x + 11 * u, y + 5 * u, s, color);
du_wf_line(ctx, x + 11 * u, y + 5 * u, x + 11 * u, y + 19 * u, s, color);
du_wf_line(ctx, x + 11 * u, y + 19 * u, x + 7 * u, y + 15 * u, s, color);
/* 声波弧 */
du_wf_ring(ctx, x + 11 * u, y + 12 * u, 7 * u, s, 1, color);
break;
case WF_ICON_POWER:
du_fill_rect(ctx, x + 11 * u, y + 3 * u, s, 8 * u, color);
du_wf_ring(ctx, x + 12 * u, y + 13 * u, 8 * u, s, 0, color);
break;
case WF_ICON_TASKMGR:
du_rect_outline(ctx, x + 2 * u, y + 3 * u, 20 * u, 18 * u, color, (du_u32)u);
du_wf_line(ctx, x + 5 * u, y + 15 * u, x + 9 * u, y + 10 * u, s, color);
du_wf_line(ctx, x + 9 * u, y + 10 * u, x + 13 * u, y + 13 * u, s, color);
du_wf_line(ctx, x + 13 * u, y + 13 * u, x + 19 * u, y + 6 * u, s, color);
break;
case WF_ICON_SETTINGS:
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 5 * u, s, 0, color);
for (int i = 0; i < 8; i++) {
/* 8 个齿:每 45° 一个短径向线 */
du_i64 ax[8] = {0, 1, 1, 1, 0, -1, -1, -1};
du_i64 ay[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
du_wf_line(ctx, x + 12 * u + ax[i] * 7 * u, y + 12 * u + ay[i] * 7 * u,
x + 12 * u + ax[i] * 10 * u, y + 12 * u + ay[i] * 10 * u,
s, color);
}
break;
case WF_ICON_FOLDER:
du_fill_rect(ctx, x + 3 * u, y + 6 * u, 7 * u, 3 * u, color);
du_rect_outline(ctx, x + 3 * u, y + 6 * u, 18 * u, 12 * u, color, (du_u32)u);
du_fill_rect(ctx, x + 3 * u, y + 11 * u, 18 * u, u, color);
break;
case WF_ICON_TERMINAL:
du_rect_outline(ctx, x + 2 * u, y + 4 * u, 20 * u, 16 * u, color, (du_u32)u);
du_wf_line(ctx, x + 6 * u, y + 9 * u, x + 10 * u, y + 12 * u, u + 1, color);
du_wf_line(ctx, x + 10 * u, y + 12 * u, x + 6 * u, y + 15 * u, u + 1, color);
du_fill_rect(ctx, x + 12 * u, y + 15 * u, 5 * u, u + 1, color);
break;
case WF_ICON_EDITOR:
du_rect_outline(ctx, x + 5 * u, y + 2 * u, 14 * u, 20 * u, color, (du_u32)u);
du_fill_rect(ctx, x + 8 * u, y + 8 * u, 8 * u, u, color);
du_fill_rect(ctx, x + 8 * u, y + 12 * u, 8 * u, u, color);
du_fill_rect(ctx, x + 8 * u, y + 16 * u, 5 * u, u, color);
break;
case WF_ICON_CALC:
du_rect_outline(ctx, x + 4 * u, y + 2 * u, 16 * u, 20 * u, color, (du_u32)u);
du_rect_outline(ctx, x + 7 * u, y + 5 * u, 10 * u, 4 * u, color, (du_u32)u);
for (int r = 0; r < 3; r++)
for (int c = 0; c < 3; c++)
du_fill_rect(ctx, x + (7 + c * 4) * u, y + (12 + r * 3) * u,
2 * u, 2 * u, color);
break;
case WF_ICON_BROWSER:
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 9 * u, s, 0, color);
du_fill_rect(ctx, x + 3 * u, y + 11 * u, 18 * u, s, color);
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 5 * u, u, 0, 0); /* 清理中圈 */
du_wf_line(ctx, x + 8 * u, y + 4 * u, x + 8 * u, y + 20 * u, u, color);
du_wf_line(ctx, x + 16 * u, y + 4 * u, x + 16 * u, y + 20 * u, u, color);
break;
case WF_ICON_TASKVIEW:
du_rect_outline(ctx, x + 3 * u, y + 5 * u, 13 * u, 11 * u, color, (du_u32)u);
du_rect_outline(ctx, x + 9 * u, y + 8 * u, 12 * u, 11 * u, color, (du_u32)u);
break;
case WF_ICON_WIDGETS:
du_fill_rounded_rect(ctx, x + 3 * u, y + 3 * u, 8 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 13 * u, y + 3 * u, 8 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 3 * u, y + 13 * u, 5 * u, 8 * u, color, (du_u32)u);
du_fill_rounded_rect(ctx, x + 10 * u, y + 13 * u, 11 * u, 8 * u, color, (du_u32)u);
break;
case WF_ICON_NOTIFY:
/* 铃铛:拱顶 + 两侧斜线 + 底线 + 铃锤 */
du_wf_ring(ctx, x + 12 * u, y + 14 * u, 8 * u, s, 1, color);
du_wf_line(ctx, x + 4 * u, y + 14 * u, x + 4 * u, y + 18 * u, s, color);
du_wf_line(ctx, x + 20 * u, y + 14 * u, x + 20 * u, y + 18 * u, s, color);
du_fill_rect(ctx, x + 3 * u, y + 19 * u, 18 * u, s, color);
du_fill_rounded_rect(ctx, x + 10 * u, y + 21 * u, 4 * u, 3 * u, color, (du_u32)u);
break;
case WF_ICON_CLOSE:
du_wf_line(ctx, x + 6 * u, y + 6 * u, x + 18 * u, y + 18 * u, s, color);
du_wf_line(ctx, x + 18 * u, y + 6 * u, x + 6 * u, y + 18 * u, s, color);
break;
case WF_ICON_MIN:
du_fill_rect(ctx, x + 6 * u, y + 11 * u, 12 * u, s, color);
break;
case WF_ICON_MAX:
du_rect_outline(ctx, x + 5 * u, y + 5 * u, 14 * u, 14 * u, color, (du_u32)u);
break;
case WF_ICON_RESTORE:
du_rect_outline(ctx, x + 4 * u, y + 8 * u, 11 * u, 11 * u, color, (du_u32)u);
du_fill_rect(ctx, x + 8 * u, y + 5 * u, 12 * u, s, color);
du_fill_rect(ctx, x + 18 * u, y + 5 * u, s, 6 * u, color);
du_fill_rect(ctx, x + 12 * u, y + 8 * u, 8 * u, u, color);
break;
case WF_ICON_USER:
du_wf_ring(ctx, x + 12 * u, y + 8 * u, 4 * u, s, 0, color);
du_wf_ring(ctx, x + 12 * u, y + 26 * u, 9 * u, s, 1, color);
break;
case WF_ICON_APPS:
for (int gy = 0; gy < 3; gy++)
for (int gx = 0; gx < 3; gx++)
du_fill_rounded_rect(ctx, x + (3 + gx * 7) * u,
y + (3 + gy * 7) * u, 4 * u, 4 * u,
color, (du_u32)u);
break;
case WF_ICON_CLOCK:
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 9 * u, s, 0, color);
du_wf_line(ctx, x + 12 * u, y + 12 * u, x + 12 * u, y + 6 * u, u + 1, color);
du_wf_line(ctx, x + 12 * u, y + 12 * u, x + 17 * u, y + 14 * u, u + 1, color);
break;
case WF_ICON_INFO:
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 9 * u, s, 0, color);
du_fill_rect(ctx, x + 11 * u, y + 7 * u, s, 5 * u, color);
du_fill_rect(ctx, x + 11 * u, y + 14 * u, s, 4 * u, color);
break;
case WF_ICON_DISK:
/* 圆柱:顶椭圆 + 两侧竖线 + 底弧 */
du_wf_ring(ctx, x + 12 * u, y + 7 * u, 8 * u, s, -1, color);
du_fill_rect(ctx, x + 4 * u, y + 7 * u, s, 10 * u, color);
du_fill_rect(ctx, x + 18 * u, y + 7 * u, s, 10 * u, color);
du_wf_ring(ctx, x + 12 * u, y + 17 * u, 8 * u, s, -1, color);
break;
case WF_ICON_PICTURE:
du_rect_outline(ctx, x + 3 * u, y + 5 * u, 18 * u, 14 * u, color, (du_u32)u);
du_wf_ring(ctx, x + 8 * u, y + 9 * u, u + 1, u + 1, 0, color);
du_wf_line(ctx, x + 5 * u, y + 17 * u, x + 11 * u, y + 11 * u, u + 1, color);
du_wf_line(ctx, x + 11 * u, y + 11 * u, x + 15 * u, y + 15 * u, u + 1, color);
du_wf_line(ctx, x + 15 * u, y + 15 * u, x + 19 * u, y + 9 * u, u + 1, color);
break;
case WF_ICON_MUSIC:
/* 八分音符:符头椭圆 + 符干 + 旗 */
du_fill_rounded_rect(ctx, x + 5 * u, y + 15 * u, 6 * u, 4 * u, color, (du_u32)u);
du_fill_rect(ctx, x + 10 * u, y + 4 * u, s, 12 * u, color);
du_wf_line(ctx, x + 11 * u, y + 4 * u, x + 18 * u, y + 8 * u, s, color);
break;
case WF_ICON_GAME:
du_rect_outline(ctx, x + 2 * u, y + 7 * u, 20 * u, 10 * u, color, (du_u32)(4 * u));
/* 十字键 */
du_fill_rect(ctx, x + 7 * u, y + 9 * u, 2 * u, 6 * u, color);
du_fill_rect(ctx, x + 5 * u, y + 11 * u, 6 * u, 2 * u, color);
/* AB 钮 */
du_wf_ring(ctx, x + 16 * u, y + 12 * u, u + 1, u + 1, 0, color);
du_wf_ring(ctx, x + 19 * u, y + 12 * u, u + 1, u + 1, 0, color);
break;
case WF_ICON_CHIP:
du_rect_outline(ctx, x + 6 * u, y + 6 * u, 12 * u, 12 * u, color, (du_u32)u);
du_rect_outline(ctx, x + 10 * u, y + 10 * u, 4 * u, 4 * u, color, (du_u32)u);
for (int i = 0; i < 3; i++) {
du_fill_rect(ctx, x + (8 + i * 4) * u, y + 2 * u, s, 4 * u, color);
du_fill_rect(ctx, x + (8 + i * 4) * u, y + 18 * u, s, 4 * u, color);
du_fill_rect(ctx, x + 2 * u, y + (8 + i * 4) * u, 4 * u, s, color);
du_fill_rect(ctx, x + 18 * u, y + (8 + i * 4) * u, 4 * u, s, color);
}
break;
case WF_ICON_REFRESH:
du_wf_ring(ctx, x + 12 * u, y + 12 * u, 8 * u, s, 1, color);
/* 箭头(右上端点) */
du_wf_line(ctx, x + 16 * u, y + 2 * u, x + 20 * u, y + 7 * u, s, color);
du_wf_line(ctx, x + 20 * u, y + 7 * u, x + 14 * u, y + 8 * u, s, color);
break;
case WF_ICON_LOCK:
du_rect_outline(ctx, x + 5 * u, y + 10 * u, 14 * u, 10 * u, color, (du_u32)u);
du_wf_ring(ctx, x + 12 * u, y + 10 * u, 5 * u, s, 1, color);
du_fill_rect(ctx, x + 11 * u, y + 14 * u, s, 3 * u, color);
break;
case WF_ICON_TRASH:
du_fill_rect(ctx, x + 9 * u, y + 3 * u, 6 * u, s, color);
du_rect_outline(ctx, x + 5 * u, y + 6 * u, 14 * u, 15 * u, color, (du_u32)u);
du_fill_rect(ctx, x + 8 * u, y + 9 * u, s, 9 * u, color);
du_fill_rect(ctx, x + 12 * u, y + 9 * u, s, 9 * u, color);
du_fill_rect(ctx, x + 16 * u, y + 9 * u, s, 9 * u, color);
break;
default:
du_rect_outline(ctx, x + 4 * u, y + 4 * u, 16 * u, 16 * u, color, (du_u32)u);
break;
}
}
#endif /* DESHAB_FLUENT_UI_H */

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