Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8554a82677 | ||
|
|
a7e5b2bc7a | ||
|
|
07f8031111 | ||
|
|
c80ee79388 | ||
|
|
d95ae091e7 | ||
|
|
8e6506721d | ||
|
|
b780a6b868 | ||
|
|
bd098f51ec | ||
|
|
14015b78a4 | ||
|
|
ace5711a1e | ||
|
|
6caf1aa322 | ||
|
|
5db517f435 | ||
|
|
c5c192c570 | ||
|
|
ccf76b3ec4 | ||
|
|
780230599d | ||
|
|
10599b62c4 | ||
|
|
240daa3409 | ||
|
|
cf395474bf | ||
|
|
845a8f0203 | ||
|
|
1c99c77b3f | ||
|
|
ee8b607f9a | ||
|
|
3aebe962be | ||
|
|
784b774bcb | ||
|
|
a570fe09b5 | ||
|
|
b4d9c8e7ed | ||
|
|
87f8ce11da | ||
|
|
87d532df01 | ||
|
|
25fbabe368 | ||
|
|
e1ebcd0f89 | ||
|
|
055c521ceb | ||
|
|
3786f2580e | ||
|
|
5f01904be1 | ||
|
|
174af71b39 | ||
|
|
506d1b18c6 | ||
|
|
dda0cc9dd0 | ||
|
|
301006d611 | ||
|
|
cd39caa10c | ||
|
|
583262f5f0 | ||
|
|
38ff3cf93c | ||
|
|
c4a5295a81 | ||
|
|
3aa15e7b21 | ||
|
|
068e0e37c3 | ||
|
|
ba16217afe | ||
|
|
1e74ee9fab | ||
|
|
7925c09677 | ||
|
|
42fc06439c | ||
|
|
b76e8f1556 | ||
|
|
1eaf1aedf8 | ||
|
|
23c29f7980 | ||
|
|
6e44fa5df7 | ||
|
|
e4307ff0ec | ||
|
|
1074962546 | ||
|
|
737a2cb0db | ||
|
|
cc0fcf26e3 | ||
|
|
880906edc9 | ||
|
|
2a50fa58c7 |
@@ -0,0 +1,8 @@
|
|||||||
|
*.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
|
||||||
+28
-7
@@ -1,13 +1,34 @@
|
|||||||
ISO/deshab.img
|
|
||||||
*.o
|
*.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/
|
.build_tmp/
|
||||||
.trae/
|
.trae/
|
||||||
docs/workbuddy-planning/
|
docs/workbuddy-planning/
|
||||||
sata_fat32_dsk.img
|
|
||||||
|
|
||||||
# Rust build artifacts
|
# IDE 配置(JetBrains 自动生成的 .idea/.gitignore 只覆盖了部分文件)
|
||||||
CODE/Deaicup Software/src/target/
|
.idea/*.iml
|
||||||
CODE/Deaicup Software/pe/target/
|
.idea/inspectionProfiles/
|
||||||
|
|
||||||
# Large binary images (regenerate from build)
|
# 本地运行日志(如 run_qemu.bat > deshab_log.log 的串口输出)
|
||||||
SYSTEM/boot/linux-rootfs.img
|
*.log
|
||||||
|
deshab_log.log
|
||||||
|
|||||||
@@ -0,0 +1,223 @@
|
|||||||
|
# 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。
|
||||||
|
---
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
---
|
||||||
@@ -0,0 +1,530 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# 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
|
||||||
@@ -85,8 +85,21 @@ SYSTEM/
|
|||||||
SYSTEM/driver/manifest.json
|
SYSTEM/driver/manifest.json
|
||||||
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
|
内核启动驱动模块清单,控制 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
|
SYSTEM/system/deshab64/FUCK
|
||||||
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为。
|
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为、panic 策略([panic] mode=strict|fallback)。
|
||||||
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
|
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
|
||||||
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
|
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
|
||||||
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
|
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
|
||||||
@@ -100,14 +113,18 @@ CODE/DKM/README.md
|
|||||||
CODE/UTSM/README.md
|
CODE/UTSM/README.md
|
||||||
UTSM 模块设计说明。
|
UTSM 模块设计说明。
|
||||||
|
|
||||||
RE/UTSM_算法记录.md
|
docs/RE/UTSM_算法记录.md
|
||||||
UTSM 核心算法记录。
|
UTSM 核心算法记录。
|
||||||
|
|
||||||
RE/UTSM_设计架构.md
|
docs/RE/UTSM_设计架构.md
|
||||||
UTSM 架构记录。
|
UTSM 架构记录。
|
||||||
|
|
||||||
RE/驱动模块ABI设计.md
|
docs/RE/驱动模块ABI设计.md
|
||||||
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。
|
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
|
ISO/deshab.img
|
||||||
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
|
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
|
||||||
@@ -121,18 +138,40 @@ ISO/run_qemu.bat
|
|||||||
已知说明:
|
已知说明:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SYSTEM/ 是打包为 IMG 后的系统根目录。
|
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/boot/ 存放启动模块,例如 utsm.elf。
|
||||||
SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
|
SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
|
||||||
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
|
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
|
||||||
build.bat: 调用 build.ps1。
|
build.bat: 调用 build.ps1,透传参数(如 -Variant dev)。
|
||||||
build.ps1: 编译 UTSM,生成 SYSTEM/boot/utsm.elf,并把 SYSTEM 打包为 ISO/deshab.img。deshab.img 是 GPT 磁盘镜像,包含 FAT32 EFI System Partition。
|
build.ps1: 编译 UTSM/DSK/工具/驱动,把整个 SYSTEM/ 打成 GPT(FAT32 ESP + ext4) 镜像:
|
||||||
ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab.img,串口输出到 stdio。
|
- 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 ESP 镜像。早期启动要求:进入 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 下驱动模块内存需可执行)。
|
当前 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。
|
||||||
|
|
||||||
DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部 14 个外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
|
|
||||||
|
|
||||||
当前已实现 14 个真实 DKM 驱动:
|
当前已实现 14 个真实 DKM 驱动:
|
||||||
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
|
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
|
||||||
@@ -156,15 +195,14 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp
|
|||||||
|
|
||||||
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
|
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
|
||||||
|
|
||||||
下一阶段优先路线:
|
下一阶段优先路线(活跃路线 = **万物互联 P0-P6**(D6/D7,设计定稿 `docs/RE/万物互联与服务总线设计.md`)+ `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`):
|
||||||
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。
|
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 万物互联表。
|
||||||
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。
|
1. **一期 F 系列收尾**: F2 统一控制台后端(fbcon.h 抽象已落,panic 已用)。(F1 莲花 panic / F3 跨平台 Makefile / F4 boot 分级自检 / F5 skip_firstinit 已完成)
|
||||||
3. **IRQ 后端升级**: ~~APIC EOI/IOAPIC redirection~~(B7 已完成:UTSM EOI 钩子 + legacy IRQ1/12/11 持续投递,QEMU 全链路验证;tick 2x 定论 = PIT mode3 IOAPIC edge 双触发 ~200Hz)。剩余:vector allocator 规范化、设备 IRQ 从 PIC 默认全面迁移、MSI/MSI-X(e1000/NVMe)。
|
2. **二期 U 系列**: ✅ 已完成(U1 UUID 表 / U2 Slow Path 真分支 / U3 @sealed 可选标记+linker .sealed section / U4 密文 dump / U5 RFC 8439 KAT)。chacha20 KAT 三方对拍通过(pycryptodome+官方向量+C),"间歇 FAIL" 确认为从未合并的幽灵缺陷。
|
||||||
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。
|
3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表升级为服务总线(并入万物互联 P1)。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本)
|
||||||
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。
|
4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核(多核在 P6 常驻调度之后排队)。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS)
|
||||||
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。
|
5. **五期 E 系列**: 启动莲花动画、假想时光键彩蛋、提交信息规范、风味 ISO。
|
||||||
7. **FirstInit 完善**: ~~修复按键崩溃(fb_text 优化问题)~~(B6 已验证未复现)、~~实现鼠标光标移动~~(M1 已完成)、~~user.conf 写盘~~(已完成)、网络配置界面。
|
6. **工程债(Phase 存档遗留)**: vector allocator/MSI-X、NVMe 高位 BAR、virtio-net virtqueue、DKM 零 warning。
|
||||||
8. ~~**鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动~~(M1 已完成)。
|
|
||||||
|
|
||||||
## M1 里程碑完成状态(2026-07-29)
|
## M1 里程碑完成状态(2026-07-29)
|
||||||
|
|
||||||
@@ -216,3 +254,94 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp
|
|||||||
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
|
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
|
||||||
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
|
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
|
||||||
- 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志
|
- 后续集成路径: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 | “封缄之下,未成之物当自爆”`
|
||||||
|
|||||||
+14
-1
@@ -108,6 +108,8 @@ static u32 g_ready_port = 0xffffffffu;
|
|||||||
static ahci_cmd_header *g_cmd_header;
|
static ahci_cmd_header *g_cmd_header;
|
||||||
static struct dkm_dma_buffer g_cmd_table;
|
static struct dkm_dma_buffer g_cmd_table;
|
||||||
static struct dkm_dma_buffer g_data;
|
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) {
|
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
return dkm_pci_read(bus, dev, func, reg);
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
@@ -534,6 +536,17 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
|
|||||||
log_hex("[ahci] IDENTIFY word0=", id[0]);
|
log_hex("[ahci] IDENTIFY word0=", id[0]);
|
||||||
log_hex("[ahci] IDENTIFY word49=", id[49]);
|
log_hex("[ahci] IDENTIFY word49=", id[49]);
|
||||||
log_hex("[ahci] IDENTIFY word83=", id[83]);
|
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);
|
ahci_read_lba0(port, hdr, &table, &data);
|
||||||
g_ready_port = port;
|
g_ready_port = port;
|
||||||
g_cmd_header = hdr;
|
g_cmd_header = hdr;
|
||||||
@@ -678,7 +691,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
struct dkm_block_device_desc desc;
|
struct dkm_block_device_desc desc;
|
||||||
desc.name = "ahci0";
|
desc.name = "ahci0";
|
||||||
desc.sector_size = 512;
|
desc.sector_size = 512;
|
||||||
desc.sector_count = 0;
|
desc.sector_count = g_sector_count;
|
||||||
desc.ctx = 0;
|
desc.ctx = 0;
|
||||||
desc.read = ahci_block_read;
|
desc.read = ahci_block_read;
|
||||||
desc.write = ahci_block_write;
|
desc.write = ahci_block_write;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
|
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
|
||||||
* (兼容 PIC 布局), 逐条目 mask
|
* (兼容 PIC 布局), 逐条目 mask
|
||||||
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
|
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
|
||||||
* 4. SIPI 多核启动框架 (预留, 未实现)
|
* 4. SIPI 多核启动框架 (预留: 调用 dkm_sipi_wake_ap 即 DKM-E03 panic,
|
||||||
|
* 见 CODE/UTSM/dkm/kernel_api.c 骨架)
|
||||||
*
|
*
|
||||||
* B7 路由接管 (apic_route):
|
* B7 路由接管 (apic_route):
|
||||||
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
|
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
|
||||||
|
|||||||
+607
-10
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../dkm_shared.h"
|
#include "../dkm_shared.h"
|
||||||
|
#include "wpa2_crypto.h"
|
||||||
|
|
||||||
static const char *const g_depends[] = {"pci","irq"};
|
static const char *const g_depends[] = {"pci","irq"};
|
||||||
static const char *const g_provides[] = {"netdev"};
|
static const char *const g_provides[] = {"netdev"};
|
||||||
@@ -173,6 +174,12 @@ static struct ath9k_scan_result g_scan_results[ATH9K_MAX_SCAN_RESULTS];
|
|||||||
static u32 g_scan_count;
|
static u32 g_scan_count;
|
||||||
static int g_scanning;
|
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) ---- */
|
/* ---- 串口日志(参考 e1000 raw_log) ---- */
|
||||||
#define COM1 0x3F8
|
#define COM1 0x3F8
|
||||||
|
|
||||||
@@ -256,6 +263,23 @@ static int ath9k_find(u8 *bus, u8 *dev) {
|
|||||||
raw_log(hex); raw_log("\n");
|
raw_log(hex); raw_log("\n");
|
||||||
return 0;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,6 +502,20 @@ static int find_bssid(const u8 bssid[6]) {
|
|||||||
|
|
||||||
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
|
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
|
||||||
static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) {
|
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;
|
struct ath9k_scan_result tmp;
|
||||||
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
|
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
|
||||||
if (parse_beacon(buf, len, &tmp) != 0) return;
|
if (parse_beacon(buf, len, &tmp) != 0) return;
|
||||||
@@ -531,8 +569,11 @@ static void ath9k_rx_drain(void) {
|
|||||||
* 真实硬件验证后增量完善。
|
* 真实硬件验证后增量完善。
|
||||||
*/
|
*/
|
||||||
static void ath9k_set_channel(u8 channel) {
|
static void ath9k_set_channel(u8 channel) {
|
||||||
(void)channel;
|
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel)。
|
||||||
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */
|
* 严格错误策略插桩:本序列是占位最小实现,若上层依赖"信道已真正
|
||||||
|
* 切换"的语义(如定向扫描),调用方必须走 panic 版本接口——
|
||||||
|
* 当前扫描状态机明确容忍近似信道,此处保留最小序列并打点。 */
|
||||||
|
raw_hex("[ath9k] set_channel (minimal sequence), ch=", channel);
|
||||||
/* 触发 RFBUS 请求,等待 grant */
|
/* 触发 RFBUS 请求,等待 grant */
|
||||||
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
|
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
|
||||||
dkm_delay_us(5);
|
dkm_delay_us(5);
|
||||||
@@ -601,12 +642,550 @@ static int ath9k_is_wireless_impl(void *ctx) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ath9k 当前不支持 TX(仅扫描) */
|
/* ============================================================
|
||||||
static int ath9k_tx_stub(void *ctx, const void *packet, u32 length) {
|
* TX 路径 + WPA2-PSK 连接状态机
|
||||||
(void)ctx; (void)packet; (void)length;
|
*
|
||||||
|
* 说明: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;
|
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) {
|
static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||||
(void)ctx; (void)buffer; (void)capacity;
|
(void)ctx; (void)buffer; (void)capacity;
|
||||||
if (out_length) *out_length = 0;
|
if (out_length) *out_length = 0;
|
||||||
@@ -732,15 +1311,29 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 9. RX 环初始化 */
|
/* 9. RX/TX 环初始化 */
|
||||||
if (api->dma && api->dma->alloc_pages) {
|
if (api->dma && api->dma->alloc_pages) {
|
||||||
int rc = ath9k_rx_init(api->dma);
|
int rc = ath9k_rx_init(api->dma);
|
||||||
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
|
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
raw_log("[ath9k.raw] RX init failed, scan unavailable\n");
|
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 {
|
} else {
|
||||||
raw_log("[ath9k.raw] no DMA api, scan unavailable\n");
|
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. 注册中断 */
|
/* 10. 注册中断 */
|
||||||
@@ -749,20 +1342,24 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc);
|
raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 11. 注册 netdev(标记 WIRELESS + RX_READY) */
|
/* 11. 注册 netdev(无线 + RX/TX) */
|
||||||
struct dkm_net_device_desc ndesc;
|
struct dkm_net_device_desc ndesc;
|
||||||
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
|
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
|
||||||
static const char devname[] = "ath9k0";
|
static const char devname[] = "ath9k0";
|
||||||
ndesc.name = devname;
|
ndesc.name = devname;
|
||||||
copy_mac(ndesc.mac, g_mac);
|
copy_mac(ndesc.mac, g_mac);
|
||||||
ndesc.flags = DKM_NET_F_WIRELESS | (g_rx_ready ? DKM_NET_F_RX_READY : 0);
|
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.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */
|
||||||
ndesc.tx = ath9k_tx_stub;
|
ndesc.tx = ath9k_tx_impl;
|
||||||
ndesc.rx_poll = ath9k_rx_poll_impl;
|
ndesc.rx_poll = ath9k_rx_poll_impl;
|
||||||
ndesc.scan_start = ath9k_scan_start_impl;
|
ndesc.scan_start = ath9k_scan_start_impl;
|
||||||
ndesc.scan_count = ath9k_scan_count_impl;
|
ndesc.scan_count = ath9k_scan_count_impl;
|
||||||
ndesc.scan_result = ath9k_scan_result_impl;
|
ndesc.scan_result = ath9k_scan_result_impl;
|
||||||
ndesc.is_wireless = ath9k_is_wireless_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) {
|
if (api->net && api->net->register_device) {
|
||||||
int rc = api->net->register_device(&ndesc);
|
int rc = api->net->register_device(&ndesc);
|
||||||
|
|||||||
@@ -0,0 +1,639 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/* 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
|
||||||
@@ -26,6 +26,26 @@ struct dkm_log_api {
|
|||||||
void (*panic)(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 {
|
struct dkm_kernel_api {
|
||||||
u32 version;
|
u32 version;
|
||||||
u32 size;
|
u32 size;
|
||||||
@@ -47,6 +67,19 @@ struct dkm_kernel_api {
|
|||||||
u64 fb_pitch;
|
u64 fb_pitch;
|
||||||
u16 fb_bpp;
|
u16 fb_bpp;
|
||||||
const void *boot_modules_response;
|
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_handle;
|
||||||
@@ -185,6 +218,31 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
devfs_file("fb0", info);
|
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 */
|
/* /dev/modules — count from boot_modules */
|
||||||
const struct limine_module_response *rsp =
|
const struct limine_module_response *rsp =
|
||||||
(const struct limine_module_response *)api->boot_modules_response;
|
(const struct limine_module_response *)api->boot_modules_response;
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ struct dkm_net_device_desc {
|
|||||||
int (*scan_count)(void *ctx);
|
int (*scan_count)(void *ctx);
|
||||||
int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out);
|
int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out);
|
||||||
int (*is_wireless)(void *ctx);
|
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 {
|
struct dkm_net_stats {
|
||||||
@@ -128,6 +131,8 @@ struct dkm_net_api {
|
|||||||
int (*scan_count)(u32 index);
|
int (*scan_count)(u32 index);
|
||||||
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
|
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
|
||||||
int (*is_wireless)(u32 index);
|
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);
|
int (*device_stats)(u32 index, struct dkm_net_stats *out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -735,11 +735,15 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
raw_log("[e1000] MAC from RAL/RAH\n");
|
raw_log("[e1000] MAC from RAL/RAH\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 仍为全 0 或多播位错误时使用与虚拟机配置一致的回退 MAC */
|
/* 仍为全 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) ||
|
if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) ||
|
||||||
(mac[0] & 1)) {
|
(mac[0] & 1)) {
|
||||||
mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56;
|
raw_log("[e1000] MAC read failed (zero/multicast)\n");
|
||||||
raw_log("[e1000] using fallback MAC 52:54:00:12:34:56\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);
|
raw_mac("[e1000] MAC=", mac);
|
||||||
|
|||||||
@@ -0,0 +1,1172 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,721 @@
|
|||||||
|
/* 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; }
|
||||||
+10
-3
@@ -920,13 +920,17 @@ static int nvme_msix_program(const struct dkm_kernel_api *api,
|
|||||||
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
|
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
|
||||||
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
|
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
|
||||||
if (!tbl) {
|
if (!tbl) {
|
||||||
g_log->warn("[nvme] MSI-X: table MMIO map failed; polling");
|
/* 严格错误策略插桩: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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vector = api->irq_vector_alloc();
|
int vector = api->irq_vector_alloc();
|
||||||
if (vector < 0 || vector > 255) {
|
if (vector < 0 || vector > 255) {
|
||||||
g_log->warn("[nvme] MSI-X: vector alloc failed; polling");
|
/* 严格错误策略插桩:向量分配失败不允许静默转轮询(NVME-E02) */
|
||||||
|
g_log->error("[nvme] MSI-X: vector alloc failed");
|
||||||
|
g_log->panic("NVME-E02 VECTOR ALLOC FAILED");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
log_dec("[nvme] MSI-X vector=", (u64)vector);
|
log_dec("[nvme] MSI-X vector=", (u64)vector);
|
||||||
@@ -958,7 +962,10 @@ static int nvme_msix_program(const struct dkm_kernel_api *api,
|
|||||||
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
|
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
|
||||||
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
|
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
|
||||||
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
|
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
|
||||||
g_log->warn("[nvme] MSI-X: readback mismatch; rolling back");
|
/* 严格错误策略插桩: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);
|
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
|
||||||
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
|
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
|
||||||
dkm_pci_write(bus, dev, func, (u8)cap, rb);
|
dkm_pci_write(bus, dev, func, (u8)cap, rb);
|
||||||
|
|||||||
@@ -775,19 +775,21 @@ static void virtio_net_selftest(void) {
|
|||||||
log_hex("[virtio_net] selftest rx frames seen=", frames);
|
log_hex("[virtio_net] selftest rx frames seen=", frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= stub(初始化失败时的安全占位,与历史行为一致) ================= */
|
/* ================= stub(初始化失败时的安全占位) =================
|
||||||
|
* 严格错误策略插桩:占位不再静默丢包/静默无数据——一旦被上层调用
|
||||||
|
* (即有人真的把流量交给未协商 virtqueue 的设备),立即莲花 panic。
|
||||||
|
* 正常路径 netman 因 flags 缺 LINK_UP 不会选中本驱动,stub 不触发。 */
|
||||||
|
|
||||||
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
|
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
|
||||||
(void)ctx;
|
(void)ctx; (void)packet;
|
||||||
(void)packet;
|
log_hex("[virtio_net] tx stub hit, len=", length);
|
||||||
log_hex("[virtio_net] tx stub: packet dropped, len=", length);
|
g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)");
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||||
(void)ctx;
|
(void)ctx; (void)buffer; (void)capacity;
|
||||||
(void)buffer;
|
g_log->panic("VNET-E02 RX VIRTQUEUE NOT IMPLEMENTED (stub rx called)");
|
||||||
(void)capacity;
|
|
||||||
if (out_length) *out_length = 0;
|
if (out_length) *out_length = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
+491
-116
@@ -92,6 +92,9 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
#define XHCI_RT_IR0_ERDP_LO 0x38
|
#define XHCI_RT_IR0_ERDP_LO 0x38
|
||||||
#define XHCI_RT_IR0_ERDP_HI 0x3C
|
#define XHCI_RT_IR0_ERDP_HI 0x3C
|
||||||
|
|
||||||
|
/* ERDP bits: bit3 = Event Handler Busy (软件写 1 清挂起) */
|
||||||
|
#define ERDP_EHB (1u << 3)
|
||||||
|
|
||||||
/* USBCMD bits */
|
/* USBCMD bits */
|
||||||
#define USBCMD_RS (1u << 0)
|
#define USBCMD_RS (1u << 0)
|
||||||
#define USBCMD_RST (1u << 1)
|
#define USBCMD_RST (1u << 1)
|
||||||
@@ -109,8 +112,11 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
#define PORTSC_PR (1u << 4)
|
#define PORTSC_PR (1u << 4)
|
||||||
#define PORTSC_PLS_SHIFT 5
|
#define PORTSC_PLS_SHIFT 5
|
||||||
#define PORTSC_PLS_MASK 0xF
|
#define PORTSC_PLS_MASK 0xF
|
||||||
#define PORTSC_SPEED_SHIFT 17
|
/* Port Speed 字段在 PORTSC bits[13:10](xHCI 1.2 §5.4.8)。
|
||||||
#define PORTSC_SPEED_MASK 0x7
|
* 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 */
|
/* TRB types */
|
||||||
#define TRB_TYPE_NORMAL 0x01
|
#define TRB_TYPE_NORMAL 0x01
|
||||||
@@ -124,12 +130,14 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
#define TRB_TYPE_CONFIGURE_EP 0x0C
|
#define TRB_TYPE_CONFIGURE_EP 0x0C
|
||||||
#define TRB_TYPE_NOOP 0x17
|
#define TRB_TYPE_NOOP 0x17
|
||||||
|
|
||||||
/* Event TRB types — same namespace as command/transfer TRB types.
|
/* Event TRB types — xHCI 1.2 spec Table 6-28 (TRB Type field [15:10]).
|
||||||
* Per xHCI spec §6.4: these are the TRB Type field values in the
|
* 规范值:Transfer=0x20, Command Completion=0x21, Port Status Change=0x22。
|
||||||
* Event TRB control dword [15:10]. */
|
* BUG-FIX: 原定义 0x01/0x02/0x21 与规范冲突——CMD_COMPLETE(0x21) 被
|
||||||
#define EVT_TYPE_TRANSFER 0x01 /* Transfer Event */
|
* 误判为 PORT_CHANGE 事件消费,导致 Enable Slot 等命令事件环被提前
|
||||||
#define EVT_TYPE_CMD_COMPLETE 0x02 /* Command Completion Event */
|
* 清空,后续命令全部超时(QEMU hcd-xhci.c xhci_write_event 佐证)。 */
|
||||||
#define EVT_TYPE_PORT_CHANGE 0x21 /* Port Status Change 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 */
|
/* TRB control field helpers */
|
||||||
#define TRB_TYPE_SHIFT 10
|
#define TRB_TYPE_SHIFT 10
|
||||||
@@ -281,6 +289,149 @@ static void log_hex(const char *prefix, u64 value) {
|
|||||||
g_log->info(buf);
|
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) {
|
static void xhci_zero(void *ptr, u32 len) {
|
||||||
u8 *p = (u8 *)ptr;
|
u8 *p = (u8 *)ptr;
|
||||||
for (u32 i = 0; i < len; i++) p[i] = 0;
|
for (u32 i = 0; i < len; i++) p[i] = 0;
|
||||||
@@ -321,7 +472,98 @@ static __inline__ void xhci_mb(void) {
|
|||||||
* Phase 1: PCI discovery + MMIO mapping
|
* Phase 1: PCI discovery + MMIO mapping
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
/* 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 (u16 bus = 0; bus < 256; bus++) {
|
||||||
for (u8 dev = 0; dev < 32; dev++) {
|
for (u8 dev = 0; dev < 32; dev++) {
|
||||||
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
|
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
|
||||||
@@ -341,11 +583,14 @@ static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
|
|||||||
if (class_code == XHCI_CLASS_SERIAL &&
|
if (class_code == XHCI_CLASS_SERIAL &&
|
||||||
subclass == XHCI_SUBCLASS_USB &&
|
subclass == XHCI_SUBCLASS_USB &&
|
||||||
prog_if == XHCI_PROGIF_XHCI) {
|
prog_if == XHCI_PROGIF_XHCI) {
|
||||||
|
if (seen == nth) {
|
||||||
*out_bus = (u8)bus;
|
*out_bus = (u8)bus;
|
||||||
*out_dev = dev;
|
*out_dev = dev;
|
||||||
*out_func = func;
|
*out_func = func;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
seen++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,9 +763,18 @@ static int xhci_parse_caps(void) {
|
|||||||
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
|
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
|
||||||
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
|
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
|
||||||
|
|
||||||
/* Page size */
|
/* 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_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE);
|
||||||
g_page_size = 1u << ((ps_reg & 0xF) + 12);
|
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] CAPLEN=", g_cap_length);
|
||||||
log_hex("[xhci] MaxSlots=", g_max_slots);
|
log_hex("[xhci] MaxSlots=", g_max_slots);
|
||||||
@@ -591,9 +845,13 @@ static u32 xhci_ring_enqueue(xhci_ring *ring, u64 param, u32 status, u32 control
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell */
|
/* 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) {
|
static void xhci_ring_doorbell(u32 slot_id, u32 target) {
|
||||||
mmio_write32(g_doorbell_off + slot_id * 0x20, target);
|
mmio_write32(g_doorbell_off + slot_id * 4, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
@@ -627,9 +885,9 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
|
|||||||
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
|
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
|
||||||
|
|
||||||
/* Update ERDP */
|
/* Update ERDP: 地址 16 字节对齐,bit0 保持 0,bit3=EHB 清挂起 */
|
||||||
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
|
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 | g_evt_ccs);
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
|
||||||
|
|
||||||
/* Check completion code */
|
/* Check completion code */
|
||||||
u32 cc = (evt->status >> 24) & 0xFF;
|
u32 cc = (evt->status >> 24) & 0xFF;
|
||||||
@@ -659,7 +917,7 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
|
|||||||
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 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);
|
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 | g_evt_ccs);
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | ERDP_EHB);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +935,7 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
|
|||||||
|
|
||||||
static int xhci_cmd_submit(u64 param, u32 status, u32 control,
|
static int xhci_cmd_submit(u64 param, u32 status, u32 control,
|
||||||
u64 timeout_ms,
|
u64 timeout_ms,
|
||||||
u64 *out_param, u32 *out_status) {
|
u64 *out_param, u32 *out_status, u32 *out_control) {
|
||||||
/* Clear Event Ring interrupt pending */
|
/* Clear Event Ring interrupt pending */
|
||||||
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
||||||
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
|
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
|
||||||
@@ -705,7 +963,7 @@ static int xhci_cmd_submit(u64 param, u32 status, u32 control,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
|
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
|
||||||
out_param, out_status, 0);
|
out_param, out_status, out_control);
|
||||||
|
|
||||||
/* Clear EINT */
|
/* Clear EINT */
|
||||||
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
|
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
|
||||||
@@ -810,8 +1068,8 @@ static int xhci_controller_start(void) {
|
|||||||
/* Configure Event Ring */
|
/* Configure Event Ring */
|
||||||
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
|
||||||
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
|
||||||
u64 erdp = g_evt_ring.dma.phys | (u64)g_evt_ccs;
|
u64 erdp = g_evt_ring.dma.phys;
|
||||||
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp);
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
|
||||||
|
|
||||||
/* Enable interrupt (IMAN.IE=1) */
|
/* Enable interrupt (IMAN.IE=1) */
|
||||||
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
|
||||||
@@ -850,8 +1108,12 @@ static int xhci_reset_port(u32 port_idx) {
|
|||||||
/* Already enabled? */
|
/* Already enabled? */
|
||||||
if (portsc & PORTSC_PED) return 0;
|
if (portsc & PORTSC_PED) return 0;
|
||||||
|
|
||||||
/* Clear any change bits (W1C), then assert port reset */
|
/* 只置 PR,并清掉 RW1C 位。把读到的 PORTSC 原样或上 PR 会把
|
||||||
mmio_write32(port_off, portsc | 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 */
|
/* Wait for PED (Port Enabled) with 500ms timeout */
|
||||||
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
|
||||||
@@ -883,7 +1145,7 @@ static int xhci_reset_port(u32 port_idx) {
|
|||||||
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
|
||||||
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 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);
|
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 | g_evt_ccs);
|
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -911,15 +1173,18 @@ static int xhci_enable_slot(u8 *out_slot_id) {
|
|||||||
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
|
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
|
||||||
u64 out_param = 0;
|
u64 out_param = 0;
|
||||||
u32 out_status = 0;
|
u32 out_status = 0;
|
||||||
|
u32 out_control = 0;
|
||||||
|
|
||||||
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status);
|
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status, &out_control);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
g_log->error("[xhci] Enable Slot failed");
|
g_log->error("[xhci] Enable Slot failed");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Slot ID is in the upper bits of the completion event parameter */
|
/* Slot ID 位于 Command Completion Event 的 TRB control[31:24]。
|
||||||
*out_slot_id = (u8)((out_param >> 24) & 0xFF);
|
* (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);
|
log_hex("[xhci] slot_id=", *out_slot_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -940,7 +1205,8 @@ static int xhci_address_device(u8 slot_id, int bsr) {
|
|||||||
|
|
||||||
u64 out_param = 0;
|
u64 out_param = 0;
|
||||||
u32 out_status = 0;
|
u32 out_status = 0;
|
||||||
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status);
|
u32 out_control = 0;
|
||||||
|
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
g_log->error("[xhci] Address Device failed");
|
g_log->error("[xhci] Address Device failed");
|
||||||
return rc;
|
return rc;
|
||||||
@@ -958,7 +1224,8 @@ static int xhci_configure_endpoint(u8 slot_id) {
|
|||||||
|
|
||||||
u64 out_param = 0;
|
u64 out_param = 0;
|
||||||
u32 out_status = 0;
|
u32 out_status = 0;
|
||||||
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status);
|
u32 out_control = 0;
|
||||||
|
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
g_log->error("[xhci] Configure Endpoint failed");
|
g_log->error("[xhci] Configure Endpoint failed");
|
||||||
return rc;
|
return rc;
|
||||||
@@ -979,7 +1246,16 @@ static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
|
|||||||
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
|
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
|
||||||
|
|
||||||
u32 setup_status = 8; /* Setup packet length */
|
u32 setup_status = 8; /* Setup packet length */
|
||||||
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
|
/* 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 */
|
/* Set direction in TRB: bit 16 of Setup Stage TRB */
|
||||||
if (direction) setup_control |= (1u << 16);
|
if (direction) setup_control |= (1u << 16);
|
||||||
|
|
||||||
@@ -995,7 +1271,8 @@ static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
|
|||||||
|
|
||||||
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
|
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
|
||||||
u32 data_status = data_len;
|
u32 data_status = data_len;
|
||||||
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
|
/* Data Stage 不设 IOC(避免多余事件残留) */
|
||||||
|
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT);
|
||||||
if (direction) data_control |= (1u << 16); /* DIR=IN */
|
if (direction) data_control |= (1u << 16); /* DIR=IN */
|
||||||
|
|
||||||
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
|
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
|
||||||
@@ -1088,7 +1365,11 @@ static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
|
|||||||
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
|
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
|
||||||
|
|
||||||
xhci_mb();
|
xhci_mb();
|
||||||
xhci_ring_doorbell(g_msc.slot_id, ep_num);
|
/* 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 */
|
/* Poll for Transfer Event */
|
||||||
u64 out_param = 0;
|
u64 out_param = 0;
|
||||||
@@ -1097,6 +1378,7 @@ static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
|
|||||||
&out_param, &out_status, 0);
|
&out_param, &out_status, 0);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
g_log->warn("[xhci] bulk transfer failed");
|
g_log->warn("[xhci] bulk transfer failed");
|
||||||
|
log_hex("[xhci] bulk rc=", (u64)(i64)rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1343,32 +1625,48 @@ static int xhci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
|
|||||||
* Phase 3: Build Input Context for Address Device
|
* Phase 3: Build Input Context for Address Device
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0) {
|
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0, u32 port_idx) {
|
||||||
(void)slot_id;
|
(void)slot_id;
|
||||||
u32 ctx_sz = g_ctx_size;
|
u32 ctx_sz = g_ctx_size;
|
||||||
u8 *ic = (u8 *)g_input_ctx.virt;
|
u8 *ic = (u8 *)g_input_ctx.virt;
|
||||||
xhci_zero(ic, g_input_ctx.size);
|
xhci_zero(ic, g_input_ctx.size);
|
||||||
|
|
||||||
/* Input Control Context: enable Slot Context (bit 0) and EP0 (bit 1) */
|
/* 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;
|
u32 *icc = (u32 *)ic;
|
||||||
icc[0] = (1u << 1) | (1u << 0); /* A0=1 (Slot), A1=1 (EP0) */
|
icc[0] = 0x0; /* Drop Context Flags */
|
||||||
|
icc[1] = (1u << 0) | (1u << 1); /* Add: Slot + EP0 */
|
||||||
|
|
||||||
/* Slot Context starts at offset ctx_sz */
|
/* 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 *slot_ctx = (u32 *)(ic + ctx_sz);
|
||||||
u32 route_string = 0; /* direct-connect, no hub */
|
u32 route_string = 0; /* direct-connect, no hub */
|
||||||
slot_ctx[0] = route_string | ((u32)speed << 29);
|
slot_ctx[0] = route_string | ((u32)speed << 20) | (1u << 27);
|
||||||
slot_ctx[1] = (u32)mps0; /* Max Packet Size for EP0 */
|
slot_ctx[1] = (port_idx + 1) << 16; /* Root Hub Port Number (1-based) */
|
||||||
slot_ctx[2] = (1u << 27); /* Context Entries = 1 (only EP0) */
|
|
||||||
|
|
||||||
/* EP0 Context starts at offset ctx_sz * 2 */
|
/* 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);
|
u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2);
|
||||||
/* EP Type = Control, Max Packet Size, etc. */
|
|
||||||
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
|
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
|
||||||
ep0_ctx[4] = 8; /* Average TRB Length */
|
|
||||||
/* Dequeue Pointer: EP0 transfer ring */
|
|
||||||
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
|
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
|
||||||
ep0_ctx[6] = (u32)(ep0_deq & 0xFFFFFFFFu);
|
ep0_ctx[2] = (u32)(ep0_deq & 0xFFFFFFF0u) | (u32)g_ep0_ring.ccs;
|
||||||
ep0_ctx[7] = (u32)(ep0_deq >> 32);
|
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,
|
static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
|
||||||
@@ -1376,31 +1674,45 @@ static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
|
|||||||
u32 ctx_sz = g_ctx_size;
|
u32 ctx_sz = g_ctx_size;
|
||||||
u8 *ic = (u8 *)g_input_ctx.virt;
|
u8 *ic = (u8 *)g_input_ctx.virt;
|
||||||
|
|
||||||
/* Update Input Control Context: enable EP0, Bulk OUT, Bulk IN */
|
/* 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 *icc = (u32 *)ic;
|
||||||
icc[0] = (1u << 1) | (1u << 0) |
|
u32 out_idx = 2u * bulk_out_ep;
|
||||||
(1u << bulk_out_ep) | (1u << bulk_in_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(bulk_in_ep, bulk_out_ep) */
|
/* Update Slot Context: Context Entries = max Context Index
|
||||||
|
* (DWORD0 bits[27:31],保留 Speed/Route 原值)。 */
|
||||||
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
|
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
|
||||||
u32 max_ep = bulk_out_ep > bulk_in_ep ? bulk_out_ep : bulk_in_ep;
|
u32 max_idx = out_idx > in_idx ? out_idx : in_idx;
|
||||||
slot_ctx[2] = (max_ep << 27);
|
slot_ctx[0] &= ~(0x1Fu << 27);
|
||||||
|
slot_ctx[0] |= (max_idx << 27);
|
||||||
|
|
||||||
/* Bulk OUT Endpoint Context (at offset ctx_sz * (1 + bulk_out_ep)) */
|
/* Bulk OUT Endpoint Context。
|
||||||
u32 *out_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_out_ep));
|
* 偏移 = 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);
|
out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16);
|
||||||
out_ctx[4] = 512; /* Average TRB Length */
|
|
||||||
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
|
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
|
||||||
out_ctx[6] = (u32)(out_deq & 0xFFFFFFFFu);
|
out_ctx[2] = (u32)(out_deq & 0xFFFFFFF0u) | (u32)g_bulk_out_ring.ccs;
|
||||||
out_ctx[7] = (u32)(out_deq >> 32);
|
out_ctx[3] = (u32)(out_deq >> 32);
|
||||||
|
out_ctx[4] = 512; /* Average TRB Length */
|
||||||
|
|
||||||
/* Bulk IN Endpoint Context (at offset ctx_sz * (1 + bulk_in_ep)) */
|
/* Bulk IN Endpoint Context (at offset ctx_sz * (in_idx + 1)) */
|
||||||
u32 *in_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_in_ep));
|
u32 *in_ctx = (u32 *)(ic + ctx_sz * (in_idx + 1));
|
||||||
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
|
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
|
||||||
in_ctx[4] = 512; /* Average TRB Length */
|
|
||||||
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
|
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
|
||||||
in_ctx[6] = (u32)(in_deq & 0xFFFFFFFFu);
|
in_ctx[2] = (u32)(in_deq & 0xFFFFFFF0u) | (u32)g_bulk_in_ring.ccs;
|
||||||
in_ctx[7] = (u32)(in_deq >> 32);
|
in_ctx[3] = (u32)(in_deq >> 32);
|
||||||
|
in_ctx[4] = 512; /* Average TRB Length */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
@@ -1411,18 +1723,22 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
u8 speed = xhci_get_port_speed(port_idx);
|
u8 speed = xhci_get_port_speed(port_idx);
|
||||||
log_hex("[xhci] port speed=", speed);
|
log_hex("[xhci] port speed=", speed);
|
||||||
|
|
||||||
/* Default MaxPacketSize for EP0 based on speed */
|
/* Default MaxPacketSize for EP0 based on speed
|
||||||
u16 mps0 = 64; /* High/Super-Speed default */
|
* (xHCI 1.2 §4.3: Full=8? No — FS/HS EP0 通常 64,LS=8,SS=512) */
|
||||||
if (speed == USB_SPEED_FULL) mps0 = 64;
|
u16 mps0 = 64; /* Full/High-Speed default */
|
||||||
if (speed == USB_SPEED_LOW) mps0 = 8;
|
if (speed == USB_SPEED_LOW) mps0 = 8;
|
||||||
|
if (speed == USB_SPEED_SUPER) mps0 = 512;
|
||||||
|
|
||||||
/* Step 1: Enable Slot */
|
/* Step 1: Enable Slot */
|
||||||
u8 slot_id = 0;
|
u8 slot_id = 0;
|
||||||
if (xhci_enable_slot(&slot_id) != 0) return -1;
|
if (xhci_enable_slot(&slot_id) != 0) {
|
||||||
|
FB_ENUM_FAIL(0x0A); /* 黄: Enable Slot 失败 */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
g_msc.slot_id = slot_id;
|
g_msc.slot_id = slot_id;
|
||||||
g_msc.speed = speed;
|
g_msc.speed = speed;
|
||||||
|
|
||||||
/* Step 2: Allocate per-device DMA buffers */
|
/* 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_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_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(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2;
|
||||||
@@ -1431,18 +1747,30 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
if (xhci_ring_init(&g_bulk_out_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;
|
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) */
|
/* Step 3: Build Input Context and Address Device (BSR=1 first) */
|
||||||
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
|
||||||
if (xhci_address_device(slot_id, 1) != 0) {
|
if (xhci_address_device(slot_id, 1) != 0) {
|
||||||
/* BSR=1 failed, try without BSR */
|
/* BSR=1 failed, try without BSR */
|
||||||
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
|
||||||
if (xhci_address_device(slot_id, 0) != 0) return -3;
|
if (xhci_address_device(slot_id, 0) != 0) {
|
||||||
|
FB_ENUM_FAIL(0x0B); /* 黄: Address Device 失败(BSR=1/0 均失败) */
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 4: Get Device Descriptor */
|
/* Step 4: Get Device Descriptor */
|
||||||
u8 dev_desc[18];
|
u8 dev_desc[18];
|
||||||
if (xhci_get_device_descriptor(dev_desc) != 0) {
|
if (xhci_get_device_descriptor(dev_desc) != 0) {
|
||||||
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
|
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
|
||||||
|
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Device) 失败 */
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1459,7 +1787,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
/* Step 5: Re-address with real MPS if different */
|
/* Step 5: Re-address with real MPS if different */
|
||||||
if (real_mps0 != mps0 && real_mps0 >= 8) {
|
if (real_mps0 != mps0 && real_mps0 >= 8) {
|
||||||
mps0 = real_mps0;
|
mps0 = real_mps0;
|
||||||
xhci_build_input_ctx_slot(slot_id, speed, mps0);
|
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
|
||||||
if (xhci_address_device(slot_id, 0) != 0) {
|
if (xhci_address_device(slot_id, 0) != 0) {
|
||||||
g_log->warn("[xhci] re-address failed");
|
g_log->warn("[xhci] re-address failed");
|
||||||
/* Continue anyway — device may still work */
|
/* Continue anyway — device may still work */
|
||||||
@@ -1470,12 +1798,16 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
u8 cfg_desc[255];
|
u8 cfg_desc[255];
|
||||||
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
|
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
|
||||||
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
|
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
|
||||||
|
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Config) 失败 */
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse configuration descriptor to find MSC interface and endpoints */
|
/* Parse configuration descriptor to find MSC interface and endpoints */
|
||||||
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
|
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
|
||||||
if (total_len < 9) return -5;
|
if (total_len < 9) {
|
||||||
|
FB_ENUM_FAIL(0x0D); /* 黄: 配置描述符过短 */
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
u8 bulk_out_ep = 0, bulk_in_ep = 0;
|
u8 bulk_out_ep = 0, bulk_in_ep = 0;
|
||||||
u16 bulk_out_mps = 512, bulk_in_mps = 512;
|
u16 bulk_out_mps = 512, bulk_in_mps = 512;
|
||||||
@@ -1497,10 +1829,16 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
msc_found = 1;
|
msc_found = 1;
|
||||||
g_log->info("[xhci] MSC BOT interface found");
|
g_log->info("[xhci] MSC BOT interface found");
|
||||||
|
|
||||||
/* Scan following endpoint descriptors */
|
/* 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;
|
u32 ep_pos = pos + desc_len;
|
||||||
while (ep_pos + 7 <= (u32)total_len &&
|
while (ep_pos + 2 <= (u32)total_len) {
|
||||||
cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */
|
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_addr = cfg_desc[ep_pos + 2];
|
||||||
u8 ep_attr = cfg_desc[ep_pos + 3];
|
u8 ep_attr = cfg_desc[ep_pos + 3];
|
||||||
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
|
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
|
||||||
@@ -1514,7 +1852,11 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
bulk_out_mps = ep_mps;
|
bulk_out_mps = ep_mps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ep_pos += cfg_desc[ep_pos];
|
} else if (cfg_desc[ep_pos + 1] == 4) {
|
||||||
|
/* 下一个 interface — 停止扫描 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ep_pos += ep_len;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1524,6 +1866,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
|
|
||||||
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
|
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
|
||||||
g_log->warn("[xhci] no MSC BOT interface found");
|
g_log->warn("[xhci] no MSC BOT interface found");
|
||||||
|
FB_ENUM_FAIL(0x0D); /* 黄: 未找到 MSC BOT 接口/端点 */
|
||||||
return -6;
|
return -6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1543,6 +1886,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
bulk_out_mps, bulk_in_mps);
|
bulk_out_mps, bulk_in_mps);
|
||||||
if (xhci_configure_endpoint(slot_id) != 0) {
|
if (xhci_configure_endpoint(slot_id) != 0) {
|
||||||
g_log->warn("[xhci] Configure Endpoint failed");
|
g_log->warn("[xhci] Configure Endpoint failed");
|
||||||
|
FB_ENUM_FAIL(0x0E); /* 黄: Configure Endpoint 失败 */
|
||||||
return -7;
|
return -7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1584,6 +1928,8 @@ static int xhci_enum_msc_device(u32 port_idx) {
|
|||||||
* driver_init — main entry point
|
* 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")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
@@ -1599,9 +1945,15 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
/* ---- Phase 1: PCI discovery ---- */
|
/* ---- Phase 1: PCI discovery ---- */
|
||||||
u8 bus = 0, dev = 0, func = 0;
|
u8 bus = 0, dev = 0, func = 0;
|
||||||
if (xhci_find(&bus, &dev, &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");
|
g_log->info("[xhci] xHCI controller not found");
|
||||||
g_log->info("[xhci] driver ready");
|
/* BUG-FIX: 不 halt。xhci 是可选驱动(manifest required=false),
|
||||||
|
* 无 xHCI 的平台(Intel 6 系/纯 EHCI)必须继续引导到 EHCI/DSK,
|
||||||
|
* 否则 `for(;;)hlt` 卡死整条启动链。仅记日志,如需诊断可开
|
||||||
|
* xhci_pci_diag_code() 打印控制器清单(见注释)。 */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1616,6 +1968,18 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
log_hex("[xhci] vendor=", vd & 0xffff);
|
log_hex("[xhci] vendor=", vd & 0xffff);
|
||||||
log_hex("[xhci] device=", vd >> 16);
|
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) */
|
/* Enable PCI MEM + BUSM, disable INTx (polling mode) */
|
||||||
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
|
||||||
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
|
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
|
||||||
@@ -1628,8 +1992,8 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
u64 bar_phys;
|
u64 bar_phys;
|
||||||
if (bar0_lo & 1u) {
|
if (bar0_lo & 1u) {
|
||||||
g_log->warn("[xhci] BAR0 is IO space; unsupported");
|
g_log->warn("[xhci] BAR0 is IO space; unsupported");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x02); /* 红: BAR0 是 IO 空间 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
if ((bar0_lo & 0x6u) == 0x4u) {
|
if ((bar0_lo & 0x6u) == 0x4u) {
|
||||||
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
|
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
|
||||||
@@ -1644,23 +2008,23 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
if (!api->hhdm_offset || !bar_phys) {
|
if (!api->hhdm_offset || !bar_phys) {
|
||||||
g_log->warn("[xhci] missing HHDM or BAR0; skip");
|
g_log->warn("[xhci] missing HHDM or BAR0; skip");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x02); /* 红: 缺 HHDM/BAR */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 1: MMIO mapping ---- */
|
/* ---- Phase 1: MMIO mapping ---- */
|
||||||
g_mmio = xhci_map_bar(api, bar_phys);
|
g_mmio = xhci_map_bar(api, bar_phys);
|
||||||
if (!g_mmio) {
|
if (!g_mmio) {
|
||||||
g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
|
g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x03); /* 红: BAR MMIO 映射失败 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 1: Parse capabilities ---- */
|
/* ---- Phase 1: Parse capabilities ---- */
|
||||||
if (xhci_parse_caps() != 0) {
|
if (xhci_parse_caps() != 0) {
|
||||||
g_log->error("[xhci] capability parse failed");
|
g_log->error("[xhci] capability parse failed");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x04); /* 红: capability 解析失败 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 1: BIOS Handoff ---- */
|
/* ---- Phase 1: BIOS Handoff ---- */
|
||||||
@@ -1669,28 +2033,28 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
/* ---- Phase 1: Controller Reset ---- */
|
/* ---- Phase 1: Controller Reset ---- */
|
||||||
if (xhci_controller_reset() != 0) {
|
if (xhci_controller_reset() != 0) {
|
||||||
g_log->error("[xhci] controller reset failed");
|
g_log->error("[xhci] controller reset failed");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x05); /* 红: 控制器 reset 失败 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
|
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
|
||||||
if (!api->dma) {
|
if (!api->dma) {
|
||||||
g_log->warn("[xhci] DMA API unavailable");
|
g_log->warn("[xhci] DMA API unavailable");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x06); /* 红: DMA API 缺失 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xhci_setup_rings() != 0) {
|
if (xhci_setup_rings() != 0) {
|
||||||
g_log->error("[xhci] ring setup failed");
|
g_log->error("[xhci] ring setup failed");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x06); /* 红: ring 初始化失败 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 2: Start Controller ---- */
|
/* ---- Phase 2: Start Controller ---- */
|
||||||
if (xhci_controller_start() != 0) {
|
if (xhci_controller_start() != 0) {
|
||||||
g_log->error("[xhci] controller start failed");
|
g_log->error("[xhci] controller start failed");
|
||||||
g_log->info("[xhci] driver ready");
|
FB_FATAL(api, 0x07); /* 红: 控制器启动失败 */
|
||||||
return 0;
|
XHCI_TRY_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Phase 3: Port Scan + Device Enumeration ---- */
|
/* ---- Phase 3: Port Scan + Device Enumeration ---- */
|
||||||
@@ -1700,53 +2064,57 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
* attachment and update port status. Without this delay, PORTSC.CCS
|
* attachment and update port status. Without this delay, PORTSC.CCS
|
||||||
* may read 0 even though a device is physically connected.
|
* may read 0 even though a device is physically connected.
|
||||||
*/
|
*/
|
||||||
g_log->info("[xhci] waiting 100ms for port settle...");
|
/* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。
|
||||||
dkm_delay_ms(100);
|
* 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;
|
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++) {
|
for (u32 port = 0; port < g_max_ports; port++) {
|
||||||
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
|
||||||
u32 portsc = mmio_read32(port_off);
|
u32 portsc = mmio_read32(port_off);
|
||||||
|
|
||||||
/* Always log PORTSC for debugging, even if no device is connected */
|
|
||||||
log_hex("[xhci] port=", port);
|
log_hex("[xhci] port=", port);
|
||||||
log_hex("[xhci] PORTSC=", portsc);
|
log_hex("[xhci] PORTSC=", portsc);
|
||||||
|
|
||||||
if (!(portsc & PORTSC_CCS)) {
|
if (!(portsc & PORTSC_CCS)) continue;
|
||||||
/*
|
|
||||||
* If PPC (Per-Port Power Control) is supported, the port may
|
|
||||||
* need to be powered on before CCS reflects device connection.
|
|
||||||
* Set PP (Port Power) bit in PORTSC to enable port power.
|
|
||||||
* If PPC=0, ports are always powered and this write is a no-op.
|
|
||||||
*/
|
|
||||||
if (g_ppc && !(portsc & (1u << 9))) {
|
|
||||||
g_log->info("[xhci] enabling port power");
|
|
||||||
mmio_write32(port_off, portsc | (1u << 9));
|
|
||||||
/* Wait briefly for power to stabilize */
|
|
||||||
dkm_delay_ms(20);
|
|
||||||
portsc = mmio_read32(port_off);
|
|
||||||
log_hex("[xhci] PORTSC after PP=", portsc);
|
|
||||||
}
|
|
||||||
if (!(portsc & PORTSC_CCS)) continue; /* Still no device */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
saw_ccs = 1;
|
||||||
g_log->info("[xhci] device connected");
|
g_log->info("[xhci] device connected");
|
||||||
|
|
||||||
/* Port Reset */
|
|
||||||
if (xhci_reset_port(port) != 0) {
|
if (xhci_reset_port(port) != 0) {
|
||||||
g_log->warn("[xhci] port reset failed; skipping");
|
g_log->warn("[xhci] port reset failed");
|
||||||
|
saw_reset_fail = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
g_log->info("[xhci] port reset OK");
|
g_log->info("[xhci] port reset OK");
|
||||||
|
|
||||||
/* Try to enumerate as MSC device */
|
|
||||||
if (xhci_enum_msc_device(port) == 0) {
|
if (xhci_enum_msc_device(port) == 0) {
|
||||||
g_log->info("[xhci] MSC enumeration successful");
|
g_log->info("[xhci] MSC enumeration successful");
|
||||||
break; /* First MSC device is enough for now */
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log->warn("[xhci] MSC enumeration failed; trying next port");
|
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 ---- */
|
/* ---- Phase 5: Register Block Device ---- */
|
||||||
if (g_msc.ready) {
|
if (g_msc.ready) {
|
||||||
@@ -1765,11 +2133,18 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g_log->info("[xhci] no MSC device found; block provider not registered");
|
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");
|
g_log->info("[xhci] driver ready");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#undef XHCI_TRY_NEXT
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
int driver_exit(struct dkm_driver_handle *handle) {
|
int driver_exit(struct dkm_driver_handle *handle) {
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
CC := clang
|
CC ?= clang
|
||||||
LD := ld.lld
|
LD ?= ld.lld
|
||||||
OBJCOPY := llvm-objcopy
|
OBJCOPY := llvm-objcopy
|
||||||
|
|
||||||
ROOT := ../..
|
ROOT := ../..
|
||||||
@@ -9,8 +9,8 @@ CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-
|
|||||||
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
|
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
|
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 dkm/*.c test/*.c vmm/*.c pe/*.c)
|
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)
|
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S sched/*.S)
|
||||||
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
|
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
|
||||||
DEPS := $(C_SOURCES:.c=.d)
|
DEPS := $(C_SOURCES:.c=.d)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
arch/x86_64/halt.o: arch\x86_64\halt.c
|
|
||||||
Binary file not shown.
+97
-19
@@ -2,6 +2,7 @@
|
|||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
#include <utsm/instr.h>
|
#include <utsm/instr.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
|
|
||||||
/* IDT gate descriptor (64-bit) */
|
/* IDT gate descriptor (64-bit) */
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
@@ -405,6 +406,43 @@ static void pic_remap(void) {
|
|||||||
outb(PIC2_DATA, 0xFF); /* mask all slave */
|
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 一致。
|
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
|
||||||
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
|
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
|
||||||
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
|
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
|
||||||
@@ -559,6 +597,8 @@ int irq_vector_alloc(void) {
|
|||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
log_warn("[IDT] vector alloc failed: pool exhausted");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,6 +631,26 @@ int irq_register(u8 vector, irq_handler_t handler) {
|
|||||||
return 0;
|
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 */
|
/* Stack frame pushed by isr_common in idt.S */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
|
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
|
||||||
@@ -601,30 +661,48 @@ typedef struct {
|
|||||||
|
|
||||||
void idt_handler(isr_frame *frame) {
|
void idt_handler(isr_frame *frame) {
|
||||||
if (frame->vector < 32) {
|
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: 当前页表基址。
|
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
|
||||||
* 寄存器 dump 用于定位 fault 指令的操作数来源。 */
|
* 寄存器 dump 用于定位 fault 指令的操作数来源。
|
||||||
|
* F1: 异常统一走莲花崩溃屏(panic_full 内部保留串口 dump +
|
||||||
|
* DRR 归档 + 帧缓冲直绘,最后 halt——不返回)。 */
|
||||||
u64 cr2, cr3;
|
u64 cr2, cr3;
|
||||||
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
|
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
|
||||||
|
(void)cr3;
|
||||||
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
|
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
|
||||||
log_error("[IDT] exception");
|
panic_regs pr;
|
||||||
log_hex64("[IDT] vector=", frame->vector);
|
pr.rip = frame->rip;
|
||||||
log_hex64("[IDT] err=", frame->error_code);
|
pr.rsp = 0; /* isr_frame 不含 rsp(stub 内使用),置 0 */
|
||||||
log_hex64("[IDT] rip=", frame->rip);
|
pr.rflags = frame->rflags;
|
||||||
log_hex64("[IDT] cr2=", cr2);
|
pr.cs = frame->cs;
|
||||||
log_hex64("[IDT] cr3=", cr3);
|
pr.err = frame->error_code;
|
||||||
log_hex64("[IDT] rax=", frame->rax);
|
pr.vector = frame->vector;
|
||||||
log_hex64("[IDT] rcx=", frame->rcx);
|
pr.cr2 = cr2;
|
||||||
log_hex64("[IDT] rdx=", frame->rdx);
|
pr.rax = frame->rax;
|
||||||
log_hex64("[IDT] rsi=", frame->rsi);
|
pr.rcx = frame->rcx;
|
||||||
log_hex64("[IDT] rdi=", frame->rdi);
|
pr.rdx = frame->rdx;
|
||||||
log_hex64("[IDT] r8=", frame->r8);
|
pr.rsi = frame->rsi;
|
||||||
log_hex64("[IDT] r9=", frame->r9);
|
pr.rdi = frame->rdi;
|
||||||
log_hex64("[IDT] r10=", frame->r10);
|
pr.r8 = frame->r8;
|
||||||
log_hex64("[IDT] r11=", frame->r11);
|
pr.r9 = frame->r9;
|
||||||
log_hex64("[IDT] cs=", frame->cs);
|
pr.r10 = frame->r10;
|
||||||
log_hex64("[IDT] rflags=", frame->rflags);
|
pr.r11 = frame->r11;
|
||||||
for (;;) { __asm__ volatile ("cli; hlt"); }
|
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)
|
/* PIC IRQ: vectors 0x20-0x2F (32-47)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
arch/x86_64/idt.o: arch\x86_64\idt.c include\utsm\idt.h \
|
|
||||||
include\utsm\types.h include\utsm\log.h include\utsm\instr.h
|
|
||||||
include\utsm\idt.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
include\utsm\instr.h:
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
|||||||
arch/x86_64/io.o: arch\x86_64\io.c include\utsm\types.h
|
|
||||||
include\utsm\types.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,192 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/* 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,2 +0,0 @@
|
|||||||
arch/x86_64/serial.o: arch\x86_64\serial.c include\utsm\types.h
|
|
||||||
include\utsm\types.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
|||||||
|
/* ===================================================================
|
||||||
|
* 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,9 +0,0 @@
|
|||||||
core/capability.o: core\capability.c include\utsm\capability.h \
|
|
||||||
include\utsm\types.h include\utsm\status.h include\utsm\segment.h \
|
|
||||||
include\utsm\uuid.h include\utsm\dirty.h
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\segment.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\dirty.h:
|
|
||||||
Binary file not shown.
@@ -1,11 +0,0 @@
|
|||||||
core/dirty.o: core\dirty.c include\utsm\dirty.h include\utsm\types.h \
|
|
||||||
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
|
|
||||||
include\utsm\capability.h include\utsm\arena.h include\utsm\config.h
|
|
||||||
include\utsm\dirty.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\segment.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\arena.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
|||||||
core/dmp.o: core\dmp.c include\utsm\dmp.h include\utsm\types.h \
|
|
||||||
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
|
|
||||||
include\utsm\capability.h include\utsm\dirty.h
|
|
||||||
include\utsm\dmp.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\segment.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\dirty.h:
|
|
||||||
Binary file not shown.
@@ -1,13 +0,0 @@
|
|||||||
core/process.o: core\process.c include\utsm\process.h \
|
|
||||||
include\utsm\types.h include\utsm\uuid.h include\utsm\capability.h \
|
|
||||||
include\utsm\status.h include\utsm\config.h include\utsm\drr.h \
|
|
||||||
include\utsm\arena.h include\utsm\log.h
|
|
||||||
include\utsm\process.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
include\utsm\drr.h:
|
|
||||||
include\utsm\arena.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
|||||||
|
/* ===================================================================
|
||||||
|
* 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,14 +0,0 @@
|
|||||||
core/segment.o: core\segment.c include\utsm\segment.h \
|
|
||||||
include\utsm\types.h include\utsm\uuid.h include\utsm\status.h \
|
|
||||||
include\utsm\capability.h include\utsm\dirty.h include\utsm\process.h \
|
|
||||||
include\utsm\config.h include\utsm\arena.h include\utsm\dmp.h
|
|
||||||
include\utsm\segment.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\dirty.h:
|
|
||||||
include\utsm\process.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
include\utsm\arena.h:
|
|
||||||
include\utsm\dmp.h:
|
|
||||||
Binary file not shown.
@@ -1,7 +1,9 @@
|
|||||||
#include <utsm/utsm.h>
|
#include <utsm/utsm.h>
|
||||||
#include <utsm/segment.h>
|
#include <utsm/segment.h>
|
||||||
#include <utsm/pckc.h>
|
#include <utsm/pckc.h>
|
||||||
|
#include <utsm/crypto.h>
|
||||||
#include <utsm/process.h>
|
#include <utsm/process.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/config.h>
|
#include <utsm/config.h>
|
||||||
|
|
||||||
@@ -23,6 +25,26 @@ void utsm_init(void) {
|
|||||||
utsm_pckc_init();
|
utsm_pckc_init();
|
||||||
log_info("[UTSM] pckc init ok");
|
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_process_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
core/utsm_init.o: core\utsm_init.c include\utsm\utsm.h \
|
|
||||||
include\utsm\types.h include\utsm\status.h include\utsm\config.h \
|
|
||||||
include\utsm\uuid.h include\utsm\process.h include\utsm\capability.h \
|
|
||||||
include\utsm\segment.h include\utsm\dirty.h include\utsm\utrw.h \
|
|
||||||
include\utsm\vmm.h include\utsm\linux_loader.h include\utsm\ipc_shm.h \
|
|
||||||
..\utsm-ipc\ipc_proto.h include\utsm\hypercall.h include\utsm\pckc.h \
|
|
||||||
include\utsm\crypto.h include\utsm\log.h
|
|
||||||
include\utsm\utsm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\status.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\process.h:
|
|
||||||
include\utsm\capability.h:
|
|
||||||
include\utsm\segment.h:
|
|
||||||
include\utsm\dirty.h:
|
|
||||||
include\utsm\utrw.h:
|
|
||||||
include\utsm\vmm.h:
|
|
||||||
include\utsm\linux_loader.h:
|
|
||||||
include\utsm\ipc_shm.h:
|
|
||||||
..\utsm-ipc\ipc_proto.h:
|
|
||||||
include\utsm\hypercall.h:
|
|
||||||
include\utsm\pckc.h:
|
|
||||||
include\utsm\crypto.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,137 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/* 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-14
@@ -1,19 +1,31 @@
|
|||||||
#include <utsm/crypto.h>
|
/* kdf.c — 段密钥派生(Phase 9:keyed BLAKE2b)
|
||||||
#include <utsm/drr.h>
|
*
|
||||||
|
* segment_key = BLAKE2b-256(key=root_key, "UTSMSEGK"||p_uuid||s_uuid||key_epoch)
|
||||||
|
* root key 不直接加密数据;UUID 作为派生调谐因子(非明文密钥)。
|
||||||
|
* 确定性:同 (puuid, suuid, epoch) 恒得同 key —— PCKC 缓存正确性依赖此性质。
|
||||||
|
*/
|
||||||
|
|
||||||
static u64 mix64(u64 x) {
|
#include <utsm/crypto.h>
|
||||||
x ^= x >> 30;
|
#include <utsm/blake2b.h>
|
||||||
x *= 0xbf58476d1ce4e5b9ULL;
|
#include <utsm/drr.h>
|
||||||
x ^= x >> 27;
|
|
||||||
x *= 0x94d049bb133111ebULL;
|
|
||||||
x ^= x >> 31;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) {
|
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();
|
const u64 *root = drr_get_root_key();
|
||||||
out_key->words[0] = mix64(root[0] ^ process_uuid.hi ^ segment_uuid.lo ^ key_epoch);
|
|
||||||
out_key->words[1] = mix64(root[1] ^ process_uuid.lo ^ segment_uuid.hi ^ (key_epoch << 1));
|
u8 root_le[32];
|
||||||
out_key->words[2] = mix64(root[2] ^ process_uuid.hi ^ segment_uuid.hi ^ (key_epoch << 7));
|
u8 data[8 + 8 + 8 + 8 + 8];
|
||||||
out_key->words[3] = mix64(root[3] ^ process_uuid.lo ^ segment_uuid.lo ^ (key_epoch << 13));
|
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,7 +0,0 @@
|
|||||||
crypto/kdf.o: crypto\kdf.c include\utsm\crypto.h include\utsm\types.h \
|
|
||||||
include\utsm\uuid.h include\utsm\drr.h include\utsm\config.h
|
|
||||||
include\utsm\crypto.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\drr.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
+19
-12
@@ -1,19 +1,26 @@
|
|||||||
|
/* 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/crypto.h>
|
||||||
|
#include <utsm/blake2b.h>
|
||||||
#include <utsm/config.h>
|
#include <utsm/config.h>
|
||||||
|
|
||||||
static u64 next_stream_word(u64 x) {
|
|
||||||
x ^= x << 13;
|
|
||||||
x ^= x >> 7;
|
|
||||||
x ^= x << 17;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) {
|
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) {
|
||||||
u64 state = key->words[0] ^ key->words[1] ^ key->words[2] ^ key->words[3] ^ tweak;
|
u32 kw[8];
|
||||||
for (u64 i = 0; i < UTSM_CACHE_LINE_SIZE; i += 8) {
|
for (int i = 0; i < 4; i++) {
|
||||||
state = next_stream_word(state + i + 0xa5a5a5a5a5a5a5a5ULL);
|
kw[2 * i] = (u32)(key->words[i] & 0xFFFFFFFFu);
|
||||||
for (u64 j = 0; j < 8; j++) {
|
kw[2 * i + 1] = (u32)(key->words[i] >> 32);
|
||||||
out[i + j] = in[i + j] ^ (u8)(state >> (j * 8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,6 +0,0 @@
|
|||||||
crypto/stream.o: crypto\stream.c include\utsm\crypto.h \
|
|
||||||
include\utsm\types.h include\utsm\uuid.h include\utsm\config.h
|
|
||||||
include\utsm\crypto.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
crypto/tweak.o: crypto\tweak.c include\utsm\crypto.h include\utsm\types.h \
|
|
||||||
include\utsm\uuid.h
|
|
||||||
include\utsm\crypto.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\uuid.h:
|
|
||||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
#include <utsm/arena.h>
|
#include <utsm/arena.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
#include "../arch/x86_64/limine.h"
|
#include "../arch/x86_64/limine.h"
|
||||||
|
|
||||||
#define ELF_MAGIC0 0x7f
|
#define ELF_MAGIC0 0x7f
|
||||||
@@ -28,6 +29,10 @@
|
|||||||
#define R_X86_64_32 10
|
#define R_X86_64_32 10
|
||||||
#define R_X86_64_32S 11
|
#define R_X86_64_32S 11
|
||||||
#define R_X86_64_PC32 2
|
#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 *memset(void *dst, int value, usize len);
|
||||||
void *memcpy(void *dst, const void *src, usize len);
|
void *memcpy(void *dst, const void *src, usize len);
|
||||||
@@ -178,10 +183,14 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
|
|||||||
|
|
||||||
if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) {
|
if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) {
|
||||||
log_error("[DKM] ELF has no section table");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
|
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
|
||||||
log_error("[DKM] ELF section table out of range");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,10 +211,14 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
|
|||||||
|
|
||||||
if (!symtab || !strtab || strtab->type != SHT_STRTAB) {
|
if (!symtab || !strtab || strtab->type != SHT_STRTAB) {
|
||||||
log_error("[DKM] missing symtab/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;
|
return -1;
|
||||||
}
|
}
|
||||||
if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) {
|
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");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +254,8 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
log_error("[DKM] required driver symbols missing");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,12 +286,16 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
|
|||||||
|
|
||||||
if (total_size == 0) {
|
if (total_size == 0) {
|
||||||
log_error("[DKM] no allocatable sections");
|
log_error("[DKM] no allocatable sections");
|
||||||
|
panic_full("DKM-E35 ELF NO ALLOC SECTIONS",
|
||||||
|
"dkm_load_elf_rel: no allocatable sections", 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
|
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
log_error("[DKM] failed to allocate module memory");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
memset(image, 0, total_size);
|
memset(image, 0, total_size);
|
||||||
@@ -348,18 +367,20 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
|
|||||||
patch = S + A;
|
patch = S + A;
|
||||||
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
|
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
|
||||||
patch = (u64)((i64)(i32)(S + A));
|
patch = (u64)((i64)(i32)(S + A));
|
||||||
} else if (reloc_type == R_X86_64_PC32) {
|
} else if (reloc_type == R_X86_64_PC32 || reloc_type == R_X86_64_PLT32) {
|
||||||
patch = (u64)((i64)(i32)(S + A - P));
|
patch = (u64)((i64)(i32)(S + A - P));
|
||||||
} else {
|
} else {
|
||||||
log_error("[DKM] unsupported relocation type");
|
log_error("[DKM] unsupported relocation type");
|
||||||
log_hex64("[DKM] reloc type=", (u64)reloc_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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bounds check: use the actual write width, not always 8. */
|
/* Bounds check: use the actual write width, not always 8. */
|
||||||
u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4;
|
u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4;
|
||||||
if (rela->offset + reloc_width <= target->size) {
|
if (rela->offset + reloc_width <= target->size) {
|
||||||
if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S || reloc_type == R_X86_64_PC32) {
|
if (reloc_type != R_X86_64_64) {
|
||||||
*(u32 *)(target_base + rela->offset) = (u32)patch;
|
*(u32 *)(target_base + rela->offset) = (u32)patch;
|
||||||
} else {
|
} else {
|
||||||
*(u64 *)(target_base + rela->offset) = patch;
|
*(u64 *)(target_base + rela->offset) = patch;
|
||||||
@@ -373,6 +394,8 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
|
|||||||
const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value);
|
const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value);
|
||||||
if (desc->magic != DKM_DRIVER_MAGIC) {
|
if (desc->magic != DKM_DRIVER_MAGIC) {
|
||||||
log_error("[DKM] external driver_desc magic mismatch");
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,11 +413,17 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
|
|||||||
handle.load_stage = desc->stage;
|
handle.load_stage = desc->stage;
|
||||||
handle.flags = desc->flags;
|
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);
|
int result = init(dkm_get_kernel_api(), &handle);
|
||||||
|
dkm_bus_set_current_provider(0);
|
||||||
|
dkm_log_set_driver(0);
|
||||||
handle.init_status = result;
|
handle.init_status = result;
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
handle.state = DKM_STATE_FAILED;
|
handle.state = DKM_STATE_FAILED;
|
||||||
log_error("[DKM] external driver init 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;
|
return result;
|
||||||
}
|
}
|
||||||
handle.state = DKM_STATE_ACTIVE;
|
handle.state = DKM_STATE_ACTIVE;
|
||||||
@@ -406,6 +435,8 @@ void dkm_scan_boot_modules(void) {
|
|||||||
struct limine_module_response *response = g_module_request.response;
|
struct limine_module_response *response = g_module_request.response;
|
||||||
if (!response) {
|
if (!response) {
|
||||||
log_warn("[DKM] no Limine module 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,6 +447,8 @@ void dkm_scan_boot_modules(void) {
|
|||||||
struct limine_file *file = response->modules[i];
|
struct limine_file *file = response->modules[i];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
log_warn("[DKM] null module entry");
|
log_warn("[DKM] null module entry");
|
||||||
|
panic_full("DKM-E41 NULL MODULE ENTRY",
|
||||||
|
"dkm_scan_boot_modules: null boot module entry", 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,11 +470,15 @@ void dkm_scan_boot_modules(void) {
|
|||||||
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
|
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
|
||||||
if (load_result != 0) {
|
if (load_result != 0) {
|
||||||
log_error("[DKM] external driver load failed");
|
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 {
|
} else {
|
||||||
log_error("[DKM] module ELF64 rejected");
|
log_error("[DKM] module ELF64 rejected");
|
||||||
log_hex64("[DKM] elf status=", (u64)(i64)status);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
dkm/boot_modules.o: dkm\boot_modules.c include\utsm\dkm.h \
|
|
||||||
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
|
||||||
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
|
||||||
include\utsm\arena.h dkm\..\arch\x86_64\limine.h
|
|
||||||
include\utsm\dkm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\dma.h:
|
|
||||||
include\utsm\block.h:
|
|
||||||
include\utsm\net.h:
|
|
||||||
include\utsm\paging.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
include\utsm\arena.h:
|
|
||||||
dkm\..\arch\x86_64\limine.h:
|
|
||||||
Binary file not shown.
@@ -0,0 +1,318 @@
|
|||||||
|
/* ===================================================================
|
||||||
|
* 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,9 +0,0 @@
|
|||||||
dkm/console_early.o: dkm\console_early.c include\utsm\dkm.h \
|
|
||||||
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
|
||||||
include\utsm\net.h include\utsm\paging.h
|
|
||||||
include\utsm\dkm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\dma.h:
|
|
||||||
include\utsm\block.h:
|
|
||||||
include\utsm\net.h:
|
|
||||||
include\utsm\paging.h:
|
|
||||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
#include <utsm/dkm.h>
|
#include <utsm/dkm.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
|
|
||||||
static int dkm_validate_desc(const dkm_driver_desc *desc) {
|
static int dkm_validate_desc(const dkm_driver_desc *desc) {
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
@@ -27,18 +28,24 @@ void dkm_init(void) {
|
|||||||
log_info("[DKM] init ok");
|
log_info("[DKM] init ok");
|
||||||
} else {
|
} else {
|
||||||
log_error("[DKM] init failed");
|
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) {
|
int dkm_load_builtin(const dkm_builtin_driver *driver) {
|
||||||
if (!driver || !driver->desc || !driver->init) {
|
if (!driver || !driver->desc || !driver->init) {
|
||||||
log_error("[DKM] invalid builtin driver");
|
log_error("[DKM] invalid builtin driver");
|
||||||
|
panic_full("DKM-E51 INVALID BUILTIN DRIVER",
|
||||||
|
"dkm_load_builtin: builtin driver descriptor invalid", 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = dkm_validate_desc(driver->desc);
|
int status = dkm_validate_desc(driver->desc);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
log_error("[DKM] driver_desc rejected");
|
log_error("[DKM] driver_desc rejected");
|
||||||
|
panic_full("DKM-E52 DRIVER DESC REJECTED",
|
||||||
|
"dkm_load_builtin: builtin driver_desc validation failed", 0);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,13 +58,17 @@ int dkm_load_builtin(const dkm_builtin_driver *driver) {
|
|||||||
|
|
||||||
log_info("[DKM] builtin driver init begin");
|
log_info("[DKM] builtin driver init begin");
|
||||||
log_info(driver->desc->name);
|
log_info(driver->desc->name);
|
||||||
|
dkm_log_set_driver(driver->desc->name); /* D4: 驱动自己的颜色 */
|
||||||
handle.state = DKM_STATE_INITING;
|
handle.state = DKM_STATE_INITING;
|
||||||
status = driver->init(dkm_get_kernel_api(), &handle);
|
status = driver->init(dkm_get_kernel_api(), &handle);
|
||||||
|
dkm_log_set_driver(0);
|
||||||
handle.init_status = status;
|
handle.init_status = status;
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
handle.state = DKM_STATE_FAILED;
|
handle.state = DKM_STATE_FAILED;
|
||||||
log_error("[DKM] builtin driver init 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;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
dkm/dkm_core.o: dkm\dkm_core.c include\utsm\dkm.h include\utsm\types.h \
|
|
||||||
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
|
||||||
include\utsm\paging.h include\utsm\log.h
|
|
||||||
include\utsm\dkm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\dma.h:
|
|
||||||
include\utsm\block.h:
|
|
||||||
include\utsm\net.h:
|
|
||||||
include\utsm\paging.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
Binary file not shown.
+115
-5
@@ -3,6 +3,10 @@
|
|||||||
#include <utsm/idt.h>
|
#include <utsm/idt.h>
|
||||||
#include <utsm/net.h>
|
#include <utsm/net.h>
|
||||||
#include <utsm/mm.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) {
|
static void dkm_api_info(const char *msg) {
|
||||||
log_info(msg);
|
log_info(msg);
|
||||||
@@ -17,10 +21,9 @@ static void dkm_api_error(const char *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void dkm_api_panic(const char *msg) {
|
static void dkm_api_panic(const char *msg) {
|
||||||
log_error(msg);
|
/* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲)。
|
||||||
for (;;) {
|
* fallback 模式下返回驱动,由驱动调用点走其备用路径。 */
|
||||||
__asm__ volatile("cli; hlt");
|
panic_full("DKM-PANIC", msg, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const dkm_log_api g_log_api = {
|
static const dkm_log_api g_log_api = {
|
||||||
@@ -30,6 +33,27 @@ static const dkm_log_api g_log_api = {
|
|||||||
.panic = dkm_api_panic
|
.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 = {
|
static dkm_kernel_api g_kernel_api = {
|
||||||
.version = DKM_KERNEL_API_VERSION,
|
.version = DKM_KERNEL_API_VERSION,
|
||||||
.size = sizeof(dkm_kernel_api),
|
.size = sizeof(dkm_kernel_api),
|
||||||
@@ -57,13 +81,59 @@ static dkm_kernel_api g_kernel_api = {
|
|||||||
.mmio = 0,
|
.mmio = 0,
|
||||||
.mm_map_mmio = 0,
|
.mm_map_mmio = 0,
|
||||||
.mm_unmap_mmio = 0,
|
.mm_unmap_mmio = 0,
|
||||||
.register_apic_eoi = 0
|
.register_apic_eoi = 0,
|
||||||
|
.sched = 0,
|
||||||
|
.kapi = 0,
|
||||||
|
.bus = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
||||||
return &g_kernel_api;
|
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"
|
#include "../arch/x86_64/limine.h"
|
||||||
extern volatile struct limine_rsdp_request g_rsdp_request;
|
extern volatile struct limine_rsdp_request g_rsdp_request;
|
||||||
extern volatile struct limine_framebuffer_request g_fb_request;
|
extern volatile struct limine_framebuffer_request g_fb_request;
|
||||||
@@ -71,10 +141,18 @@ extern volatile struct limine_module_request g_module_request;
|
|||||||
extern volatile struct limine_hhdm_request g_hhdm_request;
|
extern volatile struct limine_hhdm_request g_hhdm_request;
|
||||||
|
|
||||||
void dkm_fill_platform_info(void) {
|
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.dma = dma_get_api();
|
||||||
g_kernel_api.net = net_get_api();
|
g_kernel_api.net = net_get_api();
|
||||||
g_kernel_api.block = block_get_api();
|
g_kernel_api.block = block_get_api();
|
||||||
g_kernel_api.mmio = paging_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) {
|
if (g_rsdp_request.response && g_rsdp_request.response->address) {
|
||||||
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
|
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
|
||||||
}
|
}
|
||||||
@@ -100,4 +178,36 @@ void dkm_fill_platform_info(void) {
|
|||||||
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
||||||
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
||||||
g_kernel_api.irq_vector_free = irq_vector_free;
|
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,14 +0,0 @@
|
|||||||
dkm/kernel_api.o: dkm\kernel_api.c include\utsm\dkm.h \
|
|
||||||
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
|
||||||
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
|
||||||
include\utsm\idt.h include\utsm\mm.h dkm\..\arch\x86_64\limine.h
|
|
||||||
include\utsm\dkm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\dma.h:
|
|
||||||
include\utsm\block.h:
|
|
||||||
include\utsm\net.h:
|
|
||||||
include\utsm\paging.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
include\utsm\idt.h:
|
|
||||||
include\utsm\mm.h:
|
|
||||||
dkm\..\arch\x86_64\limine.h:
|
|
||||||
Binary file not shown.
+319
-15
@@ -1,5 +1,6 @@
|
|||||||
#include <utsm/dkm.h>
|
#include <utsm/dkm.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
#include <utsm/types.h>
|
#include <utsm/types.h>
|
||||||
#include "../arch/x86_64/limine.h"
|
#include "../arch/x86_64/limine.h"
|
||||||
#include "../kernel/ini_parser.h"
|
#include "../kernel/ini_parser.h"
|
||||||
@@ -11,6 +12,7 @@ void dsm_load_by_manifest_ex(const ini_config *cfg);
|
|||||||
#define DSM_MAX_STAGES 4
|
#define DSM_MAX_STAGES 4
|
||||||
#define DSM_MAX_DEPENDS 4
|
#define DSM_MAX_DEPENDS 4
|
||||||
#define DSM_MAX_PROVIDES 4
|
#define DSM_MAX_PROVIDES 4
|
||||||
|
#define DSM_MAX_CAPS 128
|
||||||
|
|
||||||
typedef struct dsm_driver_entry {
|
typedef struct dsm_driver_entry {
|
||||||
char name[32];
|
char name[32];
|
||||||
@@ -23,6 +25,9 @@ typedef struct dsm_driver_entry {
|
|||||||
u32 provide_count;
|
u32 provide_count;
|
||||||
struct limine_file *boot_module;
|
struct limine_file *boot_module;
|
||||||
int loaded;
|
int loaded;
|
||||||
|
/* P0(万物互联):终态标志——已加载/被 FUCK 关闭/模块缺失/加载失败,
|
||||||
|
* 不再是本轮不动点迭代的候选。resolved 但 !loaded 的驱动不登记 provides。 */
|
||||||
|
int resolved;
|
||||||
} dsm_driver_entry;
|
} dsm_driver_entry;
|
||||||
|
|
||||||
typedef struct dsm_stage_entry {
|
typedef struct dsm_stage_entry {
|
||||||
@@ -36,6 +41,119 @@ typedef struct dsm_stage_entry {
|
|||||||
static dsm_stage_entry g_stages[DSM_MAX_STAGES];
|
static dsm_stage_entry g_stages[DSM_MAX_STAGES];
|
||||||
static u32 g_stage_count;
|
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;
|
extern volatile struct limine_module_request g_module_request;
|
||||||
|
|
||||||
static int dsm_skip_ws(const char **p) {
|
static int dsm_skip_ws(const char **p) {
|
||||||
@@ -135,6 +253,7 @@ static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) {
|
|||||||
drv->required = 1;
|
drv->required = 1;
|
||||||
drv->boot_module = 0;
|
drv->boot_module = 0;
|
||||||
drv->loaded = 0;
|
drv->loaded = 0;
|
||||||
|
drv->resolved = 0;
|
||||||
drv->class_id = 0;
|
drv->class_id = 0;
|
||||||
|
|
||||||
if (dsm_expect(p, '{') != 0) return;
|
if (dsm_expect(p, '{') != 0) return;
|
||||||
@@ -266,7 +385,12 @@ static int dsm_parse_manifest(const char *text, u64 size) {
|
|||||||
dsm_expect(&p, ':');
|
dsm_expect(&p, ':');
|
||||||
|
|
||||||
if (streq_static(key, "stages")) {
|
if (streq_static(key, "stages")) {
|
||||||
if (dsm_expect(&p, '[') != 0) return -1;
|
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) {
|
while (p < end && *p) {
|
||||||
dsm_skip_ws(&p);
|
dsm_skip_ws(&p);
|
||||||
if (*p == ']') { p++; break; }
|
if (*p == ']') { p++; break; }
|
||||||
@@ -290,15 +414,23 @@ void dsm_load_by_manifest(void) {
|
|||||||
dsm_load_by_manifest_ex(NULL);
|
dsm_load_by_manifest_ex(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsm_load_by_manifest_ex(const ini_config *cfg) {
|
static int dsm_ensure_manifest(void) {
|
||||||
struct limine_module_response *rsp = g_module_request.response;
|
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) {
|
if (!rsp) {
|
||||||
log_warn("[DSM] no Limine module response");
|
log_warn("[DSM] no Limine module response");
|
||||||
return;
|
/* 严格错误策略插桩:manifest 缺失零降级(DKM-E09) */
|
||||||
|
panic_full("DKM-E09 NO LIMINE MODULE RESPONSE",
|
||||||
|
"dsm_ensure_manifest: no Limine module response", 0);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct limine_file *manifest_file = 0;
|
manifest_file = 0;
|
||||||
for (u64 i = 0; i < rsp->module_count; i++) {
|
for (i = 0; i < rsp->module_count; i++) {
|
||||||
struct limine_file *f = rsp->modules[i];
|
struct limine_file *f = rsp->modules[i];
|
||||||
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
|
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
|
||||||
manifest_file = f;
|
manifest_file = f;
|
||||||
@@ -308,70 +440,242 @@ void dsm_load_by_manifest_ex(const ini_config *cfg) {
|
|||||||
|
|
||||||
if (!manifest_file) {
|
if (!manifest_file) {
|
||||||
log_warn("[DSM] manifest not found in boot modules");
|
log_warn("[DSM] manifest not found in boot modules");
|
||||||
return;
|
/* 严格错误策略插桩: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");
|
log_info("[DSM] parsing manifest");
|
||||||
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
|
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
|
||||||
log_info("[DSM] manifest parsed");
|
log_info("[DSM] manifest parsed");
|
||||||
log_hex64("[DSM] stages=", (u64)g_stage_count);
|
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++) {
|
for (u32 si = 0; si < g_stage_count; si++) {
|
||||||
dsm_stage_entry *stage = &g_stages[si];
|
dsm_stage_entry *stage = &g_stages[si];
|
||||||
|
if (limit_stage && stage->id > max_stage_id) continue;
|
||||||
log_info("[DSM] stage begin");
|
log_info("[DSM] stage begin");
|
||||||
log_info(stage->name);
|
log_info(stage->name);
|
||||||
log_hex64("[DSM] driver count=", stage->driver_count);
|
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++) {
|
for (u32 di = 0; di < stage->driver_count; di++) {
|
||||||
dsm_driver_entry *drv = &stage->drivers[di];
|
dsm_driver_entry *drv = &stage->drivers[di];
|
||||||
|
struct limine_file *f;
|
||||||
|
struct dkm_symbol_scan symbols;
|
||||||
|
int result;
|
||||||
|
|
||||||
/* === FUCK [drivers] 驱动开关检查 === */
|
if (drv->resolved) continue;
|
||||||
|
|
||||||
|
/* === FUCK [drivers] 驱动开关检查 ===
|
||||||
|
* 关闭即退出候选(provides 不登记,依赖它的模块将 DKM-E44)。 */
|
||||||
if (cfg) {
|
if (cfg) {
|
||||||
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
|
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
log_info("[DSM] driver disabled by FUCK: ");
|
log_info("[DSM] driver disabled by FUCK: ");
|
||||||
log_info(drv->name);
|
log_info(drv->name);
|
||||||
continue; /* 跳过此驱动 */
|
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);
|
drv->boot_module = dsm_find_boot_module(drv->path);
|
||||||
if (!drv->boot_module) {
|
if (!drv->boot_module) {
|
||||||
log_info("[DSM] module not found");
|
log_info("[DSM] module not found");
|
||||||
log_info(drv->name);
|
log_info(drv->name);
|
||||||
|
drv->resolved = 1;
|
||||||
|
pending--;
|
||||||
if (drv->required && stage->required) {
|
if (drv->required && stage->required) {
|
||||||
log_error("[DSM] required module missing, abort stage");
|
/* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */
|
||||||
|
panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING",
|
||||||
|
"required driver boot module not found in manifest stage", 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct limine_file *f = drv->boot_module;
|
f = drv->boot_module;
|
||||||
log_info("[DSM] loading driver");
|
log_info("[DSM] loading driver");
|
||||||
log_info(drv->name);
|
log_info(drv->name);
|
||||||
|
|
||||||
if (dkm_check_elf64(f->address, f->size) != 0) {
|
if (dkm_check_elf64(f->address, f->size) != 0) {
|
||||||
log_error("[DSM] ELF check failed");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dkm_symbol_scan symbols;
|
|
||||||
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
|
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
|
||||||
log_error("[DSM] symbol scan failed");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = dkm_load_elf_rel(f->address, f->size, &symbols);
|
result = dkm_load_elf_rel(f->address, f->size, &symbols);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
log_error("[DSM] load failed");
|
log_error("[DSM] load failed");
|
||||||
if (drv->required && stage->required) {
|
if (drv->required && stage->required) {
|
||||||
log_error("[DSM] required driver failed, abort stage");
|
/* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */
|
||||||
break;
|
panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED",
|
||||||
|
"required driver relocation/load failed", 0);
|
||||||
}
|
}
|
||||||
|
drv->resolved = 1;
|
||||||
|
pending--;
|
||||||
} else {
|
} else {
|
||||||
drv->loaded = 1;
|
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");
|
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,13 +0,0 @@
|
|||||||
dkm/manifest.o: dkm\manifest.c include\utsm\dkm.h include\utsm\types.h \
|
|
||||||
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
|
|
||||||
include\utsm\paging.h include\utsm\log.h dkm\..\arch\x86_64\limine.h \
|
|
||||||
dkm\..\kernel\ini_parser.h
|
|
||||||
include\utsm\dkm.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\dma.h:
|
|
||||||
include\utsm\block.h:
|
|
||||||
include\utsm\net.h:
|
|
||||||
include\utsm\paging.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
dkm\..\arch\x86_64\limine.h:
|
|
||||||
dkm\..\kernel\ini_parser.h:
|
|
||||||
Binary file not shown.
+527
-6
@@ -10,6 +10,15 @@
|
|||||||
|
|
||||||
#include <utsm/drr.h>
|
#include <utsm/drr.h>
|
||||||
#include <utsm/log.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) ---- */
|
/* ---- 内部内存操作(不依赖 libc) ---- */
|
||||||
static void drr_memzero(void *dst, u64 n) {
|
static void drr_memzero(void *dst, u64 n) {
|
||||||
@@ -71,14 +80,118 @@ u64 drr_crc64(const void *data, u64 len) {
|
|||||||
/* ---- 全局 DRR 状态 ---- */
|
/* ---- 全局 DRR 状态 ---- */
|
||||||
static drr_state g_drr;
|
static drr_state g_drr;
|
||||||
|
|
||||||
/* ---- Root key(最终应从安全启动链获取) ---- */
|
/* ---- Phase 8: 快照区 / 看门狗 / 恢复行为控制 ---- */
|
||||||
static const u64 g_root_key[4] = {
|
#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" */
|
0x4452525554534D31ULL, /* "DRRUTSM1" */
|
||||||
0xFEEDFACECAFEF00DULL,
|
0xFEEDFACECAFEF00DULL,
|
||||||
0x0123456789ABCDEFULL,
|
0x0123456789ABCDEFULL,
|
||||||
0xFEDCBA9876543210ULL
|
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 内部分配 ---- */
|
/* ---- Emergency Pool 内部分配 ---- */
|
||||||
static void *pool_alloc_internal(u64 size, u64 alignment) {
|
static void *pool_alloc_internal(u64 size, u64 alignment) {
|
||||||
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
|
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
|
||||||
@@ -105,25 +218,58 @@ void drr_emergency_reset(void) {
|
|||||||
drr_init();
|
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) {
|
void drr_init(void) {
|
||||||
log_info("[DRR] init begin");
|
log_info("[DRR] init begin");
|
||||||
drr_memzero(&g_drr, sizeof(g_drr));
|
drr_memzero(&g_drr, sizeof(g_drr));
|
||||||
|
|
||||||
|
/* R3b: 池上限 = min(编译期池, FUCK 配置);小于最小子缓冲布局时回退默认 */
|
||||||
g_drr.pool_offset = 0;
|
g_drr.pool_offset = 0;
|
||||||
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
|
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
|
||||||
g_drr.root_key = g_root_key;
|
{
|
||||||
|
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.recovery_generation = 1;
|
||||||
g_drr.heartbeat_interval = 1000;
|
g_drr.heartbeat_interval = 1000;
|
||||||
g_drr.active_slot = 0; /* A */
|
g_drr.active_slot = 0; /* A */
|
||||||
|
|
||||||
/* 分配 Emergency Pool 子缓冲区 */
|
/* 分配 Emergency Pool 子缓冲区(recovery log 大小可由 FUCK 配置) */
|
||||||
g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64);
|
g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64);
|
||||||
g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE;
|
g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE;
|
||||||
g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */
|
g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */
|
||||||
|
|
||||||
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(DRR_RECOVERY_LOG_SIZE, 64);
|
u64 log_size = DRR_RECOVERY_LOG_SIZE;
|
||||||
g_drr.log_capacity = DRR_RECOVERY_LOG_SIZE / sizeof(drr_log_entry);
|
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.log_count = 0;
|
||||||
|
|
||||||
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
|
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
|
||||||
@@ -142,9 +288,102 @@ void drr_init(void) {
|
|||||||
g_drr.ckpt_b.version = DRR_CKPT_VERSION;
|
g_drr.ckpt_b.version = DRR_CKPT_VERSION;
|
||||||
|
|
||||||
g_drr.initialized = 1;
|
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");
|
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) {
|
const u64 *drr_get_root_key(void) {
|
||||||
return g_drr.root_key;
|
return g_drr.root_key;
|
||||||
}
|
}
|
||||||
@@ -172,6 +411,30 @@ void drr_report_fault(const char *reason) {
|
|||||||
g_drr.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 ---- */
|
/* ---- Recovery Log ---- */
|
||||||
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
|
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
|
||||||
if (!g_drr.initialized || !g_drr.log_base) return;
|
if (!g_drr.initialized || !g_drr.log_base) return;
|
||||||
@@ -314,3 +577,261 @@ void drr_heartbeat_tick(void) {
|
|||||||
u64 drr_heartbeat_get(void) {
|
u64 drr_heartbeat_get(void) {
|
||||||
return g_drr.heartbeat_counter;
|
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,6 +0,0 @@
|
|||||||
drr/drr_stub.o: drr\drr_stub.c include\utsm\drr.h include\utsm\types.h \
|
|
||||||
include\utsm\config.h include\utsm\log.h
|
|
||||||
include\utsm\drr.h:
|
|
||||||
include\utsm\types.h:
|
|
||||||
include\utsm\config.h:
|
|
||||||
include\utsm\log.h:
|
|
||||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
#define UTSM_ASSERT_H
|
#define UTSM_ASSERT_H
|
||||||
|
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
|
#include <utsm/panic.h>
|
||||||
|
|
||||||
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
|
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#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 */
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#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 */
|
||||||
@@ -25,6 +25,7 @@ typedef struct dkm_block_api {
|
|||||||
u64 (*sector_size)(u32 index);
|
u64 (*sector_size)(u32 index);
|
||||||
const char *(*device_name)(u32 index);
|
const char *(*device_name)(u32 index);
|
||||||
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
||||||
|
u64 (*sector_count)(u32 index); /* 0x38: tail-append, 老内核该槽为 0 表示未知 */
|
||||||
} dkm_block_api;
|
} dkm_block_api;
|
||||||
|
|
||||||
void block_init(void);
|
void block_init(void);
|
||||||
|
|||||||
@@ -14,4 +14,17 @@ void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 ke
|
|||||||
u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch);
|
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);
|
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
|
#endif
|
||||||
|
|||||||
@@ -738,27 +738,28 @@ static inline du_u32 du_theme_accent(du_theme t) {
|
|||||||
#define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
|
#define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
* L1 — Winux-Kate 语义色值
|
* L1 — 桌面语义色值(白色调主题,去 winux-Kate 深色风)
|
||||||
|
* 底色白/浅灰,组件白卡片 + 浅灰描边,文字深灰,强调色深板岩。
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
#define KS_BG_PRIMARY DP_KATE_BG
|
#define KS_BG_PRIMARY 0xFFF3F4F6u /* 页面底:浅灰 */
|
||||||
#define KS_BG_SECONDARY DP_KATE_PANEL_SOLID
|
#define KS_BG_SECONDARY 0xFFFFFFFFu /* 面板:白 */
|
||||||
#define KS_BG_TERTIARY DP_KATE_BG2
|
#define KS_BG_TERTIARY 0xFFE9ECF0u /* 三级底:稍深浅灰 */
|
||||||
|
|
||||||
#define KS_TEXT_PRIMARY DP_KATE_TEXT
|
#define KS_TEXT_PRIMARY 0xFF1A1A1Au /* 主文字:近黑 */
|
||||||
#define KS_TEXT_DIM DP_KATE_TEXT_DIM
|
#define KS_TEXT_DIM 0xFF6B7280u /* 次文字:中灰 */
|
||||||
#define KS_TEXT_INVERT DP_KATE_BG
|
#define KS_TEXT_INVERT 0xFFFFFFFFu /* accent 上的文字:白 */
|
||||||
|
|
||||||
#define KS_ACCENT DP_KATE_ACCENT
|
#define KS_ACCENT 0xFF111827u /* 强调:深板岩(白底上高对比) */
|
||||||
#define KS_ACCENT2 DP_KATE_ACCENT2
|
#define KS_ACCENT2 0xFF4B5563u /* 次强调:中板岩 */
|
||||||
#define KS_ACCENT_DIM DP_KATE_ACCENT_DIM_SOLID
|
#define KS_ACCENT_DIM 0xFFE5E7EBu /* 强调淡底:浅灰 */
|
||||||
|
|
||||||
#define KS_BORDER DP_KATE_PANEL_BORDER_SOLID
|
#define KS_BORDER 0xFFD1D5DBu /* 边框:浅灰 */
|
||||||
#define KS_BORDER_FOCUS DP_KATE_ACCENT
|
#define KS_BORDER_FOCUS 0xFF111827u /* 聚焦边框:深板岩 */
|
||||||
#define KS_BORDER_DIM 0xFF1A2840u /* 暗边框 */
|
#define KS_BORDER_DIM 0xFFE5E7EBu /* 暗边框:极浅灰 */
|
||||||
|
|
||||||
#define KS_DANGER DP_KATE_DANGER
|
#define KS_DANGER 0xFFDC2626u /* 红 */
|
||||||
#define KS_WARN DP_KATE_WARN
|
#define KS_WARN 0xFFD97706u /* 琥珀 */
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
* Winux-Kate 布局常量(源自 theme.css / pages.css)
|
* Winux-Kate 布局常量(源自 theme.css / pages.css)
|
||||||
|
|||||||
@@ -33,6 +33,76 @@
|
|||||||
struct dkm_kernel_api;
|
struct dkm_kernel_api;
|
||||||
struct dkm_driver_handle;
|
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 {
|
typedef struct dkm_driver_desc {
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u16 abi_version;
|
u16 abi_version;
|
||||||
@@ -63,6 +133,24 @@ typedef struct dkm_driver_desc {
|
|||||||
typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
|
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);
|
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 {
|
typedef struct dkm_log_api {
|
||||||
void (*info)(const char *msg);
|
void (*info)(const char *msg);
|
||||||
void (*warn)(const char *msg);
|
void (*warn)(const char *msg);
|
||||||
@@ -84,7 +172,7 @@ typedef struct dkm_kernel_api {
|
|||||||
const void *vfs;
|
const void *vfs;
|
||||||
const dkm_net_api *net;
|
const dkm_net_api *net;
|
||||||
const void *timer;
|
const void *timer;
|
||||||
const void *drr;
|
const drr_recovery_api *drr; /* 恢复根服务(原占位 void* 改具体类型,布局不变) */
|
||||||
|
|
||||||
const void *rsdp_address; /* ACPI RSDP physical address */
|
const void *rsdp_address; /* ACPI RSDP physical address */
|
||||||
const void *fb_address; /* framebuffer base */
|
const void *fb_address; /* framebuffer base */
|
||||||
@@ -111,6 +199,16 @@ typedef struct dkm_kernel_api {
|
|||||||
* 接口已接受向量命名空间,无需新注册 ABI)。 */
|
* 接口已接受向量命名空间,无需新注册 ABI)。 */
|
||||||
int (*irq_vector_alloc)(void);
|
int (*irq_vector_alloc)(void);
|
||||||
void (*irq_vector_free)(int vector);
|
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;
|
} dkm_kernel_api;
|
||||||
|
|
||||||
typedef enum dkm_driver_state {
|
typedef enum dkm_driver_state {
|
||||||
@@ -136,12 +234,39 @@ typedef struct dkm_builtin_driver {
|
|||||||
} dkm_builtin_driver;
|
} dkm_builtin_driver;
|
||||||
|
|
||||||
void dkm_init(void);
|
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);
|
int dkm_load_builtin(const dkm_builtin_driver *driver);
|
||||||
void dkm_scan_boot_modules(void);
|
void dkm_scan_boot_modules(void);
|
||||||
void dsm_load_by_manifest(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);
|
void dkm_fill_platform_info(void);
|
||||||
const dkm_kernel_api *dkm_get_kernel_api(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 */
|
/* exposed for manifest parser */
|
||||||
struct dkm_symbol_scan {
|
struct dkm_symbol_scan {
|
||||||
const void *driver_desc;
|
const void *driver_desc;
|
||||||
|
|||||||
@@ -102,9 +102,15 @@ typedef struct {
|
|||||||
/* ---- 公共接口 ---- */
|
/* ---- 公共接口 ---- */
|
||||||
|
|
||||||
void drr_init(void);
|
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);
|
const u64 *drr_get_root_key(void);
|
||||||
u64 drr_recovery_generation(void);
|
u64 drr_recovery_generation(void);
|
||||||
void drr_report_fault(const char *reason);
|
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_intent(u32 segment_slot, u64 offset, u64 len);
|
||||||
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
|
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
|
||||||
|
|
||||||
@@ -124,4 +130,55 @@ u64 drr_heartbeat_get(void);
|
|||||||
/* CRC64 */
|
/* CRC64 */
|
||||||
u64 drr_crc64(const void *data, u64 len);
|
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 */
|
#endif /* UTSM_DRR_H */
|
||||||
|
|||||||
@@ -10,10 +10,30 @@
|
|||||||
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
|
||||||
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
|
||||||
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
|
#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) ---- */
|
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
|
||||||
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
|
#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 {
|
typedef struct probe_info {
|
||||||
u32 magic; /* PROBE_INFO_MAGIC */
|
u32 magic; /* PROBE_INFO_MAGIC */
|
||||||
u32 version; /* ABI 版本,当前 1 */
|
u32 version; /* ABI 版本,当前 1 */
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
#define EPT_PAGE_SIZE 4096ULL
|
#define EPT_PAGE_SIZE 4096ULL
|
||||||
#define EPT_ENTRIES 512ULL
|
#define EPT_ENTRIES 512ULL
|
||||||
|
|
||||||
/* EPTP 构造:bits [5:0]=memory type, bits [7:6]=walk length-1, bits [51:12]=PML4 HPA */
|
/* EPTP 构造:bits [2:0]=memory type, bits [5:3]=walk length-1, bit 6=AD, bits [51:12]=PML4 HPA
|
||||||
#define EPTP_WALK_LEN_4 (3ULL << 6) /* 4 级页表 */
|
* 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)
|
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
|
||||||
|
|
||||||
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
|
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
|
||||||
@@ -30,6 +31,14 @@ int ept_identity_map(u64 gpa, u64 size, u64 flags);
|
|||||||
* Handles 4KB pages and 2MB large pages. */
|
* Handles 4KB pages and 2MB large pages. */
|
||||||
u64 ept_gpa_to_hpa(u64 gpa);
|
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).
|
/* Map a 2MB large page in EPT (PD-level direct mapping).
|
||||||
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
|
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
|
||||||
* Required for real hardware MTRR compliance. */
|
* Required for real hardware MTRR compliance. */
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#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 */
|
||||||
@@ -0,0 +1,648 @@
|
|||||||
|
/* 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 */
|
||||||
@@ -45,4 +45,25 @@ void idt_register_apic_eoi(void (*eoi_fn)(void));
|
|||||||
int irq_vector_alloc(void);
|
int irq_vector_alloc(void);
|
||||||
void irq_vector_free(int vector);
|
void irq_vector_free(int vector);
|
||||||
|
|
||||||
|
/* 显式 EOI(Phase7: 调度器 tick 等可能切换任务的中断路径用)。
|
||||||
|
* 在任何可能换栈的工作之前完成 EOI,防止 EOI 悬挂一个时间片。
|
||||||
|
* 序列与 idt_handler 默认路径一致: LAPIC 钩子 → 8259 EOI(按向量范围)。
|
||||||
|
* 参数兼容双命名空间(同 irq_register): 0-15=ISA IRQ, 0x20+=向量。 */
|
||||||
|
void idt_irq_eoi(u8 vector);
|
||||||
|
|
||||||
|
/* CPU 异常钩子(Phase8: DRR 任务级 fault 接管)。
|
||||||
|
* 在 idt_handler 的 vector<32 分支最前调用: 返回非 0 = 异常已被消费
|
||||||
|
* (fault 任务被杀/切换,控制权不回到日志+停机路径);返回 0 = 走原
|
||||||
|
* 全量日志 + cli;hlt 路径(DSK 阶段行为不变)。传 NULL 注销。 */
|
||||||
|
typedef int (*idt_exception_hook_t)(u64 vector, u64 error_code, u64 rip);
|
||||||
|
void idt_set_exception_hook(idt_exception_hook_t hook);
|
||||||
|
|
||||||
|
/* P1(万物互联): PIC IMR 位操作 —— kern.irq.unmask/mask 总线服务后端。
|
||||||
|
* 仅 ISA IRQ 0-15(越界 → IRQ-E01 panic 零降级);
|
||||||
|
* unmask IRQ8-15 自动放开主片 IRQ2 级联。
|
||||||
|
* apic_route=1 时 PIC 已全屏蔽(IOAPIC 接管),本操作无效果——该路径的
|
||||||
|
* 解屏蔽随 P6 真 IRQ 输入走 IOAPIC 接线。 */
|
||||||
|
void pic_irq_unmask(u8 irq);
|
||||||
|
void pic_irq_mask(u8 irq);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
#ifndef UTSM_IPC_NATIVE_H
|
||||||
|
#define UTSM_IPC_NATIVE_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Experimental native Deshab capability IPC ABI.
|
||||||
|
*
|
||||||
|
* This is intentionally distinct from CODE/utsm-ipc/ipc_proto.h, which is a
|
||||||
|
* UTSM <-> Linux guest transport. No native queue implementation or syscall
|
||||||
|
* binding exists yet. All ABI fields use bounded fixed-width Deshab types;
|
||||||
|
* pointers and implementation-owned kernel addresses never appear on wire.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define UTSM_IPC_NATIVE_MAGIC 0x44534950u /* "DSIP" */
|
||||||
|
#define UTSM_IPC_NATIVE_ABI_MAJOR 1u
|
||||||
|
#define UTSM_IPC_NATIVE_ABI_MINOR 0u
|
||||||
|
|
||||||
|
#define UTSM_IPC_NATIVE_MESSAGE_BYTES 256u
|
||||||
|
#define UTSM_IPC_NATIVE_PAYLOAD_BYTES 144u
|
||||||
|
#define UTSM_IPC_NATIVE_MAX_CAPS 4u
|
||||||
|
#define UTSM_IPC_NATIVE_MIN_DEPTH 2u
|
||||||
|
#define UTSM_IPC_NATIVE_MAX_DEPTH 4096u
|
||||||
|
#define UTSM_IPC_NATIVE_DEADLINE_NONE (~0ULL)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rights field requests or describes attenuation. Authority comes from
|
||||||
|
* the calling task's kernel capability table; copying or editing this value
|
||||||
|
* must never create authority.
|
||||||
|
*/
|
||||||
|
typedef struct utsm_ipc_native_cap {
|
||||||
|
u64 object_id;
|
||||||
|
u32 generation;
|
||||||
|
u32 rights;
|
||||||
|
} utsm_ipc_native_cap;
|
||||||
|
|
||||||
|
typedef enum utsm_ipc_native_right {
|
||||||
|
UTSM_IPC_RIGHT_SEND = (1u << 0),
|
||||||
|
UTSM_IPC_RIGHT_RECEIVE = (1u << 1),
|
||||||
|
UTSM_IPC_RIGHT_CALL = (1u << 2),
|
||||||
|
UTSM_IPC_RIGHT_REPLY = (1u << 3),
|
||||||
|
UTSM_IPC_RIGHT_TRANSFER = (1u << 4),
|
||||||
|
UTSM_IPC_RIGHT_DUPLICATE = (1u << 5),
|
||||||
|
UTSM_IPC_RIGHT_MANAGE = (1u << 6),
|
||||||
|
UTSM_IPC_RIGHT_WAIT = (1u << 7),
|
||||||
|
UTSM_IPC_RIGHT_SIGNAL = (1u << 8)
|
||||||
|
} utsm_ipc_native_right;
|
||||||
|
|
||||||
|
#define UTSM_IPC_RIGHT_ALL ((u32)((1u << 9) - 1u))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Queue operations are carried in a u16 ABI field. CALL creates a transaction
|
||||||
|
* ID; REPLY must name that transaction. WAIT/SIGNAL are queue-state events,
|
||||||
|
* not substitutes for SEND/RECEIVE.
|
||||||
|
*/
|
||||||
|
typedef enum utsm_ipc_native_queue_op {
|
||||||
|
UTSM_IPC_QUEUE_CREATE = 1,
|
||||||
|
UTSM_IPC_QUEUE_DESTROY = 2,
|
||||||
|
UTSM_IPC_QUEUE_SEND = 3,
|
||||||
|
UTSM_IPC_QUEUE_RECEIVE = 4,
|
||||||
|
UTSM_IPC_QUEUE_CALL = 5,
|
||||||
|
UTSM_IPC_QUEUE_REPLY = 6,
|
||||||
|
UTSM_IPC_QUEUE_WAIT = 7,
|
||||||
|
UTSM_IPC_QUEUE_SIGNAL = 8,
|
||||||
|
UTSM_IPC_QUEUE_QUERY = 9
|
||||||
|
} utsm_ipc_native_queue_op;
|
||||||
|
|
||||||
|
#define UTSM_IPC_MSG_F_NONBLOCK (1u << 0)
|
||||||
|
#define UTSM_IPC_MSG_F_MOVE_CAPS (1u << 1)
|
||||||
|
#define UTSM_IPC_MSG_F_EXPECT_REPLY (1u << 2)
|
||||||
|
#define UTSM_IPC_MSG_F_CANCEL (1u << 3)
|
||||||
|
|
||||||
|
typedef enum utsm_ipc_native_result {
|
||||||
|
UTSM_IPC_NATIVE_OK = 0,
|
||||||
|
UTSM_IPC_NATIVE_ERR_INVALID = -1,
|
||||||
|
UTSM_IPC_NATIVE_ERR_ACCESS = -2,
|
||||||
|
UTSM_IPC_NATIVE_ERR_STALE_CAP = -3,
|
||||||
|
UTSM_IPC_NATIVE_ERR_FULL = -4,
|
||||||
|
UTSM_IPC_NATIVE_ERR_EMPTY = -5,
|
||||||
|
UTSM_IPC_NATIVE_ERR_CLOSED = -6,
|
||||||
|
UTSM_IPC_NATIVE_ERR_TIMEOUT = -7,
|
||||||
|
UTSM_IPC_NATIVE_ERR_CANCELLED = -8,
|
||||||
|
UTSM_IPC_NATIVE_ERR_UNSUPPORTED = -38
|
||||||
|
} utsm_ipc_native_result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixed 256-byte message. message_size must equal sizeof(this structure),
|
||||||
|
* payload_length is bounded by PAYLOAD_BYTES, and capability_count is bounded
|
||||||
|
* by MAX_CAPS. Receivers must reject nonzero reserved fields.
|
||||||
|
*/
|
||||||
|
typedef struct utsm_ipc_native_message {
|
||||||
|
u32 magic;
|
||||||
|
u16 abi_major;
|
||||||
|
u16 abi_minor;
|
||||||
|
|
||||||
|
u16 operation; /* utsm_ipc_native_queue_op */
|
||||||
|
u16 flags;
|
||||||
|
u32 message_size;
|
||||||
|
u64 transaction_id;
|
||||||
|
|
||||||
|
utsm_ipc_native_cap endpoint;
|
||||||
|
u32 payload_length;
|
||||||
|
u16 capability_count;
|
||||||
|
u16 reserved0;
|
||||||
|
|
||||||
|
utsm_ipc_native_cap capabilities[UTSM_IPC_NATIVE_MAX_CAPS];
|
||||||
|
u8 payload[UTSM_IPC_NATIVE_PAYLOAD_BYTES];
|
||||||
|
} utsm_ipc_native_message;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixed queue-control request for a future syscall dispatcher. depth is valid
|
||||||
|
* only for CREATE and must stay within MIN_DEPTH..MAX_DEPTH. deadline_ns is
|
||||||
|
* an absolute monotonic deadline or UTSM_IPC_NATIVE_DEADLINE_NONE.
|
||||||
|
*/
|
||||||
|
typedef struct utsm_ipc_native_queue_request {
|
||||||
|
u16 abi_major;
|
||||||
|
u16 abi_minor;
|
||||||
|
u16 operation; /* utsm_ipc_native_queue_op */
|
||||||
|
u16 flags;
|
||||||
|
|
||||||
|
u32 depth;
|
||||||
|
u32 message_size;
|
||||||
|
utsm_ipc_native_cap queue;
|
||||||
|
u64 deadline_ns;
|
||||||
|
} utsm_ipc_native_queue_request;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(utsm_ipc_native_cap) == 16u,
|
||||||
|
"native IPC capability ABI changed");
|
||||||
|
_Static_assert(sizeof(utsm_ipc_native_message) ==
|
||||||
|
UTSM_IPC_NATIVE_MESSAGE_BYTES,
|
||||||
|
"native IPC message ABI changed");
|
||||||
|
_Static_assert(sizeof(utsm_ipc_native_queue_request) == 40u,
|
||||||
|
"native IPC queue request ABI changed");
|
||||||
|
|
||||||
|
#endif /* UTSM_IPC_NATIVE_H */
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef UTSM_LAPIC_TIMER_H
|
||||||
|
#define UTSM_LAPIC_TIMER_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* 宿主自持 LAPIC 定时器 —— SAS-R0-PCQ 调度器 tick 源(ROADMAP Phase 2 目标方案)。
|
||||||
|
*
|
||||||
|
* 动机:默认 FUCK 配置 apic_route=1 时,apic.drv 验证完 tick 链路后会把
|
||||||
|
* GSI2(PIT IRQ0 线)重新 mask(apic.c route_switch 收尾),PIT 周期中断
|
||||||
|
* 在该配置下不可达。LAPIC timer 不经 IOAPIC/PIC 路由,与路由模式解耦。
|
||||||
|
*
|
||||||
|
* 实现要点:
|
||||||
|
* - IA32_APIC_BASE MSR (0x1B) 取 LAPIC 物理基址,经 paging map_mmio 映射
|
||||||
|
* (SAS-R0:直接扩当前 CR3,全 CPU 可见)。
|
||||||
|
* - x2APIC(MSR bit10)不支持 → 返回负数,调度器降级为协作模式。
|
||||||
|
* - 频率校准:LAPIC 定时器 divide=16、initial=0xFFFFFFFF,用 PIT ch0
|
||||||
|
* 单发 10ms 窗口采样 CCR 差值(pit.h 单发助手)。
|
||||||
|
* - 向量来自 irq_vector_alloc(0x40-0xDF 动态池);EOI 由本模块直写
|
||||||
|
* LAPIC EOI 寄存器(不依赖 apic.drv 钩子,PIC/IOAPIC 两模式均成立)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 校准并配置周期定时器(LVT 保持 mask,尚未投递)。
|
||||||
|
* 返回 0 成功,负数失败(x2APIC/映射失败/校准异常)。 */
|
||||||
|
int lapic_timer_init(u32 freq_hz);
|
||||||
|
|
||||||
|
/* init 成功后返回分配到的 IDT 向量(0x40-0xDF),未初始化返回 -1 */
|
||||||
|
int lapic_timer_vector(void);
|
||||||
|
|
||||||
|
/* LVT 解除 mask —— 开始周期投递(须在 irq_register 向量 handler 之后调用) */
|
||||||
|
void lapic_timer_start(void);
|
||||||
|
|
||||||
|
/* 停止:LVT 置 mask + initial count 清零 + 归还动态向量 */
|
||||||
|
void lapic_timer_stop(void);
|
||||||
|
|
||||||
|
/* 直写 LAPIC EOI 寄存器(tick handler 用;未初始化时为空操作) */
|
||||||
|
void lapic_timer_eoi(void);
|
||||||
|
|
||||||
|
#endif /* UTSM_LAPIC_TIMER_H */
|
||||||
@@ -21,6 +21,34 @@
|
|||||||
|
|
||||||
#define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */
|
#define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */
|
||||||
|
|
||||||
|
/* VSCode Phase 3: virtio-gpu scanout 查询结果。
|
||||||
|
* 镜像 virtio_mmio.h 的 virtio_gpu_scanout_info 字段布局,保持公共 ABI 自洽
|
||||||
|
* (不强制消费者 include virtio_mmio.h)。必须在 linux_compat_service 之前
|
||||||
|
* 定义,因为服务表中有指向此类型的函数指针。 */
|
||||||
|
struct linux_compat_scanout_info {
|
||||||
|
void *host_vaddr;
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
u32 stride;
|
||||||
|
u32 dirty;
|
||||||
|
u32 enabled;
|
||||||
|
/* Phase 7: 累积 dirty rect(scanout 坐标系并集,查询随 dirty 清零)。
|
||||||
|
* dirty=0 时全为 0;desktop 据此只 blit 变化区域(尾部追加,ABI 兼容)。 */
|
||||||
|
u32 dirty_x;
|
||||||
|
u32 dirty_y;
|
||||||
|
u32 dirty_w;
|
||||||
|
u32 dirty_h;
|
||||||
|
/* P7.6: hardware cursor state(尾部追加,ABI 兼容)。 */
|
||||||
|
u32 cursor_visible;
|
||||||
|
u32 cursor_x;
|
||||||
|
u32 cursor_y;
|
||||||
|
u32 cursor_hot_x;
|
||||||
|
u32 cursor_hot_y;
|
||||||
|
u32 cursor_w;
|
||||||
|
u32 cursor_h;
|
||||||
|
void *cursor_bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
/* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */
|
/* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */
|
||||||
typedef struct linux_compat_service {
|
typedef struct linux_compat_service {
|
||||||
u64 magic; /* LINUX_COMPAT_MAGIC */
|
u64 magic; /* LINUX_COMPAT_MAGIC */
|
||||||
@@ -93,6 +121,46 @@ typedef struct linux_compat_service {
|
|||||||
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
|
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
|
||||||
int (*file_push)(const char *fat32_name, const char *linux_path,
|
int (*file_push)(const char *fat32_name, const char *linux_path,
|
||||||
u64 *out_size);
|
u64 *out_size);
|
||||||
|
|
||||||
|
/* VSCode Phase 2: 输入转发到 Linux guest(virtio-input 键盘+鼠标)。
|
||||||
|
* 由 desktop 在 IDE attached 时调用,把 Deshab PS/2 事件注入 guest。
|
||||||
|
* 事件排到 pending ring,由 vmexit poll 路径排空到 eventq + 注入 IRQ。
|
||||||
|
*
|
||||||
|
* input_forward_keyboard(code, value)
|
||||||
|
* code : linux keycode(KEY_A=30 等,见 linux/input-event-codes.h)
|
||||||
|
* value : 0=release, 1=press, 2=repeat
|
||||||
|
*
|
||||||
|
* input_forward_mouse(dx, dy, buttons)
|
||||||
|
* dx, dy : 相对位移(像素差,可为负)
|
||||||
|
* buttons : 按钮位图 bit0=左, bit1=右, bit2=中(内部与上次状态比较,
|
||||||
|
* 仅变化的按钮产生 press/release 事件;末尾自动追加 SYN) */
|
||||||
|
void (*input_forward_keyboard)(u16 code, u32 value);
|
||||||
|
void (*input_forward_mouse)(i32 dx, i32 dy, u8 buttons);
|
||||||
|
|
||||||
|
/* VSCode Phase 3: 查询 virtio-gpu scanout 状态(desktop IDE blit 用)。
|
||||||
|
* 在 IDE attached 时由 desktop 周期调用:若 dirty=1,把 host_vaddr 指向的
|
||||||
|
* scanout 影子缓冲(width×height,stride 字节/行,XRGB8888)blit 到
|
||||||
|
* framebuffer 的 IDE host 区域,然后重新查询以清零 dirty。
|
||||||
|
* out->host_vaddr : scanout 影子缓冲 host 虚拟地址(SAS-R0 下可直接解引用)
|
||||||
|
* out->enabled : 1 = scanout 已绑定 2D resource(X server 已 SET_SCANOUT)
|
||||||
|
* out->dirty : 1 = 自上次查询有新帧(查询会清零该位)
|
||||||
|
* 返回 0 成功(enabled=0 时其余字段为零),-1 后端未就绪(无 surface pool)。 */
|
||||||
|
int (*gpu_get_scanout_info)(struct linux_compat_scanout_info *out);
|
||||||
|
|
||||||
|
/* VSCode Phase 5: 异步执行 Linux 程序(daemon fork+setsid+detach)。
|
||||||
|
* 与 exec 语义相同,但 daemon 在 spawn 成功后立即回 EXEC_EXIT(0),
|
||||||
|
* 不捕获 stdout、不等待进程退出——用于启动 X server / VSCode 等
|
||||||
|
* 长生命周期 GUI 进程而不阻塞 UTSM 侧。子进程 stdio 重定向到
|
||||||
|
* /dev/null,环境自带 LD_LIBRARY_PATH=/usr/lib/deshab 与 DISPLAY=:0。
|
||||||
|
* 返回 0 = 已成功 spawn(不代表程序后续存活),负值失败
|
||||||
|
* (-1=服务不可用, -2=IPC 发送失败, -3=linux_resume 失败,
|
||||||
|
* -4=无响应, -5=daemon 报告 spawn 失败)。 */
|
||||||
|
int (*exec_async)(const char *path, int argc, const char *const *argv);
|
||||||
|
|
||||||
|
/* 不发送 IPC,只跑一段 Linux vCPU(HLT 或 preemption timeslice)。
|
||||||
|
* KDE/Plasma 全屏循环用这个唤醒 async 子进程,避免再 exec sleep。
|
||||||
|
* 返回 0 成功,负值同 linux_resume。 */
|
||||||
|
int (*run_slice)(void);
|
||||||
} linux_compat_service;
|
} linux_compat_service;
|
||||||
|
|
||||||
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
|
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
|
||||||
@@ -111,4 +179,21 @@ const linux_compat_service *linux_compat_get_service(void);
|
|||||||
/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */
|
/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */
|
||||||
void linux_compat_init(void);
|
void linux_compat_init(void);
|
||||||
|
|
||||||
|
/* 同步 FAT32 lib 目录下所有 .so 文件到 Linux guest。
|
||||||
|
* guest_lib_path : guest 端库目录路径(如 "/usr/lib/deshab")
|
||||||
|
* 调用时机:linux_compat_init 成功后。失败不阻断启动。
|
||||||
|
* 返回 0 成功,负值失败。 */
|
||||||
|
int lxc_sync_lib_dir(const char *guest_lib_path);
|
||||||
|
|
||||||
|
/* VSCode Phase 5: 把 VSCode tarball(Limine boot module,路径含 "vscode")
|
||||||
|
* 安装到 Linux guest 的 /opt/vscode(经 overlay 持久化在 extra-rootfs 卷)。
|
||||||
|
* 流程:已安装标记探测(/opt/vscode/bin/code 可执行则跳过)→
|
||||||
|
* 流式推送 module 字节到 guest /tmp/vscode.tar.gz →
|
||||||
|
* mkdir -p /opt/vscode → tar 解包(--strip-components=1,失败回退不解层)→
|
||||||
|
* 清理 /tmp tarball → 验证 /opt/vscode/bin/code。
|
||||||
|
* 调用时机:linux_compat_init 成功后(kernel main,FUCK [compat] vscode_install=1)。
|
||||||
|
* 返回 0 成功或已安装;负值失败(-1 服务不可用, -11 无 tarball module,
|
||||||
|
* -12 推送失败, -13 解包失败, -14 验证失败)。所有失败均不阻断启动。 */
|
||||||
|
int lxc_vscode_install(void);
|
||||||
|
|
||||||
#endif /* UTSM_LINUX_COMPAT_H */
|
#endif /* UTSM_LINUX_COMPAT_H */
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ struct linux_setup_header {
|
|||||||
/* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */
|
/* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */
|
||||||
#define BOOT_PARAMS_SIZE 4096
|
#define BOOT_PARAMS_SIZE 4096
|
||||||
#define SETUP_HEADER_OFFSET 0x1f1
|
#define SETUP_HEADER_OFFSET 0x1f1
|
||||||
|
#define BOOT_PARAMS_ACPI_RSDP_ADDR_OFF 0x070
|
||||||
|
#define BOOT_PARAMS_RSDP_COPY_OFF 0x800
|
||||||
|
#define BOOT_PARAMS_RSDP_COPY_SIZE 36
|
||||||
|
|
||||||
/* Key field offsets within bzImage / boot_params */
|
/* Key field offsets within bzImage / boot_params */
|
||||||
#define BZ_BOOT_FLAG_OFF 0x1fe
|
#define BZ_BOOT_FLAG_OFF 0x1fe
|
||||||
@@ -88,11 +91,10 @@ struct linux_setup_header {
|
|||||||
#define E820_TYPE_UNUSABLE 5
|
#define E820_TYPE_UNUSABLE 5
|
||||||
#define E820_TYPE_PMEM 7
|
#define E820_TYPE_PMEM 7
|
||||||
|
|
||||||
/* e820_table entry in boot_params at offset 0x2e0 (actually 0x2d0 in older,
|
/* e820_table is at 0x2d0 in Linux 6.6 boot_params (arch/x86/include/uapi/asm/bootparam.h).
|
||||||
* 0x2e0 in newer). We use 0x2e0 and the e820_entries count at 0x1e8.
|
* e820_entries count is at 0x1e8. Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
|
||||||
* Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
|
|
||||||
#define E820_ENTRIES_OFFSET 0x1e8
|
#define E820_ENTRIES_OFFSET 0x1e8
|
||||||
#define E820_TABLE_OFFSET 0x2e0
|
#define E820_TABLE_OFFSET 0x2d0
|
||||||
#define E820_MAX_ENTRIES 128
|
#define E820_MAX_ENTRIES 128
|
||||||
#define E820_ENTRY_SIZE 20
|
#define E820_ENTRY_SIZE 20
|
||||||
|
|
||||||
@@ -103,7 +105,9 @@ struct linux_setup_header {
|
|||||||
* 0x01000000-0x02FFFFFF : Linux kernel image
|
* 0x01000000-0x02FFFFFF : Linux kernel image
|
||||||
* 0x03000000-0x03FFFFFF : Linux initrd
|
* 0x03000000-0x03FFFFFF : Linux initrd
|
||||||
* 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW)
|
* 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW)
|
||||||
* 0x05000000-MAX : Linux general RAM
|
* 0x04100000-0x04FFFFFF : Graphics surface pool (16MB, VSCode integration Phase 1)
|
||||||
|
* 0x05000000-0x5FFFFFFF : Linux general RAM (~1.4GB, expanded Phase 1 for VSCode)
|
||||||
|
* 0xF4000000-0xF4007000 : virtio-mmio devices (8 slots, expanded Phase 1)
|
||||||
*/
|
*/
|
||||||
#define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */
|
#define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */
|
||||||
#define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */
|
#define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */
|
||||||
@@ -118,8 +122,19 @@ struct linux_setup_header {
|
|||||||
#define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */
|
#define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */
|
||||||
#define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
|
#define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
|
||||||
|
|
||||||
/* General RAM starts after IPC region */
|
/* VSCode integration Phase 1: Graphics surface pool.
|
||||||
|
* Separate DMA region EPT-mapped into the Linux guest. Linux user-space mmaps
|
||||||
|
* /dev/utsm at this GPA to obtain a writable scanout buffer that UTSM reads
|
||||||
|
* for blitting to the Deshab framebuffer (Phase 3 virtio-gpu shadow buffer).
|
||||||
|
* 16MB accommodates a 1920×1080×4 framebuffer (~8MB) with headroom for
|
||||||
|
* cursor plane and dirty rect tracking metadata. */
|
||||||
|
#define LINUX_GUEST_SURFACE_GPA 0x04100000ULL /* 65 MB (immediately after IPC shm) */
|
||||||
|
#define LINUX_GUEST_SURFACE_SIZE (16 * 1024 * 1024) /* 16MB */
|
||||||
|
|
||||||
|
/* General RAM starts after IPC + surface regions.
|
||||||
|
* Phase 1 expansion: 1.4GB to fit VSCode + Electron + GUI stack. */
|
||||||
#define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */
|
#define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */
|
||||||
|
#define LINUX_GUEST_RAM_SIZE 0x5B000000ULL /* 1.4GB (0x05000000..0x5FFFFFFF) */
|
||||||
|
|
||||||
/* ===== virtio-mmio device GPA layout =====
|
/* ===== virtio-mmio device GPA layout =====
|
||||||
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
|
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
|
||||||
@@ -129,16 +144,33 @@ struct linux_setup_header {
|
|||||||
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
|
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
|
||||||
* 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。
|
* 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。
|
||||||
*
|
*
|
||||||
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x09000000)。 */
|
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x5FFFFFFF)。
|
||||||
|
*
|
||||||
|
* VSCode integration Phase 1 expansion: 槽位从 3 扩到 8,为后续阶段预留:
|
||||||
|
* slot 0: blk (Phase 1-) IRQ5
|
||||||
|
* slot 1: net (Phase 1-) IRQ6
|
||||||
|
* slot 2: rootfs (Phase 1-) IRQ7
|
||||||
|
* slot 3: keyboard (Phase 2) IRQ8 — virtio-input, evdev /dev/input/event0
|
||||||
|
* slot 4: mouse (Phase 2) IRQ9 — virtio-input, evdev /dev/input/event1
|
||||||
|
* slot 5: gpu (Phase 3) IRQ10 — virtio-gpu 2D, /dev/dri/card0
|
||||||
|
* slot 6: extra-rootfs (Phase 4) IRQ11 — persistent rw volume (/dev/vdc)
|
||||||
|
* slot 7: reserved — future use
|
||||||
|
*/
|
||||||
#define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL
|
#define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL
|
||||||
#define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */
|
#define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */
|
||||||
|
#define VIRTIO_MMIO_MAX_SLOTS 8 /* Phase 1: 3→8 */
|
||||||
|
|
||||||
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
|
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
|
||||||
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
|
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
|
||||||
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
|
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
|
||||||
|
/* VSCode integration: Phase 2/3/4 device slots (currently unused, reserved) */
|
||||||
|
#define VIRTIO_MMIO_KBD_GPA (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4003000 */
|
||||||
|
#define VIRTIO_MMIO_MOUSE_GPA (VIRTIO_MMIO_GPA_BASE + 4 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4004000 */
|
||||||
|
#define VIRTIO_MMIO_GPU_GPA (VIRTIO_MMIO_GPA_BASE + 5 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4005000 */
|
||||||
|
#define VIRTIO_MMIO_EXTRA_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 6 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4006000 */
|
||||||
|
|
||||||
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
|
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
|
||||||
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE)
|
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + VIRTIO_MMIO_MAX_SLOTS * VIRTIO_MMIO_GPA_STRIDE)
|
||||||
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
|
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
|
||||||
|
|
||||||
/* ===== Linux loader API ===== */
|
/* ===== Linux loader API ===== */
|
||||||
@@ -156,6 +188,13 @@ struct linux_guest_info {
|
|||||||
u64 pgt_gpa; /* GPA of guest page tables */
|
u64 pgt_gpa; /* GPA of guest page tables */
|
||||||
u64 gdt_gpa; /* GPA of guest GDT */
|
u64 gdt_gpa; /* GPA of guest GDT */
|
||||||
int loaded; /* 1 = successfully loaded */
|
int loaded; /* 1 = successfully loaded */
|
||||||
|
/* VSCode integration Phase 1: graphics surface pool.
|
||||||
|
* Allocated as a separate DMA buffer and EPT-mapped into the guest at
|
||||||
|
* surface_gpa. Exposed to Linux user-space via UTSM_HCALL_SURFACE_INFO
|
||||||
|
* and /dev/utsm mmap (see CODE/linux/patches/utsm_hcall.c). */
|
||||||
|
u64 surface_hpa; /* Host physical address of surface pool (0 = none) */
|
||||||
|
u64 surface_gpa; /* GPA visible inside Linux guest */
|
||||||
|
u64 surface_size; /* Surface pool size in bytes */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Find the Linux bzImage boot module among Limine modules.
|
/* Find the Linux bzImage boot module among Limine modules.
|
||||||
@@ -167,10 +206,24 @@ void *linux_find_bzimage_module(u64 *size_out);
|
|||||||
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
|
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
|
||||||
void *linux_find_rootfs_module(u64 *size_out);
|
void *linux_find_rootfs_module(u64 *size_out);
|
||||||
|
|
||||||
/* Parse bzImage header and extract key parameters.
|
/* VSCode Phase 4: find the extra rootfs image boot module (path contains
|
||||||
|
* "extra"). Exposed via writable virtio-blk slot6 as /dev/vdc (overlayfs
|
||||||
|
* upper / VSCode data volume). Returns NULL when the module is absent. */
|
||||||
|
void *linux_find_extra_rootfs_module(u64 *size_out);
|
||||||
|
|
||||||
|
/* VSCode Phase 5: find the VSCode tarball boot module (path contains
|
||||||
|
* "vscode", e.g. linux-vscode.tar.gz). The tarball is streamed into the
|
||||||
|
* guest and extracted to /opt/vscode by lxc_vscode_install().
|
||||||
|
* Returns NULL when the module is absent (install skipped, non-fatal). */
|
||||||
|
void *linux_find_vscode_module(u64 *size_out);
|
||||||
|
|
||||||
|
/* Parse bzImage header and return the protected-mode image's file offset,
|
||||||
|
* load length, decompression allocation size, and startup_64 entry offset.
|
||||||
|
* The protected-mode image starts after setup sectors; setup_header's
|
||||||
|
* payload_offset instead locates compressed data inside that image.
|
||||||
* Returns 0 on success, negative on error. */
|
* Returns 0 on success, negative on error. */
|
||||||
int linux_parse_bzimage(const void *bzimage, u64 size,
|
int linux_parse_bzimage(const void *bzimage, u64 size,
|
||||||
u64 *payload_offset_out, u64 *payload_length_out,
|
u64 *kernel_offset_out, u64 *kernel_length_out,
|
||||||
u64 *init_size_out, u64 *entry_offset_out);
|
u64 *init_size_out, u64 *entry_offset_out);
|
||||||
|
|
||||||
/* Initialize the Linux guest: parse bzImage, allocate guest memory,
|
/* Initialize the Linux guest: parse bzImage, allocate guest memory,
|
||||||
@@ -181,25 +234,51 @@ int linux_loader_init(void);
|
|||||||
/* Get the loaded Linux guest info (NULL if not loaded). */
|
/* Get the loaded Linux guest info (NULL if not loaded). */
|
||||||
const struct linux_guest_info *linux_get_guest_info(void);
|
const struct linux_guest_info *linux_get_guest_info(void);
|
||||||
|
|
||||||
|
/* VSCode integration Phase 1: query graphics surface pool.
|
||||||
|
* Returns 0 and fills outputs if the surface pool was allocated and EPT-mapped
|
||||||
|
* into the Linux guest; returns -1 if not initialized (e.g. Linux guest not
|
||||||
|
* loaded). hpa_out receives the host physical address (for UTSM-side blit),
|
||||||
|
* gpa_out receives the GPA visible inside Linux (for the guest mmap path),
|
||||||
|
* size_out receives the pool size in bytes. Any output pointer may be NULL. */
|
||||||
|
int linux_get_surface_info(u64 *hpa_out, u64 *gpa_out, u64 *size_out);
|
||||||
|
|
||||||
|
/* VSCode Phase 3: return host virtual address of the graphics surface pool.
|
||||||
|
* The virtio-gpu 2D backend composites the scanout into this buffer; the
|
||||||
|
* desktop reads it to blit guest graphics onto the IDE host area.
|
||||||
|
* Returns NULL if the pool was not allocated (Linux guest not loaded / alloc failed). */
|
||||||
|
void *linux_get_surface_vaddr(void);
|
||||||
|
|
||||||
/* Configure VMCS for Linux guest and perform vmlaunch.
|
/* Configure VMCS for Linux guest and perform vmlaunch.
|
||||||
* Must be called after vmm_init() + linux_loader_init().
|
* Must be called after vmm_init() + linux_loader_init().
|
||||||
* Returns 0 on successful guest termination, negative on error. */
|
* Returns 0 on successful guest termination, negative on error. */
|
||||||
int linux_launch(void);
|
int linux_launch(void);
|
||||||
|
|
||||||
/* Default Linux command line (can be overridden).
|
/* Default Linux command line (can be overridden).
|
||||||
* nohlt: prevent kernel idle loop from using HLT — only the exec daemon's
|
* Daemon PARK is a VMCALL. Guest idle may HLT; that stays in the
|
||||||
* explicit ioctl(PARK) HLT should trigger VM-Exit park, so that
|
* guest (wait-for-IRQ). Do not use nohlt/idle=poll — poll idle
|
||||||
* linux_launch() returns only after the daemon is ready.
|
* NULL-derefs current after the first real sleep.
|
||||||
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
|
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
|
||||||
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
|
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
|
||||||
* virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5,
|
* virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5,
|
||||||
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。 */
|
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。
|
||||||
|
* VSCode Phase 2: kbd@0xF4003000 IRQ8, mouse@0xF4004000 IRQ9 (virtio-input)。
|
||||||
|
* VSCode Phase 3: gpu@0xF4005000 IRQ10 (virtio-gpu 2D, /dev/dri/card0)。
|
||||||
|
* VSCode Phase 4: xrootfs@0xF4006000 IRQ11 (可写 virtio-blk, /dev/vdc)。
|
||||||
|
* 注:无 extra-rootfs boot module 时,后端改映射 ESP 上的
|
||||||
|
* boot/linux-extra-rootfs.img(AHCI+FAT32),避免 Limine 把 4GiB
|
||||||
|
* 打进 RAM。文件也没有则不注册,guest 探测超时跳过。 */
|
||||||
#define LINUX_DEFAULT_CMDLINE \
|
#define LINUX_DEFAULT_CMDLINE \
|
||||||
"console=ttyS0,115200 earlyprintk=serial nokaslr " \
|
"earlyprintk=serial,ttyS0,115200 console=ttyS0,115200 " \
|
||||||
"no_timer_check loglevel=7 nohlt idle=poll " \
|
"nokaslr debug loglevel=8 noinvpcid nopcid nopti pti=off nogbpages " \
|
||||||
"noapic nolapic nosmp " \
|
"tsc_early_khz=2000000 lpj=4000000 i8042.nokbd i8042.noaux " \
|
||||||
|
"no_timer_check " \
|
||||||
|
"noapic nolapic nosmp ibt=off user_shstk=off " \
|
||||||
"virtio_mmio.device=4K@0xF4000000:5 " \
|
"virtio_mmio.device=4K@0xF4000000:5 " \
|
||||||
"virtio_mmio.device=4K@0xF4001000:6 " \
|
"virtio_mmio.device=4K@0xF4001000:6 " \
|
||||||
"virtio_mmio.device=4K@0xF4002000:7"
|
"virtio_mmio.device=4K@0xF4002000:7 " \
|
||||||
|
"virtio_mmio.device=4K@0xF4003000:8 " \
|
||||||
|
"virtio_mmio.device=4K@0xF4004000:9 " \
|
||||||
|
"virtio_mmio.device=4K@0xF4005000:10 " \
|
||||||
|
"virtio_mmio.device=4K@0xF4006000:11"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef UTSM_LINUX_XSAVE_H
|
||||||
|
#define UTSM_LINUX_XSAVE_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* L2 Linux XSAVE (x87+SSE first). UTSM is built -mno-sse, so every
|
||||||
|
* XSAVE/XRSTOR/XSETBV goes through raw opcodes. Guest state must be
|
||||||
|
* saved before returning to DSK/desktop and restored on linux_resume. */
|
||||||
|
|
||||||
|
int linux_xsave_available(void);
|
||||||
|
void linux_xsave_init(void);
|
||||||
|
void linux_xsave_save_guest(void);
|
||||||
|
void linux_xsave_load_guest(void);
|
||||||
|
void linux_xsave_save_host(void);
|
||||||
|
void linux_xsave_load_host(void);
|
||||||
|
void linux_xsave_set_guest_xcr0(u64 xcr0);
|
||||||
|
void linux_xsave_adjust_cpuid(u32 leaf, u32 subleaf,
|
||||||
|
u32 *eax, u32 *ebx, u32 *ecx, u32 *edx,
|
||||||
|
u64 guest_cr4);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -13,4 +13,17 @@ void log_error(const char *msg);
|
|||||||
void log_hex64(const char *prefix, u64 value);
|
void log_hex64(const char *prefix, u64 value);
|
||||||
void panic(const char *msg);
|
void panic(const char *msg);
|
||||||
|
|
||||||
|
/* D4: DKM 彩色日志——驱动 init 期间的颜色上下文。
|
||||||
|
* log_set_color 传 ANSI CSI(如 "\x1b[36m"),0/"" 恢复默认;
|
||||||
|
* 颜色只走串口,BOOTLOG 缓冲保持纯文本。log_color_enable 总开关
|
||||||
|
* (FUCK [debug] color_log)。 */
|
||||||
|
void log_set_color(const char *ansi);
|
||||||
|
void log_color_enable(int enable);
|
||||||
|
|
||||||
|
/* ---- 内存日志缓冲区(实机启动日志持久化) ----
|
||||||
|
* 由 disk_log_flush() 写入 FAT32 文件 SYSTEM/DESHAB64/DEV/BOOTLOG.TXT。 */
|
||||||
|
void log_capture_enable(int enable);
|
||||||
|
const char *log_get_buffer(void);
|
||||||
|
u32 log_get_length(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ typedef int (*dkm_net_scan_count_fn)(void *ctx);
|
|||||||
typedef int (*dkm_net_scan_result_fn)(void *ctx, u32 n, dkm_net_scan_result *out);
|
typedef int (*dkm_net_scan_result_fn)(void *ctx, u32 n, dkm_net_scan_result *out);
|
||||||
typedef int (*dkm_net_is_wireless_fn)(void *ctx);
|
typedef int (*dkm_net_is_wireless_fn)(void *ctx);
|
||||||
|
|
||||||
|
/* 无线关联回调(wlan 驱动填;实施 802.11 认证/关联 + WPA 握手)。
|
||||||
|
* connect: ssid/password 关联请求,返回 0 = 已关联。
|
||||||
|
* disconnect: 解除关联。 */
|
||||||
|
typedef int (*dkm_net_connect_fn)(void *ctx, const char *ssid, const char *password);
|
||||||
|
typedef int (*dkm_net_disconnect_fn)(void *ctx);
|
||||||
|
|
||||||
typedef struct dkm_net_device_desc {
|
typedef struct dkm_net_device_desc {
|
||||||
const char *name;
|
const char *name;
|
||||||
u8 mac[6];
|
u8 mac[6];
|
||||||
@@ -50,6 +56,9 @@ typedef struct dkm_net_device_desc {
|
|||||||
dkm_net_scan_count_fn scan_count;
|
dkm_net_scan_count_fn scan_count;
|
||||||
dkm_net_scan_result_fn scan_result;
|
dkm_net_scan_result_fn scan_result;
|
||||||
dkm_net_is_wireless_fn is_wireless;
|
dkm_net_is_wireless_fn is_wireless;
|
||||||
|
/* 关联扩展(尾部追加,ABI 兼容)。 */
|
||||||
|
dkm_net_connect_fn connect;
|
||||||
|
dkm_net_disconnect_fn disconnect;
|
||||||
} dkm_net_device_desc;
|
} dkm_net_device_desc;
|
||||||
|
|
||||||
typedef struct dkm_net_device_info {
|
typedef struct dkm_net_device_info {
|
||||||
@@ -79,11 +88,17 @@ typedef struct dkm_net_api {
|
|||||||
int (*scan_count)(u32 index);
|
int (*scan_count)(u32 index);
|
||||||
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
|
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
|
||||||
int (*is_wireless)(u32 index);
|
int (*is_wireless)(u32 index);
|
||||||
|
/* 无线关联(dispatch 到 wlan 驱动回调;尾部追加,ABI 兼容)。 */
|
||||||
|
int (*connect)(u32 index, const char *ssid, const char *password);
|
||||||
|
int (*disconnect)(u32 index);
|
||||||
/* 网络统计查询(返回 0 成功,-1 索引越界)。 */
|
/* 网络统计查询(返回 0 成功,-1 索引越界)。 */
|
||||||
int (*device_stats)(u32 index, dkm_net_stats *out);
|
int (*device_stats)(u32 index, dkm_net_stats *out);
|
||||||
} dkm_net_api;
|
} dkm_net_api;
|
||||||
|
|
||||||
void net_init(void);
|
void net_init(void);
|
||||||
|
|
||||||
|
/* attest: 有线/无线设备计数(启动期能力证明用,见 utsm/attest.h) */
|
||||||
|
void net_attest_counts(u32 *out_total, u32 *out_wireless);
|
||||||
const dkm_net_api *net_get_api(void);
|
const dkm_net_api *net_get_api(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef UTSM_PANIC_H
|
||||||
|
#define UTSM_PANIC_H
|
||||||
|
|
||||||
|
#include <utsm/types.h>
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* F1: 内核 panic 统一兜底(莲花崩溃屏)
|
||||||
|
*
|
||||||
|
* 崩了也得崩出莲花:
|
||||||
|
* - 帧缓冲直接绘制(不走 console_fb 驱动——崩溃时驱动可能已挂)
|
||||||
|
* - 淡金莲花 + panic_symbol + 消息 + 寄存器简表
|
||||||
|
* - DRR 归档(crash_buf)后 halt
|
||||||
|
*
|
||||||
|
* 本路径只依赖:Limine framebuffer(Limine 常驻映射)+ serial + DRR
|
||||||
|
* Emergency Pool(静态区),不依赖堆/驱动/中断。
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* 寄存器简表(可传 0:无异常帧的普通 panic) */
|
||||||
|
typedef struct panic_regs {
|
||||||
|
u64 rip;
|
||||||
|
u64 rsp;
|
||||||
|
u64 rflags;
|
||||||
|
u64 cs;
|
||||||
|
u64 err;
|
||||||
|
u64 vector;
|
||||||
|
u64 cr2;
|
||||||
|
u64 rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11;
|
||||||
|
} panic_regs;
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* panic 模式(FUCK [panic] mode=strict|fallback,默认 strict)
|
||||||
|
*
|
||||||
|
* strict :panic_full 画莲花屏 + DRR 归档 + halt,不返回(零降级铁律)。
|
||||||
|
* fallback:panic_full 只留串口坐标 + 计数后返回,调用点必须紧跟一条
|
||||||
|
* 可执行的备用路径(返回错误码 / 跳过该能力 / 回退实现)。
|
||||||
|
* 寄存器帧非空(CPU 异常)时无论模式都 halt。
|
||||||
|
*
|
||||||
|
* panic_halt 无视模式恒 halt,仅用于"没有任何可继续的状态"的终点:
|
||||||
|
* CPU 异常、LA57 激活(4 级页表 walk 会写坏内存)、DSK 交接失败、
|
||||||
|
* panic_test 演示钩子。
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* 统一 panic 入口:strict 模式不返回;fallback 模式记录后返回 */
|
||||||
|
void panic_full(const char *symbol, const char *msg, const panic_regs *regs);
|
||||||
|
|
||||||
|
/* 恒 halt 的 panic(不受 fallback 模式影响) */
|
||||||
|
__attribute__((noreturn))
|
||||||
|
void panic_halt(const char *symbol, const char *msg, const panic_regs *regs);
|
||||||
|
|
||||||
|
/* 设置/查询 fallback 模式(kernel/main.c 读 FUCK 后设置一次) */
|
||||||
|
void panic_set_fallback(int enable);
|
||||||
|
int panic_fallback_enabled(void);
|
||||||
|
/* fallback 模式下被吞掉的 panic 次数 */
|
||||||
|
u32 panic_fallback_hits(void);
|
||||||
|
|
||||||
|
/* 兼容旧接口:panic("msg") = panic_full("UTSM-PANIC", msg, 0) */
|
||||||
|
void panic(const char *msg);
|
||||||
|
|
||||||
|
#endif /* UTSM_PANIC_H */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user