Compare commits
1
Commits
main
..
fffb16cdf5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fffb16cdf5 |
@@ -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
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
name: Bug 报告
|
||||
about: 报告内核崩溃、驱动故障、构建失败等问题
|
||||
title: "[bug] "
|
||||
labels: bug
|
||||
---
|
||||
|
||||
## 问题描述
|
||||
|
||||
<!-- 简要描述问题 -->
|
||||
|
||||
## 复现步骤
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## 预期行为
|
||||
|
||||
<!-- 应该发生什么 -->
|
||||
|
||||
## 实际行为
|
||||
|
||||
<!-- 实际发生了什么 -->
|
||||
|
||||
## 环境
|
||||
|
||||
- 组件:<!-- UTSM / DSK / DKM:<驱动名> / FirstInit / 构建 -->
|
||||
- 运行环境:<!-- QEMU / 真机 -->
|
||||
- QEMU 参数(如非默认):
|
||||
|
||||
## 串口日志
|
||||
|
||||
```
|
||||
<!-- 粘贴 QEMU 串口输出,至少包含崩溃点前后上下文 -->
|
||||
```
|
||||
|
||||
## 崩溃分析(如适用)
|
||||
|
||||
<!-- 三重故障 / page fault / panic 时的寄存器、栈、调用上下文 -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: 功能请求
|
||||
about: 提议新驱动、新子系统或架构改进
|
||||
title: "[feat] "
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
## 功能描述
|
||||
|
||||
<!-- 简要描述提议的功能 -->
|
||||
|
||||
## 动机
|
||||
|
||||
<!-- 为什么需要这个功能,解决什么问题 -->
|
||||
|
||||
## 涉及子系统
|
||||
|
||||
- [ ] UTSM 首阶段内核
|
||||
- [ ] DSK 主内核
|
||||
- [ ] DKM 驱动
|
||||
- [ ] 内存管理
|
||||
- [ ] 文件系统 / VFS
|
||||
- [ ] 网络
|
||||
- [ ] 调度器 (SAS-R0-PCQ)
|
||||
- [ ] 恢复系统 (DRR)
|
||||
- [ ] FirstInit / 用户空间
|
||||
- [ ] 文档
|
||||
|
||||
## 设计思路
|
||||
|
||||
<!-- 描述实现方案,如有 ABI / 架构影响请重点说明 -->
|
||||
|
||||
## 路线图对应
|
||||
|
||||
<!-- 对应 docs/ROADMAP.md 中的哪个 Phase?(如适用) -->
|
||||
|
||||
## 验收标准
|
||||
|
||||
<!-- 完成后如何验证 -->
|
||||
@@ -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
@@ -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
|
||||
Generated
-15
@@ -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
|
||||
Generated
-4
@@ -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>
|
||||
Generated
-8
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-6
@@ -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>
|
||||
@@ -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。
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 | “封缄之下,未成之物当自爆”`
|
||||
@@ -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 与驱动加载的整合说明。
|
||||
```
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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(§ors, &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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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(§ors, &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",
|
||||
]
|
||||
Generated
-240
@@ -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",
|
||||
]
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Generated
-4639
@@ -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",
|
||||
]
|
||||
@@ -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"] }
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 }"
|
||||
@@ -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 可选
|
||||
```
|
||||
@@ -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:
|
||||
@@ -1,5 +0,0 @@
|
||||
void arch_halt_forever(void) {
|
||||
for (;;) {
|
||||
__asm__ volatile ("cli; hlt");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
Reference in New Issue
Block a user