Compare commits

64 Commits
Author SHA1 Message Date
LinehuandCursor 8554a82677 feat(panic): FUCK [panic] mode=strict|fallback + realtest image variant
- UTSM panic_full returns in fallback mode (logs [PANIC:FALLBACK] + code + hit
  count); panic_halt keeps always-halt sites (CPU exception, VMM-E11 LA57,
  VMM-E15 DSK jump, UTSM-E42 test hook). Call sites given safe continuations.
- Mode passed to DSK/FirstInit/login/desktop via DSK_BOOT_FLAG_PANIC_FALLBACK;
  DKM kernel_api.panic / kern.log.panic return to driver; ext4 fatal aborts the
  op via setjmp/longjmp and DSK falls back to FAT32 ESP.
- build.ps1/Makefile: dev=strict, release=fallback, new realtest (release
  options + strict); default -Variant all; serialized iso packing under -j.
- Fix '*/' inside comments (dkm.h, kernel_api.h, bus.c) that broke the build.
- Includes pending work: dkm_bus service bus, mt7920/wlan drivers, desktop
  files/startmenu/prank, pe-tools, diskinfo, docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 23:57:19 +08:00
Linehu a7e5b2bc7a config(release): bootable on machines without supported NIC (ath9k=0, wlan=0, require_net_wired=0) 2026-10-06 23:33:19 +08:00
Linehu 07f8031111 feat(ath9k): log all wireless controllers when no Atheros match; document AR928x HW requirement + PLT32 loader note 2026-10-06 21:41:58 +08:00
Linehu c80ee79388 config(release): enable ath9k=1 for real-hardware WiFi test (scan + WPA2-PSK connect) 2026-10-06 21:37:48 +08:00
Linehu d95ae091e7 fix(dkm): support R_X86_64_PLT32 relocations in ELF loader (DKM-E37)
Multi-TU drivers produce PLT32 for cross-object calls (lld keeps a
relocation for potentially-interposable global symbols). ath9k.drv
(ath9k.o + wpa2_crypto.o) has 22 such relocations, which tripped
"DKM-E37 UNSUPPORTED RELOCATION" at boot.

PLT32 has the same semantics as PC32 in a static relocatable
(patch = S + A - P), and is a 4-byte write. Added the constant, the
patch computation, and switched the write-width branch to
"not R_X86_64_64 -> u32" so all 4-byte types are handled uniformly.

Verified: all 28 .drv files now use only
{R_X86_64_32S, R_X86_64_64, R_X86_64_PC32, R_X86_64_PLT32}.
2026-10-06 21:37:37 +08:00
Linehu 8e6506721d feat(ath9k): WPA2-PSK crypto core + connect path (auth/assoc/4-way)
wpa2_crypto.{c,h} (new): self-contained, verified crypto base for the
ath9k STA connect path. No heap, no SSE, DKM-freestanding.
- SHA1 (RFC 3174), HMAC-SHA1 (RFC 2202), PBKDF2-HMAC-SHA1 (RFC 2898)
- PRF-512 (802.11i H.4) for PMK -> PTK
- AES-128 encrypt/decrypt (FIPS-197)
- RFC 3394 AES key unwrap (KEK -> GTK)
- CCMP (RFC 3610 + 802.11i): MIC verify + CTR decrypt, and encrypt for TX
- wpa2_kat(): 7 in-driver known-answer/reference cases (SHA1/HMAC/PBKDF2/
  AES x2/CCMP round-trip+tamper/unwrap). Panics ATH-E02 if any case fails.

ath9k.c: connect path built on the crypto base.
- TX descriptor ring + ath9k_tx_frame (AR9002 minimal field set; bit
  layout to be reconciled with HAL initvals during hardware bring-up)
- management frames: auth (open) / assoc request with WPA2-PSK RSN IE
- EAPOL-Key builder with HMAC-SHA1-128 MIC; M2 and M4 senders
- 4-way state machine driven from RX: M1 (ANonce) -> derive PTK ->
  M2 -> M3 (MIC verify, GTK unwrap) -> M4 -> CONNECTED
- ath9k_tx_impl for data frames (CCMP encrypt); data TX before key
  install panics ATH-E04 (zero-degradation rule)
- netdev now registers tx/connect/disconnect callbacks

build.ps1: Build-DkmDriverMulti to compile+ld -r ath9k.c with wpa2_crypto.c.

Verified on host: all wpa2_kat cases pass (AES-128 = FIPS-197 C.1
69c4e0d8..., RFC 3394 C.1 unwrap = 0011..eeff, CCMP round-trip + tamper).
Hardware bring-up (AR9285 initvals, TX/RX descriptor bit layout, key
install into the MAC) still requires real-hardware iteration.
2026-10-06 15:11:22 +08:00
Linehu b780a6b868 feat(desktop): taskbar wifi quick card - scan list / password / connect via net api
- taskbar.c: wifi popup card anchored above tray net icon; net api slots read
  via kernel_api+0x48 hard offsets (UTSM net.h layout, connect@0x48)
- flow: icon click -> scan_start -> per-frame scan_count/result pull -> SSID
  list (security tag + rssi bars) -> open net direct connect / secured net
  password textbox (scan set 1 -> ascii, enter=connect) -> net_api.connect
- no wireless device in QEMU: graceful "not detected" status, no panic (by
  design: wireless dev absence is attest-exempt, not a stub)
- shell.c: handle_key routes to wifi_key when card open (modal)
- sdk kernel_api.h: mirror UTSM dkm_net_api layout (append connect/disconnect
  before device_stats, fixes slot offset mismatch)
- QEMU verified: card opens, renders, graceful no-device path (serial
  "[wifi] no wireless device")
2026-10-06 13:51:00 +08:00
Linehu bd098f51ec feat(wlan): DKM wlan driver framework + net connect/disconnect ABI extension
- wlan.drv: multi-vendor PCI probe (class 02:80; Intel AX2xx/Realtek/Atheros ath9k/Broadcom), registers wireless netdev when real hw present (attest net_wlan)
- scan/connect/disconnect: strict panic skeletons (NETWL-E10/E11/E12) until native chip backends (ath9k first, no fw blob) or linuxguest backend land
- net.h/dkm_shared.h: connect/disconnect appended to dkm_net_api + device_desc (ABI-compatible tail append); net.c dispatch with zero-degrade panic
- QEMU (no wlan card): no netdev registered, attest MISSING exempted by FUCK [attest] as designed
2026-10-06 12:13:59 +08:00
Linehu 14015b78a4 chore: drop realtest image variant (dev/release only) 2026-10-05 21:30:51 +08:00
Linehu ace5711a1e fix(login): atomic ld.lld output (tmp+move) - stop intermittent make failure under Defender lock 2026-10-05 21:24:48 +08:00
Linehu 6caf1aa322 feat: login wallpaper blur backdrop + no logo intro + login spinner; firstInit wallpaper backdrop
- login: drop ohMyLogo intro, blurred back.rgba as full-screen backdrop (built-in data)
- login: spinner (int 16-seg table, no SSE) below card during desktop load, wallpatch clear
- firstInit: same blurred wallpaper backdrop behind wizard cards
- note: 1.8MB wallpaper data also absorbs the DSK-E70 write-zero minefield -> integrity OK
2026-10-05 20:12:51 +08:00
boonxiong2andGitHub 5db517f435 chore/gitignore-local-artifacts
chore(git): ignore IDE config and local run logs
2026-10-05 18:41:54 +08:00
boonxiong2andGitHub c5c192c570 fix/boot-to-desktop
fix(dsk,firstInit,build): let the system boot all the way to the desktop
2026-10-05 18:41:28 +08:00
boonxiong2 ccf76b3ec4 chore(git): ignore IDE config and local run logs
JetBrains only ships a partial .idea/.gitignore, so Deshab-OS.iml and
inspectionProfiles/ kept showing up as untracked. Local QEMU serial dumps
(run_qemu.bat > deshab_log.log) are build-time artifacts of the same kind.

Machine-local files must not land in a shared repository - they carry no
information for anyone else and create noise on every checkout.
2026-10-05 18:35:16 +08:00
boonxiong2 780230599d fix(dsk,firstInit,build): let the system boot all the way to the desktop
Four independent blockers sat between "the image boots UTSM" and "the desktop
comes up". Each one wedged the boot on its own; all four are fixed here and the
tree now reaches "[desktop] desktop ready" under QEMU/OVMF
(evidence: .build_tmp/d2.log).

1. FirstInit made networking a hard gate (CODE/firstInit/main.c)
   The setup wizard always showed the network page, and on a machine with no
   wireless device the only exit was Save - pressing Connect raised NET-E01.
   Networking is not a precondition for entering the system, so a wizard that
   cannot be completed without it is a plain design defect.
   - No wireless device detected -> skip the network page entirely (the
     g_no_wireless_device flag already existed but was only logged).
   - With a device present, add a Skip button (draw + mouse hit-box + keyboard
     Enter), mirroring "I don't have internet" on Windows.
   - Connect keeps its zero-fallback NET-E01 panic: the unimplemented path is
     still honest, it just no longer holds the user hostage.

2. ESP cluster size left no room in the root directory
   (CODE/linux/pack_system_image.sh)
   With the cluster size left to mkfs it chose 1 sector, i.e. 16 directory
   entries per cluster. The ESP root ends up holding exactly 16 entries
   (DESHABBOOT/EFI/LIMINE/BOOT/DRIVER/SYSTEM/USER/NVVARS plus one long-name
   entry each), and the root chain is a single cluster - so on first boot the
   DSK could never write USER.CONF.
   Pin -s 4 (2KiB, 64 entries): it clears the FAT32 65525 lower bound and the
   0xFFF5 boundary that -s 8 lands on at this 256MiB size, and leaves 48 free
   entries. The alternatives were measured, not guessed (see the comment).

3. DSK could not grow the root directory (CODE/dsk/main.c)
   fat32_write_root_file returned -4 "no free dir entry" whenever the scan of
   the root chain came up empty. A FAT filesystem is supposed to extend the
   directory chain, so allocate a cluster, link it at the current tail, write
   the entry there and commit the FAT. Fixing only (2) would have papered over
   this: any real disk whose root fills up would still fail the same way.

4. Nothing generated the CJK bitmap font packs (build.ps1)
   The desktop loads system/font/simhei_16.dbf and panics DESK-E04 FONT PACK
   MISSING without it. The .dbf files are generated from simhei.ttf by
   CODE/font/mkfont.py and are gitignored, but no build step ever invoked it -
   same class of bug as the previously untracked .drv binaries. Generate
   simhei_16.dbf / simhei_24.dbf in build.ps1 (with the existing WSL python3
   fallback for Pillow), rebuilding only when the TTF is newer than the pack.

docs/STATUS.md updated to match: FirstInit wizard and the D3 named API export
table move to done, and the FAT32 / ESP cluster size / CJK font pack entries
are added or corrected. D3 was already fully implemented (table, lookup,
selftest with positive and negative cases, TST-E21..E24 instrumentation) -
only the status row was stale.

Verified boot chain (QEMU/OVMF, dev variant):
  BdsDxe -> Limine -> UTSM -> DKM 17/17 -> DSK -> FirstInit (network page
  skipped) -> USER.CONF written -> login (password accepted) -> desktop:
  "[zhfont] dbf loaded (16px ok)" ... "[desktop] desktop ready"
2026-10-05 18:32:24 +08:00
LinehuandGitHub 10599b62c4 Merge pull request #4 from Linehu-Studio/fix/bootable-image-and-dkm-build
fix(build): make the packed image bootable, restore 9 missing DKM drivers
2026-10-05 15:13:41 +08:00
boonxiong2 240daa3409 docs(lessons): record the three causes of "build succeeds, image won't boot"
Covers the ESP FAT geometry vs OVMF FatPkg, the limine.conf guest module
drift, and the nine drivers that 055c521 untracked without a build step that
could regenerate them. Also the autocrlf/.gitattributes conclusion and the
check_env.sh hardcoded-path trap.
2026-10-05 14:54:25 +08:00
boonxiong2 cf395474bf docs(status): correct stale subsystem states against the source
STATUS.md/TASKBOARD.md under-reported several subsystems that are in fact
implemented. Corrected against the code and against a clean QEMU boot log
(.build_tmp/qemu_final.log, this branch).

- 页表映射接口 was ⬜ "mmio 通用映射待做". It exists and is in use:
  kernel_api.mm_map_mmio / mm_unmap_mmio (PCD|PWT independent window), called
  by apic, ehci, xhci and nvme, including BARs above 4G. -> ✅

- NVMe was 🔶 "PCI discovery only, BAR0>4G 待映射". CODE/DKM/nvme/nvme.c is
  1641 lines: admin SQ/CQ with phase bit, IDENTIFY, IO queue (qid=1), read/
  write/flush, cross-page PRP lists, MSI and MSI-X (vector allocator, IDT pool
  0x40-0xDF), block provider registration, NVME-E01..E03 zero-fallback
  instrumentation. -> ✅

- virtio_net was 🔶 "PCI/capability 枚举, 无 virtqueue". It has a legacy
  split-ring virtqueue (desc/avail/used), vq_setup, 32x2048B RX buffers, TX/RX
  rings and a DHCP DISCOVER->OFFER selftest. -> ✅

- ELF64 .drv loading said 14/14. build.ps1 now compiles all 17 drivers listed
  in manifest.json (fixed on this branch; nine were never built since 055c521
  untracked the .drv binaries). -> 17/17

- FAT32 (DKM side) stays 🔶 but the boundary was wrong. It is read-only
  BPB/FAT/dirent parsing plus a root-directory walk, with no filesystem API
  layer (no open/read/stat) - not "子目录路径待完善". The large-disk read/write
  path lives in the DSK-side FAT32 implementation.

- TASKBOARD 遗留工程债: vector allocator/MSI-X, NVMe high BAR MMIO and
  virtio-net virtqueue RX/TX are done. The remaining FAT32 item is reworded to
  what is actually missing (the FS API layer).

Evidence for the driver/NVMe/virtio rows is the boot log on this branch:
14x "[<drv>] driver ready", and with no NVMe controller / virtio-net device
attached the drivers correctly log a warning and still reach "driver ready"
(designed fallback, both listed in the CLAUDE.md whitelist).
2026-10-05 14:09:19 +08:00
boonxiong2 845a8f0203 chore(git): pin *.sh to LF via .gitattributes
Shell scripts are run by bash (WSL/Linux/CI). With core.autocrlf=true the
working tree gets CRLF, and bash then fails on the stray CR: the packing step
of build.bat died with

    pack_system_image.sh: line 13: $'\r': command not found
    pack_system_image.sh: line 14: set: pipefail\r: invalid option name

core.autocrlf is a per-clone setting and is not shared, so the only reliable
fix is a repository-level attribute. Pin *.sh to LF for every checkout.
2026-10-05 14:05:27 +08:00
boonxiong2 1c99c77b3f fix(build): make the packed image actually bootable
There were three independent reasons a fresh build produced an image that
OVMF would not boot. All three are fixed here; verified by booting the
resulting ISO/deshab-dev.img under QEMU/OVMF through to FirstInit.

1. ESP rejected by OVMF (CODE/linux/pack_system_image.sh)
   The vfat formatter was invoked with an explicit -s 8, forcing 4KiB clusters.
   On the 256MiB ESP that yields exactly 65535 clusters - the FAT32 0xFFF5
   boundary - and OVMF's FatPkg refuses to mount the volume. Symptom: BdsDxe
   logs "failed to load ... Not Found", the ESP shows up as BLKn with no FS in
   the UEFI Shell, and the firmware falls through to the internal shell.
   The sidecar FAT32 image (make_sata_sidecar.sh) mounts fine because it calls
   the formatter without -s. Drop -s here too and let the tool pick geometry.
   Do NOT add -f 1: OVMF mounts nfat=1 and nfat=2 alike, it was not the cause.

2. Limine dropped into a panic (CODE/linux/pack_system_image.sh)
   limine.conf declares the Linux-compat guest modules. build.sh uncomments
   them once Linux has been built, but nothing ever comments them back, so the
   file says "load boot():/boot/linux-initrd.img" while only linux-bzImage
   exists on disk - Limine panics on the missing module and never reaches the
   kernel. Filter the guest module_path lines against SYSTEM/boot/ at pack
   time and write the result to the ESP. Both the root /limine.conf and
   /limine/limine.conf are written, since Limine 10 prefers the latter. The
   tracked source file is left untouched.

3. Nine drivers were never compiled (build.ps1)
   Commit 055c521 untracked the .drv binaries on the grounds that they are
   regenerable, but build.ps1 only ever invoked Build-DkmDriver for 8 of the
   17 drivers listed in manifest.json. timer/acpi/pci/ahci/bootfs/vfs/fat32/
   devfs/ps2kbd had sources under CODE/DKM/ and no build step, so the image
   shipped without them and Limine failed on the first one. Add the missing
   compile steps, grouped by load stage. All 17 now match manifest.json.

Also in this commit (environment/tooling hygiene):
- check_env.sh: stop hardcoding /mnt/d/Code/Deshab (the original author's
  machine); derive the repo root from the script's own location. It was
  reporting a bogus "wsl_mount" failure on every other checkout and sending
  people to install packages that were already present. Also check and
  advertise the image-packing tools (dosfstools/e2fsprogs/fdisk/rsync), which
  were missing from the dependency list even though pack_system_image.sh
  hard-requires them.
- build_initrd.sh: same hardcoded-path fix.
- README.md: relative path instead of the stale absolute one.

Verified: ./build.bat -Variant dev under WSL2/Ubuntu, then QEMU/OVMF boots
through BdsDxe -> Limine -> UTSM -> DKM (17/17) -> DSK -> FirstInit, with the
PS/2 mouse initialising cleanly.
2026-10-05 14:03:39 +08:00
Linehu ee8b607f9a fix: login cursor ghost after double save-bg + integrity GOT immunity (DSK-E70 workaround)
- login: remove duplicate cursor_save_bg after draw_login_card (ghost residue)
- login: hidden visibility on __text_*/__expected_hash -> lea rip, len calc no longer depends on GOT slot
- login: dsk_e70_layout_guard keeps .text out of the DSK write-zero minefield (whitelisted, see STATUS.md)
- patch_login_hash: hash first PT_LOAD filesz (matches loader/runtime range) + e_shstrndx section names
- STATUS.md: register DSK-E70 (unresolved ibuf-tail write-zero, root cause open)
2026-10-05 02:34:15 +08:00
LinehuandGitHub 3aebe962be Merge pull request #2 from Linehu-Studio/build/fix-texture-stamp-guard
build: fix desktop texture regeneration crash on clean tree
2026-10-05 01:44:25 +08:00
boonxiong2 784b774bcb build: fix desktop texture regeneration crash on clean tree
stale-check compared LastWriteTime against a stamp file that does not
exist yet on the first build, so Get-Item threw PathNotFound and aborted
the build before make DSK.

- build.ps1: guard the texSrc/texStamp mtime comparison with
  Test-Path $TexStamp; $needTex is already $true when the stamp is
  absent, so the comparison is only meaningful when both files exist
- .gitignore: ignore IDEA.md local scratch note

Verified: .\build.bat now reaches gen_textures.py and emits
back.rgba / startMenuLogo.rgba / bootlogo.rgba into
SYSTEM/system/deshab64/desktop/textures/.
2026-10-05 01:41:50 +08:00
Linehu a570fe09b5 feat: white cursor with black outline (desktop/login/firstInit) + fix hash patcher param
- outline = empty pixel with any 8-neighbour filled, stays inside cursor save region

- patch_login_hash.ps1: typed byte[] params + ASCII comments, default path fallback (was silently misbinding under some hosts)
2026-10-04 22:43:18 +08:00
Linehu b4d9c8e7ed feat: desktop wallpaper from material pack back.rgba (889x500) instead of system/wallpaper.rgba 2026-10-04 22:21:14 +08:00
Linehu 87f8ce11da feat: all-white mouse cursor across desktop/login/firstInit 2026-10-04 21:43:43 +08:00
Linehu 87d532df01 feat: SAS-R0 boot-time capability attestation - unimplemented feature blocks boot
- attest.c: subsystems report readiness, attest_check() before DSK handoff

- FUCK [attest] require_<name>=1 (default) + not-ready -> lotus panic with feature errcode

- wired: sealed_mem/pckc/sched/drr/fs_fat32_rw/net_wired + placeholders (pkg/brand/dkm/nvme/virtio/wlan/vmx/drr_real_mac)

- negative test: require_pkg=1 -> boot-time PKG-E10 panic (verified); dev/release FUCK whitelist = STATUS.md undone items

- CLAUDE.md whitelist rule updated
2026-10-04 21:34:50 +08:00
LinehuandGitHub 25fbabe368 Merge pull request #1 from Linehu-Studio/fix/del_o_and_fix_build_in_linux
build: untrack generated .o/.d and allow CC/LD override
2026-10-04 20:47:21 +08:00
boonxiong2andGitHub e1ebcd0f89 Merge branch 'main' into fix/del_o_and_fix_build_in_linux 2026-10-04 20:33:01 +08:00
Linehu 055c521ceb chore: gitignore only blocks regenerable binaries - demo video via LFS
- ignore: *.o/*.elf/*.drv/*.img/*.rlib/*.rmeta/*.pdb/*.dbf/*.rgba/target//.build_tmp (all regenerable)

- keep: source assets (png/ttf/webp), vendored payload (SYSTEM/lib dll, SYSTEM/bin exe), demo video (TESTSHOW/*.mp4 via Git LFS), text build logs (.d/.timestamp)

- untrack 290+ build artifacts from index
2026-10-04 20:03:06 +08:00
boonxiong2 3786f2580e build: untrack generated .o/.d and allow CC/LD override 2026-10-04 19:49:19 +08:00
Linehu 5f01904be1 feat: white-tone desktop + login crossfade + BUG-031 FAT chain fix + DESK-E texture panics
- BUG-031: fat32_io.h on-demand FAT sector read (FAT beyond 128KB cache walked OOB, font/wallpaper corrupted past first clusters); host unit test diff=0

- material pack: gen_textures.py wired into build.ps1, startMenuLogo.rgba packaged; missing wallpaper/logo/font now fatal (DESK-E02/E03/E04 red fatal screen)

- login->desktop crossfade 16 steps, boot typewriter screen removed

- IDE exit: Esc detaches and returns to desktop page

- white theme: KS_* light palette, g_wf_light taskbar, hardcoded dark fills replaced

- QEMU verified: labels/clock render, wallpaper + start logo loaded, zero spurious panic
2026-10-04 15:53:04 +08:00
Linehu 174af71b39 feat: exhaustive STUB instrumentation - 241 panic sites across VMM/kernel + whitelist alignment
- VMM cluster: XJ3-E/LIN-E/VMM-E/VMX-E/EPT-E/IPC-E/VVB-E (124 sites)

- kernel cluster: TST-E/MM-E/UTRW-E/LAPIC-E/IDT-E/SAS-E/PRB-E/NETST-E/DKM-E (117 sites)

- whitelist fixes: vmm_init env probes E10-E14 fallback, disk_log read-only block path, DSL-E05 non-FAT32 device 0 probe

- QEMU smoke: wizard -> USER.CONF -> login -> desktop ready, zero spurious panic
2026-10-03 21:26:04 +08:00
Linehu 506d1b18c6 feat: SAS-R0 signature-feature attestation - not-ready means very-early panic
- SAS-E01: scheduler bootstrap failure was "non-fatal" -> panic (O(1)
  scheduler must stand up or nothing runs)
- PCKC-E01: boot-time key-derive roundtrip in utsm_init (get_or_derive
  probe segment, all-zero key or error -> panic; probe slot invalidated)
- DKM-E04..E07: manifest required-driver failures (module missing /
  ELF invalid / symbol scan fail / load fail) -> panic instead of
  "abort stage" silent continue
- QEMU verify: pckc derive roundtrip ok, sealed MAC established,
  selftest OK, boot reaches wizard, zero spurious panics

[STUB] P0/UTSM SAS-R0 attestation | 剩余STUB命中=101(违规=0) | “特色不是招牌,是性命” | 已提交=<this>
2026-10-03 15:49:19 +08:00
Linehu dda0cc9dd0 feat: sealed memory early init + verify - UTSM-E01..E04 very-early panic
- NEW core/sealed.c: utsm_sealed_early_init() runs right after utsm_init:
  empty .sealed section -> UTSM-E01 panic; no root key -> UTSM-E02;
  establishes BLAKE2b(root_key) MAC over [__sealed_start,__sealed_end).
- utsm_sealed_verify(): MAC mismatch -> UTSM-E03; verify-before-init ->
  UTSM-E04. Wired into utsm_selftest (U3+ step).
- .sealed region now non-empty by construction: sealed brand record
  "DESHAB-SEALED-MEMORY-V1-DEAICUP" lives in section(".sealed").
- Before this commit nothing ever referenced __sealed_start/__sealed_end:
  the sealed section was never registered, MACed or verified.
- QEMU verify: [SEAL] region=0x20 bytes, MAC established, selftest OK,
  boot reaches wizard, zero spurious panics.

[STUB] P0/UTSM/core/sealed.c | 剩余STUB命中=101(违规=0) | “封缄即落款,空区即自爆” | 已提交=<this>
2026-10-03 15:40:00 +08:00
Linehu 301006d611 feat: re-instrument per STUB rules - all silent placeholders now panic
- PE-E03 pe_loader.c: unsupported base-reloc type silently skipped -> panic
- PE-E04 pe_shim.c ReadFile: fake TRUE+0-bytes ("pretend EOF") -> panic
- PE-E05 pe_shim.c CreateFileA: no file backend -> panic (no honest-failure excuse)
- PE-E06/E07 x86emu32.c: emu IAT unimplemented API / shim index OOB -> panic
- NETST-E01 net.c: netdev stats all-zero placeholder ("lying zero traffic") -> panic
- apic.c comment: SIPI reserved now points to DKM-E03 skeleton
- pe_dll_manager comments: circular-dep stub call now documented as PE-E01
- virtio_blk.c: capacity declaration marked as instrumentation whitelist
- QEMU verify: wizard -> Save -> verify ok -> login success -> desktop ready,
  zero spurious panics

[STUB] P1/UTSM/pe+net re-instrument sweep | 剩余STUB命中=101 (全部为 panic 坐标注释 /
IDT halt-stub 防御术语 / 白名单) | “未成之物,不留活口”
2026-10-03 12:44:57 +08:00
Linehu cd39caa10c docs: CLAUDE.md add STUB instrumentation rules - grep-verified zero-fallback contract 2026-10-03 12:31:03 +08:00
Linehu 583262f5f0 feat: SKEL skeleton instrumentation - designed-but-unimplemented paths panic
- hypercall.c: 5 Phase 1.3 stubs -> HCALL-E01..E05 panic (no NOSYS return)
- drr_stub.c: snapshot alloc fail -> DRR-E01; new skeletons DRR-E02 root key
  seal, DRR-E03 task kill crypto erase, DRR-E04/E05 segment/system rollback,
  DRR-E06/E07 driver recovery ops dispatch
- sched.c: enable fail -> SCH-E02 panic (no silent demo skip)
- dkm/kernel_api.c: D1 hot-unload DKM-E01, D2 bare code loader DKM-E02,
  SIPI AP wake DKM-E03 skeletons
- virtio_net tx/rx stubs -> VNET-E01/E02; ath9k tx stub -> ATH-E01;
  xhci port reset -> XHC-E01; nvme MSI/MSI-X fail -> NVME-E01..E03 (no
  silent polling downgrade); e1000 MAC fallback -> E1K-E01
- pe_shim unimpl API -> PE-E01; PE ordinal import -> PE-E02
- kernel/main.c: lib sync / vscode install fail -> LNX-E01/E02
- NEW brand/brand.c (E1 boot anim BRAND-E01, E2 time egg BRAND-E02) and
  pkg/pkg.c (pacman skeleton PKG-E01..03), wired into UTSM MAKEFILE
- Deliberate design-internal fallbacks kept and documented (VMX skip,
  bzImage skip, KDE fallback, boot-module fallback, apic->PIC route)
2026-10-03 00:30:54 +08:00
Linehu 38ff3cf93c feat: FS-E strict filesystem mode - ext4 rw driver, FAT32 big-disk fix, forced re-login
- CODE/dsk/ext4.c: minimal full ext4 read/write (GPT probe, feature gate,
  extent depth 0/1 rw, block/inode alloc-free, dir insert, write-verify);
  boot selftest PASS on p2. Unsupported features panic (FS-E2x), zero fallback.
- DSK fat32 write: on-demand FAT entry RMW + chunked free-cluster scan
  (fixes "no free clusters" on 5.3G ESP); rec_len shrink offset (+4) and
  chunk alignment fixes. USER.CONF write-verify ok, firstInit flip ok.
- First-boot: wizard -> persist -> forced login (FS-E06 if skipped/failed)
  -> desktop. login.elf loaded via system/user/use fallback.
- FirstInit network page: Connect now panics NET-E01 (no wlan stack);
  Save (config only) is the only path forward.
- FirstInit/DSK fatal: ohMyLogo lotus fatal screen (NET-E01 / FS-E series).
- Boot screen: Logo.png image removed, "DESHAB P01" text centered only;
  dcp/xj logos regenerated from TEXTURES/.
- run_qemu.bat: main GPT image on SATA ide.0 (AHCI provider must see ext4).
2026-10-02 23:49:05 +08:00
Linehu c4a5295a81 feat: UI strict-error mode - ohMyLogo login intro, typewriter removed, fatal instrumentation
- desktop boot screen: typewriter effect removed, whole screen drawn in one pass
- login: ohMyLogo (DEAICUP lotus, 288px embedded) intro screen before password card
- strict error policy instrumentation (zero degradation): login LOGIN-E01..E05 (integrity/badctx/noconf/nouser/nohash) and desktop DSK-E01 now full-screen fatal with symbol + halt instead of silent skip
- root-cause fixed: missing .build_tmp/mkfat32.ps1 silently left SATA sidecar stale (login.elf 'not found' degradation) - replaced with CODE/linux/make_sata_sidecar.sh (auto adaptive FAT32 rebuild each build)
- recreated lost CODE/login/patch_login_hash.ps1 (.text SHA256 -> .hashseg)
- dev config now full flow: show_logo=1, wizard+login enabled, dev shell off
2026-10-02 21:07:05 +08:00
Linehu 3aa15e7b21 feat: D4 DKM colored log prefixes - every driver has its own temper
- log.c: color context (log_set_color / log_color_enable); ANSI only goes to serial, BOOTLOG.TXT buffer stays clean plain text
- kernel_api.c: dkm_log_set_driver() — driver-name FNV-1a hash into an 8-color bright ANSI palette, deterministic per driver
- load sites (dkm_core builtin + boot_modules external) set color around driver_init and clear after
- FUCK [debug] color_log gate (dev=1, release=0)
- QEMU verified: 1150 colored lines, per-driver stable colors (.build_tmp/qemu_serial_d4color.log)
2026-10-02 19:48:54 +08:00
Linehu 068e0e37c3 docs: README 项目特质章 - explain what makes Deshab Deshab
- new traits section: 疯但好使 / 毛坯神殿非赛博保险柜 / 仪式感是功能(@sealed旋钮+莲花崩溃屏) / 诚实比体面重要 / 错也错得有章法
- refresh stale status refs (UTRW slow path + KAT, phase 1-2 done) and build section (cross-platform Makefile)
2026-10-02 19:37:07 +08:00
Linehu ba16217afe feat: U3 @sealed optional segment marking - phase 2 complete
- UTSM_SEG_F_UNSEALED: plaintext direct-store segments (zero crypto overhead) while keeping capability checks, dirty tracking, intent/commit log and seqlock protocol; default remains sealed
- linker.ld: .sealed output section with __sealed_start/__sealed_end symbols for future static sealed data
- selftest: unsealed segment stores plaintext verifiably + read-back, full SELFTEST PASS (.build_tmp/qemu_serial_u3sealed.log)
- phase 2 (UTSM/UTRW) now complete in ROADMAP_INSANE
2026-10-02 15:30:25 +08:00
Linehu 1e74ee9fab feat: U5 land deterministic chacha20 KAT (RFC 8439 2.4.2)
- test/gen_chacha_kat.py emits KAT from RFC 8439 section 2.4.2 mapped to the impl's 64+64 counter/nonce layout; expected bytes cross-checked against pycryptodome (independent impl) and the official vector: 10f1e7e4d13b5915... byte-exact
- verdict: the 'intermittent chacha20 KAT FAIL' referenced in FUCK comments was a phantom — that test was never merged into this tree; the implementation itself matches RFC byte-for-byte
- selftest now runs KAT + page-MAC determinism checks at selftest=2; full SELFTEST PASS in QEMU (.build_tmp/qemu_serial_u5kat.log)
2026-10-02 15:24:15 +08:00
Linehu 7925c09677 feat: U4 ciphertext page dump tool
- utrw/debug.c: utrw_debug_dump_page(slot,page,offset) — kernel debug-privileged dump of 64B ciphertext + decrypted plaintext hex to serial (PCKC key derive, no capability path)
- selftest demo: dump at offset 128 shows plaintext 'deshab utsm sealed memory selftest' vs unrelated ciphertext; SELFTEST PASS (.build_tmp/qemu_serial_u4dump.log)
2026-10-02 14:40:26 +08:00
Linehu 42fc06439c feat: R3b wire drr_emergency_pool_kb / drr_recovery_log_size from FUCK
- drr_apply_config(pool_kb, log_size) before drr_init; pool_limit clamped to compile-time 64KB static pool (min sub-buffer layout enforced, defaults kept on bad values); recovery log size clamped to remaining pool
- verified in QEMU: pool_kb=32 -> pool_limit=0x8000, log_capacity=0x80, boot continues (.build_tmp/qemu_serial_r3b.log)
2026-10-02 14:34:35 +08:00
Linehu b76e8f1556 feat: U2 UTRW slow path real branches
- slow_path.c: KEY_MISS -> PCKC re-derive (retry OK); EPOCH -> refresh cap epoch via out_cap (retry OK); STALE_CAP permanent; MAC_FAILED/POISONED -> PCKC invalidate + DRR fault report; RETRY passthrough
- status.h: add UTSM_ERR_MAC_FAILED (-11); utrw.h: slow path takes cap/rights/offset/len/reason + out_cap
- selftest: EPOCH refresh + KEY_MISS re-derive + POISONED passthrough coverage, full SELFTEST PASS in QEMU (.build_tmp/qemu_serial_u2slowpath.log)
2026-10-02 14:29:28 +08:00
Linehu 1eaf1aedf8 docs: F3 cross-platform build status (Makefile) 2026-10-02 14:22:24 +08:00
Linehu 23c29f7980 feat: F3 cross-platform root Makefile (Linux/macOS make dev|release|realtest)
- mirrors build.ps1: kernels (UTSM/DSK), user apps, tools, 8 DKM .drv drivers (clang -c + ld.lld -r), config injection from build/configs/<variant>, packing via CODE/linux/pack_system_image.sh
- panic logo regeneration hook (ohMyLogo.png newer -> gen_panic_logo.py)
- Windows still primary via build.ps1; Makefile targets POSIX (pack needs sudo + mkfs)
2026-10-02 14:22:00 +08:00
Linehu 6e44fa5df7 feat: DEAICUP lotus panic screen + F2 fbcon abstraction
- panic screen now draws embedded ohMyLogo.png (384px RGBA, gen_panic_logo.py); background color sampled from image corners so screen bg matches logo bg
- F2: include/utsm/fbcon.h unified framebuffer console (pixel/blend/fill/rgba-image/text with FBCON_HAVE_FONT opt-in); panic.c migrated onto it
- build.ps1 regenerates panic_logo_data.inc when ohMyLogo.png changes
2026-10-01 23:50:04 +08:00
Linehu e4307ff0ec feat: F1/F4/F5 phase-1 skeleton + insane-roadmap docs rework
- docs: ROADMAP_INSANE (five-phase plan), TASKBOARD, STATUS single source of truth, PHILOSOPHY; rewrite README/ARCHITECTURE non-goals; archive ROADMAP phase 0-9; drop contradictory dev-plan doc
- F1: unified lotus panic screen (panic_full: fb draw + regs dump + DRR crash archive); IDT exception path and dkm_api_panic routed through it
- F4: leveled boot selftest (FUCK [boot] selftest=0|1|2), IDT/CS fatal checks + DMA probe
- F5: [dsk] skip_firstinit direct-to-desktop; build/configs dev+release templates now injected
2026-10-01 23:29:49 +08:00
Linehu 1074962546 ohhhhhhhhhhh (exclude linux-extra-rootfs.img build artifact) 2026-10-01 23:13:42 +08:00
LinehuandCursor 737a2cb0db fix: let nested-KVM vmlaunch succeed with Virtual NMIs
Self-test and Linux guest were failing VM-instruction error 7. Share a KVM-style control adjust, request PIN_VIRTUAL_NMIS, and stop intercepting guest #PF. Land isolated XNU/Python/KDE scaffolding off the boot path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 10:48:07 +08:00
Linehu cc0fcf26e3 FU 2026-08-29 18:47:54 +08:00
Linehu 880906edc9 docs: 添加 Apache 2.0 许可证 (Apache-2.0) 2026-08-10 13:54:51 +08:00
Linehu 2a50fa58c7 FUCKKKK 2026-08-09 22:20:01 +08:00
xyou 8717ea8f20 docs: 多智能体推进收尾交接文档 + Bug记录/策划文档状态修正
新增 docs/上下文/260805/交接文档-多智能体推进收尾.md:W2-W6 全程成果、W6 丢失项、Bug 状态核对、下一步优先级、环境坑点

Bug记录.txt: BUG-005/006/007 状态 OPEN->FIXED(代码已修于 a26866e,文档过时未同步)

系统开发策划.md: M2 进度更新(vector allocator/LAPIC EOI 标完成,MSI-X 标 NVMe完成/virtio_net待做,B7 阻塞点更新)
2026-08-05 14:08:11 +08:00
xyou 4a62932db2 FUCK3 2026-08-05 13:58:13 +08:00
cool 6287513794 feat: 添加 Linux 预览后端和 Notes 应用预览版本
- 创建 desktop_app_linux.h 作为跨平台后端抽象层
- 创建 notes_preview.c 使用 5x7 位图字体渲染
- 添加 Makefile.linux
构建脚本
- 添加 README_LINUX_PREVIEW.md 使用说明- 修复键盘映射、字符渲染、文本输入显示等问题
2026-08-05 13:15:46 +08:00
xyou 86df253a32 chore: gitignore Rust target/ 构建产物 + linux-rootfs.img 大镜像 2026-08-04 21:29:02 +08:00
xyou 99b51c8a1f merge: Rider IDE vcs.xml 配置 2026-08-04 18:36:18 +08:00
xyou 019585f2bf feat: B7阶段3 NVMe MSI/MSI-X中断驱动化 + IDT向量分配器 + 幽灵CQE防御
- nvme.drv: MSI-X表项编程 + 动态向量(0x40-0xDF池) + 完成槽+irqsave自适应轮询混合模型 + CID校验防御幽灵CQE(BUG-20260803-010) + g_cid跳0 + handler自写LAPIC EOI
- IDT: 向量分配器(bitmap+ctzll, O(1)) + irq_vector_alloc/free (kernel_api尾部追加)
- FUCK: apic_route=1 (生产配置启用IOAPIC->LAPIC接管)
- LESSONS_LEARNED: B7阶段3经验教训(向量分配器/MSI编程顺序/混合轮询/幽灵CQE)
- Bug记录: BUG-20260803-010 (QEMU MSI-X unmask边界幽灵CQE)
2026-08-04 18:36:09 +08:00
xyou a26866e339 feat: B7 APIC/IOAPIC接管 + NVMe写路径 + virtio_net virtqueue + PE兼容层修复 + QEMU测试套件全自动化
中断路由 (B7):
- apic.drv: LAPIC enable + IOAPIC重定向表编程(MADT ISO感知) + apic_route/apic_route_legacy/apic_diag开关
- UTSM: LAPIC EOI钩子(register_apic_eoi, ABI尾部追加) + IDT 0-255全覆盖 + spurious 0xFF特判
- FUCK: apic_route=0默认PIC(零回归) / =1 IOAPIC->LAPIC接管(失败完整降级) / legacy放开IRQ1/12/11

存储:
- nvme.drv: 写路径 + PRP list + 读回校验 + MSI/MSI-X中断驱动(nvme_msi开关, 回退纯轮询)
- build.ps1: apic.drv纳入构建(原为预置二进制)

网络:
- virtio_net: virtqueue RX/TX + netdev注册 + DHCP自测(slirp真实OFFER)

PE兼容层:
- pe_shim: guest->host指针转换(g_emu_mem_base + gp()) + shim fn32列补齐
- gen_pe.py: PE32+ RVA偏置修复(BUG-004) + cmd.exe生成
- desktop_app.h: da_mouse_poll先判AUX位 + da_mouse_init冲刷残留(BUG-003/008)
- shell: Ctrl修饰键跟踪(BUG-001) + g_run_image扩至1MB(BUG-002)

QEMU测试套件:
- 9用例全自动化(Run-All.ps1): boot-regression/driver-matrix/linux-compat/pe-compat/deaicup-e2e/userapps-matrix/network-e2e/performance-baseline/x86emu32-coverage
- QemuTest.ps1公共库: 串口断言/monitor sendkey/screendump/HTTP服务器/并发隔离

文档:
- CLAUDE.md: APIC/IOAPIC能力 + kernel_api暴露 + IRQ后端升级状态
- LESSONS_LEARNED.md: B7 LAPIC/IOAPIC经验教训(2条)
- Bug记录.txt: BUG-20260801-001~008 + BUG-20260803-009(9条)
- test-cases/*.md: 全部补充自动化脚本小节
- 桌面设计.md: .lnk快捷方式 + LINUXAPP.CNF Linux应用图标
- 系统开发策划.md: NVMe/virtio_net标完成 + B2/B7标已解决 + M2进度列
2026-08-04 18:08:08 +08:00
xyou f52157b7af FUCKYOU 2026-07-31 13:53:49 +08:00
626 changed files with 200790 additions and 12750 deletions
+8
View File
@@ -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
+30 -2
View File
@@ -1,6 +1,34 @@
ISO/deshab.img
*.o
*.obj
*.elf
*.drv
*.img
*.iso
*.vhd
*.raw
*.ppm
target/
*.rlib
*.rmeta
*.pdb
*.dbf
*.rgba
IDEA.md
SYSTEM/boot/linux-extra-rootfs.img
*.d
target/
# ---- 本地工作区 ----
.build_tmp/
.trae/
docs/workbuddy-planning/
sata_fat32_dsk.img
# IDE 配置(JetBrains 自动生成的 .idea/.gitignore 只覆盖了部分文件)
.idea/*.iml
.idea/inspectionProfiles/
# 本地运行日志(如 run_qemu.bat > deshab_log.log 的串口输出)
*.log
deshab_log.log
+223
View File
@@ -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。
---
+57
View File
@@ -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
---
+530
View File
@@ -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
+26
View File
@@ -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
+215 -22
View File
@@ -55,6 +55,10 @@ DMP
PCKC
Per-CPU Key Cache
每 CPU 密钥缓存
FUCK
内核启动配置文件
INI 分区格式,控制启动行为/驱动开关/算法参数/桌面行为
```
## 关键设计约束
@@ -81,20 +85,46 @@ SYSTEM/
SYSTEM/driver/manifest.json
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
SYSTEM/STARTMENU.CNF
M3 开始菜单注册表(FAT32 根目录,desktop 启动时解析)。
格式 NAME|显示名|path|args;path 支持 builtin:<name> / program/xxx.exe(GUI PE,
g++ 编译,run_windowed 嵌入桌面)/ bin/xxx.elf(ELF 工具,全屏加载)/
/guest/path(Linux guest 应用)。显示名可 UTF-8 中文;文件缺失时回退全部已注册应用。
纯 CLI exe(bin/pciscan.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行,
输出走串口。源码在 CODE/tools/pe-tools/(build.ps1 用 mingw-w64 g++ 编译),
ELF 工具源码在 CODE/tools/diskinfo/(→ SYSTEM/bin/diskinfo.elf)。
SYSTEM/program/ + SYSTEM/bin/ + SYSTEM/lib/ + PEAPPS.CNF + LINUXAPP.CNF + STARTMENU.CNF
桌面 PE/工具链全部经 DSK FAT32 只读 ESP——pack_system_image.sh 会把这几项
额外拷到 FAT32 ESP(p1)。漏拷则开始菜单/PE 应用在 GPT 镜像里静默失效。
SYSTEM/system/deshab64/FUCK
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为、panic 策略([panic] mode=strict|fallback)。
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
CODE/UTSM/kernel/ini_parser.h / ini_parser.c
UTSM INI 配置解析器,支持分区格式解析、key/value 查询、整型和布尔值便捷获取。
CODE/DKM/README.md
DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。
CODE/UTSM/README.md
UTSM 模块设计说明。
RE/UTSM_算法记录.md
docs/RE/UTSM_算法记录.md
UTSM 核心算法记录。
RE/UTSM_设计架构.md
docs/RE/UTSM_设计架构.md
UTSM 架构记录。
RE/驱动模块ABI设计.md
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。
docs/RE/驱动模块ABI设计.md
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机、boot module 方案和服务总线 ABI(§8.5)。
docs/RE/万物互联与服务总线设计.md
万物互联架构定稿:dkm_bus 按名服务总线(kern.*/srv.*)、dsk_boot_context 逐槽迁移表、
DSK 溶解地图、P0-P6 落地路线与错误码登记(BUS-E/DKM-E44..47/SESSION-E)。
ISO/deshab.img
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
@@ -108,22 +138,44 @@ ISO/run_qemu.bat
已知说明:
```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/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
build.bat: 调用 build.ps1。
build.ps1: 编译 UTSM,生成 SYSTEM/boot/utsm.elf,并把 SYSTEM 打包为 ISO/deshab.img。deshab.img 是 GPT 磁盘镜像,包含 FAT32 EFI System Partition。
ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab.img,串口输出到 stdio。
build.bat: 调用 build.ps1,透传参数(如 -Variant dev)。
build.ps1: 编译 UTSM/DSK/工具/驱动,把整个 SYSTEM/ 打成 GPT(FAT32 ESP + ext4) 镜像:
- ISO/deshab-dev.img 开发者模式(dev_mode=1,自动跑 shell.elf 测试,调试全开,QEMU 验证选项,panic=strict)
- ISO/deshab-release.img 发布版(dev_mode=0,进桌面+登录,调试零开销,真机安全选项,panic=fallback 即关闭 panic)
- ISO/deshab-realtest.img 真机测试版(release 同款真机选项 + 登录路径,panic=strict 即启用 panic)
参数 -Variant dev|release|realtest|both|all(默认 all = 三者;both = dev+release)。both/all 构建后恢复 SYSTEM 为 dev 配置。
配置模板在 build/configs/{dev,release,realtest}/(FUCK + firstInit.txt),打包前注入到 SYSTEM。
realtest/FUCK 由 release/FUCK 复制而来,仅头注释与 [panic] mode 不同;改 release 真机选项须同步 realtest。
ISO/run_qemu.bat [dev|release|realtest]: 用 QEMU + UEFI 固件启动对应镜像(默认 dev),串口输出到 stdio。
ISO/run_qemu_kvm.sh: 默认 12G 内存(Limine 会把 SYSTEM/boot 里的 rootfs 模块装进内存)。
build.ps1 自动把 Git usr/bin 加入 PATH(sh.exe + mkdir),解决 make `mkdir -p` 在 cmd.exe 下失败的问题。
```
当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT + FAT32 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 驱动:
- **timer** (stage0): PIT 校准, 100ms busy-wait demo
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC enable + IOAPIC 重定向表编程(ISO 表感知); B7: FUCK `apic_route=1` 时 IOAPIC→LAPIC 接管(注册 UTSM LAPIC EOI 钩子 + PIC 全屏蔽 + TPR=0 + tick vec 0x20 链路自验,失败自动完整降级回 PIC); `apic_route_legacy=1` 额外放开 IRQ1/12/11 持续投递(e1000 强制 level/active-low); `apic_diag=1` HPET 绝对时基对拍 + 键鼠流观察窗
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
- **pci** (stage0): PCI config space 扫描, 设备枚举
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管)
@@ -137,18 +189,159 @@ DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解
- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup
- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包
当前内核平台能力:IDT 0–47 stub、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、Limine RSDP/framebuffer/module/HHDM request。
当前内核平台能力:IDT 0–255 stub 全覆盖、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、UTSM 侧 LAPIC EOI 钩子(`idt_register_apic_eoi`,IRQ 分发末尾调用,spurious 0xFF 特判跳过)、IOAPIC→LAPIC 路由接管(B7,legacy IRQ1/12/11 可持续投递)、Limine RSDP/framebuffer/module/HHDM request。
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages。
kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages, block provider registry, mmio/mm_map_mmio/mm_unmap_mmio, register_apic_eoi(尾部追加,ABI 兼容)。
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
下一阶段优先路线:
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。
3. **IRQ 后端升级**: 在现有 PIC fallback 稳定基础上扩展 IDT/vector allocator/APIC EOI/IOAPIC redirection,逐步迁移设备 IRQ,最后接入 MSI/MSI-X。
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。
7. **FirstInit 完善**: 修复按键崩溃(fb_text 优化问题)、实现鼠标光标移动、user.conf 写盘(需 block write API)、网络配置界面。
8. **鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动。
下一阶段优先路线(活跃路线 = **万物互联 P0-P6**(D6/D7,设计定稿 `docs/RE/万物互联与服务总线设计.md`)+ `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`):
0. **万物互联(D6/D7,当前最高优先)**: P0 manifest 依赖图强制 → P1 dkm_bus 双向服务总线(kern.*/srv.*)→ P2 fat32/ext4.drv 归位 → P3 加载器 FS 化 + stage4 + ps2mouse.drv → P4 六 app 模块 DKM 化(netman/shell/firstInit/login/desktop/cmd)→ P5 DSK 退役 + session.drv → P6 常驻调度 + 真 IRQ 输入。每 phase QEMU 验证后提交,进度见 STATUS.md 万物互联表。
1. **一期 F 系列收尾**: F2 统一控制台后端(fbcon.h 抽象已落,panic 已用)。(F1 莲花 panic / F3 跨平台 Makefile / F4 boot 分级自检 / F5 skip_firstinit 已完成)
2. **二期 U 系列**: ✅ 已完成(U1 UUID 表 / U2 Slow Path 真分支 / U3 @sealed 可选标记+linker .sealed section / U4 密文 dump / U5 RFC 8439 KAT)。chacha20 KAT 三方对拍通过(pycryptodome+官方向量+C),"间歇 FAIL" 确认为从未合并的幽灵缺陷。
3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表升级为服务总线(并入万物互联 P1)。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本)
4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核(多核在 P6 常驻调度之后排队)。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS)
5. **五期 E 系列**: 启动莲花动画、假想时光键彩蛋、提交信息规范、风味 ISO。
6. **工程债(Phase 存档遗留)**: vector allocator/MSI-X、NVMe 高位 BAR、virtio-net virtqueue、DKM 零 warning。
## M1 里程碑完成状态(2026-07-29)
### 任务 A: B6 FirstInit 按键崩溃修复
**状态**: [RESOLVED-NOT-REPRODUCIBLE]
**验证结果**:
- QEMU 复现测试完成,系统完整运行 FirstInit 流程无崩溃
- 按键扫描码处理正常,字段输入正确(测试序列: a, b, Enter, shift+comma, Enter, 1, 2, Enter)
- user.conf 成功写入磁盘,firstInit.txt 成功更新为 "1"
- 系统继续加载 desktop
**证据**: `.build_tmp/qemu_serial_b6.log` 串口日志,调试记录 `debug-firstinit-key-crash.md`
**结论**: B6 崩溃在当前构建中未复现,可能已在之前的修改中修复,或崩溃条件较特殊未触发。
### 任务 B: PS/2 鼠标完整实现
**状态**: [COMPLETED]
**实现内容**:
1. **完整初始化序列** (`CODE/mouse/main.c`):
- ps2_drain() 冲刷控制器残留数据
- 读配置字节 → 启用 AUX 时钟/IRQ/翻译 → 写回
- 启用 AUX 端口
- 复位鼠标 → 验证 ACK/自检/设备 ID
- Set Defaults → 采样率 100 → 分辨率 2 → 启用数据报告
- 全程超时保护(100000 次),单步失败只记日志继续
2. **IRQ12 中断处理**:
- 经 kernel_api.irq_register 注册 IRQ12 handler
- 读数据前检查 AUX 位(键盘数据留给键盘路径)
- 3 字节包解码:同步位验证、溢出检测、9 位有符号位移合成
- 坐标累计并钳位到 framebuffer 边界
- 安全不变式:注册但不解屏蔽 PIC,DSK 阶段 IF=0
3. **共享状态接口**:
- `mouse_state_t` 结构体(magic/version/x/y/buttons/计数器/边界)
- `g_mouse_state` 导出全局符号
- `mouse_poll_state()` 只读快照查询接口
**验证结果**:
- mouseInit 初始化成功:`cfg byte=0x47`, `data reporting enabled`, `IRQ12 handler registered`
- FirstInit 中鼠标轮询正常:AUX 端口启用、复位、自检、流模式启用成功
- 鼠标数据包正确接收和解码(串口日志显示 "[mouse] pkt")
**证据**: `.build_tmp/qemu_serial_mouse.log` 串口日志
**技术要点**:
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
- 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志
## M4 里程碑完成状态(2026-09-15):SAS-R0-PCQ 调度器 + DRR 恢复根
### 任务 A: SAS-R0-PCQ 调度器(ROADMAP Phase 7)
**状态**: [COMPLETED]
**实现内容**:
1. **O(1) 位图调度器** (`CODE/UTSM/sched/sched.c`):
- 64 槽 TCB 表 + 8 CPU runqueue(当前 BSP-only),ready_bitmap ctz 选最高优先级(0 最高)
- 侵入式 O(1) FIFO runqueue(slot 索引非指针),冻结 ABI 9 API 真实现
- 任务栈从 arena 分配,entry/arg/stack_base 存 task_aux
2. **xv6 式栈驱动切换** (`CODE/UTSM/sched/switch.S`):
- 抢占 = IRQ 帧落在被抢占任务自己的栈上,切换只换 rsp
- trampoline 跳 entry;schedule/yield/sleep/exit 全路径
- 切换路径只触碰 crypto_context 指针(禁止扫描 capability/MAC/checkpoint)
3. **LAPIC timer tick** (`CODE/UTSM/arch/x86_64/lapic_timer.c`):
- MMIO 映射 + TSC 延时校准(10ms 逆计数法)+ 周期模式
- tick_hz/timeslice_ms 由 FUCK [sched] 配置(默认 100Hz/10ms)
- demo 窗口含 tick 失速兜底:心跳无进展自动中止窗口,不卡死 bootstrap
4. **FUCK 配置**: `[sched] enable/tick_hz/timeslice_ms/demo_ms/demo_fault`
**验证结果**:
- selftest: runqueue O(1) PASS、block/wake PASS
- demo 窗口: 3 任务(prio 1/20/40)按优先级交错运行并正常退出
- 窗口结束 quiesce → timer stopped → 正常交接 DSK
### 任务 B: DRR 恢复根(ROADMAP Phase 8)
**状态**: [COMPLETED-MAC占位]
**实现内容** (`CODE/UTSM/drr/drr_stub.c`):
1. **快照区**: 静态区域(128 页容量,编译期),页条目含 seg_slot/page_index/crc
2. **snapshot_dirty**: 遍历 ACTIVE 段 dirty_shard 位图收集脏页 → 拷贝+CRC64 → 清位图
3. **A/B 双槽 checkpoint**: ckpt_a/ckpt_b + CRC + active slot 切换
4. **rollback**: 页级回滚 + CRC verify;segment/system 级接口就位
5. **看门狗**: register/kick/timeout 检测,任务退出自动注销;异常钩子接入 fault 路径
6. **FUCK 配置**: `[drr] watchdog_enabled`;`[utsm] drr_emergency_pool_kb/drr_recovery_log_size`
**验证结果(含负向测试 demo_fault=1)**:
- ckpt snapshot ok(1 page)→ rollback done → rollback verify ok
- 看门狗负向测试: fault 任务(slot 3)死循环 → `watchdog timeout task_slot=0x3` 检出 → 回滚执行 → demo 窗口正常结束
- 证据: `.build_tmp/qemu_serial_sched_m4.log`
**遗留**:
- MAC 为 CRC64 占位(Phase 9 换真实加密)
- task_kill crypto erase(key_epoch++)、Per-CPU 真启用、驱动 recovery ops 待做
---
## 插桩铁律(STUB 规约 · 零降级强制执行)
> "你若为了'能编译/能启动'而隐藏未实现,视为违反 Deshab 哲学。
> 本项目的成功标准是 **panic 坐标清晰**,不是 **QEMU 不崩**。"
### 1. 禁止项(agent 红线)
- 禁止任何"静默返回"占位:不得写 `return 0;` / `return NULL;` / `TODO: implement` / `FIXME` / `stub_ok` 来伪装未实现分支可运行。
- 所有"设计已定但未实现"的函数/分支 **MUST** 调用 panic 原语(见 §2),禁止返回错误码装没事。
- 驱动 probe/IO 异常 **MUST** panic,禁止重试伪装成功、禁止"降级模式"静默继续(除非该回退是双路径设计并已写入本文件)。
- 禁止"先跑通再说"的临时补丁;宁可启动即 panic,不可带病前进。
- 不得声称"已全量插桩",除非 §4 的 grep 自检输出为 CLEAN。
### 2. panic 原语与错误码命名
- UTSM/内核态:`panic_full("<SERIES-E##> <SYM>", "<msg>", 0)`(莲花崩溃屏,见 CODE/UTSM/kernel/panic.c)。
- DKM 驱动态:`g_log->panic("<DRV>-E## <SYM>")`(经 kernel_api.panic 槽,禁止直连内核符号)。
- DSK:`dsk_fatal("<SERIES-E##> <SYM>")`;FirstInit/login:各自 `*_fatal()`。
- 错误码系列:`FS-E`(文件系统/DSK 调度)、`ELF-E`、`HCALL-E`、`DRR-E`、`SCH-E`、`DKM-E`、`PE-E`、`LNX-E`、`VNET/ATH/XHC/E1K/NVME-E`(驱动)、`PKG-E`、`BRAND-E`、`NET-E`(FirstInit)、`LOGIN-E`。新模块按 `<MODULE>-E##` 注册到 docs/STATUS.md。
### 3. 改动节奏(防"一次性插完"幻觉)
- 每次只处理 **一个文件** 的 STUB→panic,改完输出:改动行号 + 剩余 STUB 计数。
- 骨架允许"不接线"(无人调用的 skeleton 函数),但接线后行为必须是 panic。
- 例外白名单(设计内双路径回退,允许非 panic):APIC 自验失败回 PIC、块设备缺失回 boot module、KDE 回落 native、VMX/bzImage/kernel.krl 未配置跳过。新增白名单项必须改本文件并说明理由。
- **panic 双模式(FUCK `[panic] mode=strict|fallback`,默认 strict)**:strict = 本规约原样执行。fallback = 全局白名单开关,用于真机/演示"带病看全貌":
- UTSM `panic_full(sym,msg,NULL)` 只打 `[PANIC:FALLBACK]` + 错误码 + 命中计数后返回,调用点走紧随其后的备用路径(跳过该能力/驱动/段/页面);DKM `kernel_api.panic` / `kern.log.panic` 同样返回驱动,驱动 init 返回非零。
- 模式经 `DSK_BOOT_FLAG_PANIC_FALLBACK (1<<4)` 下传,DSK/FirstInit/login/desktop 的 `*_fatal` 同样只留串口后返回;DSK ext4 内部 fatal 经 `__builtin_setjmp/longjmp` 中止本次操作 → 只用 FAT32 ESP。
- **永远停机(`panic_halt`,两种模式都不返回)**:CPU 异常(带 regs)、LA57 已启用(VMM-E11,4 级页表遍历会写坏内存)、DSK 跳转失败(VMM-E15)、panic 测试钩子(UTSM-E42)。
- 新增 panic 点必须保证 panic 调用后存在安全的继续路径(return 错误码 / continue / 置能力未就绪),否则改用 `panic_halt` 并登记到上一条。
- 启动期能力证明(attest,CODE/UTSM/kernel/attest.c):FUCK `[attest]` `require_<name>=1`(默认)而能力未就绪 → 交接 DSK 前 panic(莲花屏,错误码=该能力登记码)。`require_<name>=0` 豁免行是设计内白名单,仅限两类:① 该构建环境不可能具备(QEMU 无 wlan/嵌套 VMX);② STATUS.md ⬜/🔶 未落地项(实现后逐项删除豁免行)。当前豁免清单见 build/configs/*/FUCK [attest]。
### 4. 自检命令(验收只认这个输出,不认自然语言保证)
```
powershell -Command "Get-ChildItem CODE\UTSM,CODE\DKM,CODE\dsk -Recurse -Include *.c,*.h |
Select-String -Pattern 'TODO|FIXME|stub_ok|\bstub\b|未实现|暂不支持|待实现|not implemented' |
Group-Object Path | Sort-Object Count -Descending"
```
- 命中行 **只允许** 两类:① panic 调用/其注释(含错误码字面量);② 本文件白名单内的设计内回退日志。
- 其余命中 = 未清干净,禁止提交。基线快照(2026-10-03):~118 命中,绝大多数已是 panic 坐标注释;新增代码不得抬高裸命中数。
### 5. 验收格式(每次插桩提交必须附)
```
[STUB] <阶段>/<模块>/<文件>::<函数> | 改动行=<L1-L2> | 剩余STUB=<N> | “<一句疯洁标语>” | 已提交=<hash>
```
- 例:`[STUB] P1/UTSM/vmm/hypercall.c::hcall_utrw_read | 改动行=176-181 | 剩余STUB=112 | “封缄之下,未成之物当自爆”`
+180
View File
@@ -103,6 +103,15 @@ typedef struct __attribute__((packed)) {
u32 creator_revision;
} acpi_sdt_header;
/* ACPI Generic Address Structure (GAS) — HPET 等表使用 */
typedef struct __attribute__((packed)) {
u8 space_id; /* 0=SystemMemory, 1=SystemIO, 2=PCIConfig, ... */
u8 bit_width;
u8 bit_offset;
u8 access_size; /* 0=undefined, 1=byte, 2=word, 3=dword, 4=qword */
u64 address;
} acpi_generic_address;
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
@@ -212,6 +221,168 @@ static const acpi_rsdp_v2 *acpi_find_rsdp(const void *limine_rsdp) {
return NULL;
}
/* ---------------------------------------------------------------
* MCFG (PCIe ECAM Memory-mapped Configuration) 解析
*
* **QEMU 无法测试但真机必需**: PCIe 配置空间通过 ECAM 访问,
* MCFG 表提供 ECAM 基址和总线范围。没有 MCFG 解析,
* 真机无法访问 Bus 0 以外的 PCIe 设备配置空间。
* QEMU 的 PCI 设备都在 Bus 0, 使用传统 CF8/CFC 端口即可。
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
u64 base_address; /* ECAM 基址 (4K 对齐, 256MB per bus) */
u16 pci_segment_group; /* PCI 段组号 (通常为 0) */
u8 start_bus_number; /* 起始总线号 */
u8 end_bus_number; /* 结束总线号 */
u32 reserved;
} mcfg_entry;
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u64 reserved; /* 保留, 通常为 0 */
/* mcfg_entry entries[] 紧跟 */
} mcfg_table;
static void acpi_parse_mcfg(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(mcfg_table)) return;
const mcfg_table *mcfg = (const mcfg_table *)hdr;
u32 entry_size = sizeof(mcfg_entry);
u32 entries_len = mcfg->hdr.length - sizeof(mcfg_table);
u32 count = entries_len / entry_size;
g_log->info("[acpi] MCFG entries:");
log_hex("[acpi] count=", count);
for (u32 i = 0; i < count; i++) {
const mcfg_entry *e = (const mcfg_entry *)((const u8 *)mcfg + sizeof(mcfg_table) + i * entry_size);
log_hex("[acpi] MCFG base=", e->base_address);
log_hex("[acpi] MCFG seg=", e->pci_segment_group);
log_hex("[acpi] MCFG bus_start=", e->start_bus_number);
log_hex("[acpi] MCFG bus_end=", e->end_bus_number);
/* 验证 ECAM 基址合理性 */
if (e->base_address == 0 || e->base_address == 0xFFFFFFFFFFFFFFFFULL) {
g_log->warn("[acpi] MCFG entry has invalid base address");
continue;
}
/* 真机: ECAM 基址通常在 0xB0000000 - 0xBFFFFFFF (3G-4G 范围),
* 但也可能在 4G 以上 (特别是大内存系统) */
if (e->base_address >= 0x100000000ULL) {
g_log->info("[acpi] MCFG base above 4G — requires MMIO mapping");
}
}
}
/* ---------------------------------------------------------------
* HPET (High Precision Event Timer) 解析
*
* 真机需要 HPET 替代 PIT 作为高精度定时器源。
* QEMU 默认也提供 HPET, 但当前内核使用 PIT 即可。
* HPET 信息留作后续高精度定时器实现的参考。
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
u8 hardware_rev_id;
u8 comparator_count : 5;
u8 counter_size : 1; /* 0=32-bit, 1=64-bit */
u8 reserved0 : 1;
u8 legacy_replacement : 1; /* 1=LegacyReplacement IRQ routing available */
u16 pci_vendor_id;
acpi_generic_address address; /* HPET 寄存器基址 */
u8 hpet_number;
u16 minimum_tick;
u8 page_protection;
} hpet_entry_data;
/* HPET 表不含子条目, 只有 1 个 data block */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 event_timer_block_id; /* 与 hpet_entry_data 的前 4 字节相同 */
acpi_generic_address base_address;
u8 hpet_number;
u16 minimum_tick;
u8 page_protection;
} hpet_table;
static void acpi_parse_hpet(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(hpet_table)) return;
const hpet_table *hpet = (const hpet_table *)hdr;
u32 block_id = hpet->event_timer_block_id;
u8 comp_count = (block_id >> 8) & 0x1f;
u8 count_size = (block_id >> 13) & 1;
u8 legacy = (block_id >> 15) & 1;
u8 rev_id = block_id & 0xff;
u16 vendor_id = (block_id >> 16) & 0xffff;
g_log->info("[acpi] HPET info:");
log_hex("[acpi] rev_id=", rev_id);
log_hex("[acpi] comparators=", comp_count);
log_hex("[acpi] counter_size=", count_size ? 64 : 32);
log_hex("[acpi] legacy_irq=", legacy);
log_hex("[acpi] vendor_id=", vendor_id);
log_hex("[acpi] min_tick=", hpet->minimum_tick);
/* HPET 地址空间类型 */
if (hpet->base_address.space_id == 0) {
/* System Memory — MMIO 访问 */
u64 addr = hpet->base_address.address;
log_hex("[acpi] HPET MMIO addr=", addr);
if (addr >= 0x100000000ULL) {
g_log->info("[acpi] HPET base above 4G — requires MMIO mapping");
}
} else if (hpet->base_address.space_id == 1) {
/* System I/O — 端口 I/O */
log_hex("[acpi] HPET I/O port=", hpet->base_address.address);
}
}
/* ---------------------------------------------------------------
* MADT (APIC) 解析 — 为 apic 驱动提供数据
* --------------------------------------------------------------- */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 lapic_addr;
u32 flags;
} acpi_madt;
static void acpi_parse_madt(const acpi_sdt_header *hdr) {
if (!hdr || hdr->length < sizeof(acpi_madt)) return;
const acpi_madt *madt = (const acpi_madt *)hdr;
g_log->info("[acpi] MADT detail:");
log_hex("[acpi] LAPIC addr=", madt->lapic_addr);
log_hex("[acpi] flags=", madt->flags);
if (madt->flags & 1) {
g_log->info("[acpi] PCAT_COMPAT=1 (8259 PIC present)");
}
/* 枚举 MADT 子条目做汇总统计 */
const u8 *base = (const u8 *)madt;
u32 off = sizeof(acpi_madt);
u32 lapic_count = 0, ioapic_count = 0, iso_count = 0, nmi_count = 0;
while (off + 2 <= madt->hdr.length) {
const u8 *entry = base + off;
u8 type = entry[0];
u8 len = entry[1];
if (len < 2 || off + len > madt->hdr.length) break;
if (type == 0) lapic_count++;
else if (type == 1) ioapic_count++;
else if (type == 2) iso_count++;
else if (type == 3 || type == 4) nmi_count++;
off += len;
}
log_hex("[acpi] LAPIC entries=", lapic_count);
log_hex("[acpi] IOAPIC entries=", ioapic_count);
log_hex("[acpi] ISO entries=", iso_count);
log_hex("[acpi] NMI entries=", nmi_count);
}
/* ---------------------------------------------------------------
* Table enumeration
* --------------------------------------------------------------- */
@@ -241,6 +412,15 @@ static void acpi_enumerate_rsdt(const acpi_sdt_header *rsdt, u32 entry_size) {
g_log->info(sig);
log_hex("[acpi] addr=", addr);
log_hex("[acpi] len =", (u64)hdr->length);
/* 对关键表做详细解析 */
if (sig[0]=='A' && sig[1]=='P' && sig[2]=='I' && sig[3]=='C') {
acpi_parse_madt(hdr);
} else if (sig[0]=='M' && sig[1]=='C' && sig[2]=='F' && sig[3]=='G') {
acpi_parse_mcfg(hdr);
} else if (sig[0]=='H' && sig[1]=='P' && sig[2]=='E' && sig[3]=='T') {
acpi_parse_hpet(hdr);
}
}
}
+116 -21
View File
@@ -5,6 +5,13 @@
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(ahci_port_found);
DKM_STAT_DECL(ahci_identify_ok);
DKM_STAT_DECL(ahci_block_reads);
DKM_STAT_DECL(ahci_block_writes);
DKM_TS_DECL(ts_abar);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
@@ -101,6 +108,8 @@ static u32 g_ready_port = 0xffffffffu;
static ahci_cmd_header *g_cmd_header;
static struct dkm_dma_buffer g_cmd_table;
static struct dkm_dma_buffer g_data;
/* IDENTIFY 读出的用户可寻址扇区数;LBA48 优先,回退 LBA28 */
static u64 g_sector_count;
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
@@ -130,7 +139,8 @@ static void ahci_zero(void *ptr, u32 len) {
}
static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u8 bus = 0; bus < 8; bus++) {
/* PCI 规范允许 256 条 bus (0-255),实机 AHCI 可能位于 bus > 7 */
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
@@ -298,13 +308,16 @@ static int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_
g_abar[(base + PxIS) / 4] = 0xffffffffu;
g_abar[HBA_IS / 4] = (1u << port);
g_abar[(base + PxCI) / 4] = 1;
for (u32 i = 0; i < 5000000; i++) {
/* TSC deadline 超时: 5 秒,实机机械 HDD 首次寻道需数百 ms */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
u8 *dst = (u8 *)buffer;
u8 *src = (u8 *)data->virt;
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
return 0;
}
__asm__ volatile("pause");
}
g_log->warn("[ahci] READ timeout");
log_hex("[ahci] READ lba=", lba);
@@ -330,6 +343,10 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
for (u32 j = 0; j < bytes; j++) dst[j] = src[j];
}
/* BUG-P0-2: 写入前必须 flush CPU cache,否则 DMA 控制器从物理内存
* 读到陈旧数据(CPU write-back cache 尚未写回)。实机必现。 */
__asm__ volatile("wbinvd" ::: "memory");
hdr[0].flags = 5 | (1u << 6); /* CFL=5, W=1 (write) */
hdr[0].prdtl = 1;
hdr[0].prdbc = 0;
@@ -341,29 +358,39 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31);
ahci_fill_write_fis(tbl->cfis, lba, count);
/* set PxCMD.W to indicate write direction */
u32 cmd = g_abar[(base + PxCMD) / 4];
cmd |= AHCI_CMD_W;
g_abar[(base + PxCMD) / 4] = cmd;
/* P0 修复: 移除 PxCMD.W 写入。AHCI 规范中 PxCMD.W 是只读位,
* 由硬件根据命令头 flags.W 自动设置。软件写入此位违反规范,
* 可能在某些实机 AHCI 控制器上导致未定义行为。 */
/* (原代码: cmd |= AHCI_CMD_W; g_abar[...] = cmd; — 已删除) */
g_abar[(base + PxIS) / 4] = 0xffffffffu;
g_abar[HBA_IS / 4] = (1u << port);
g_abar[(base + PxCI) / 4] = 1;
for (u32 i = 0; i < 5000000; i++) {
/* P0 修复: 写入超时改为 TSC deadline,与读取路径一致。
* 固定循环在实机 HDD 上会因 CPU 频率差异导致提前超时或死循环。 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
/* clear PxCMD.W after write completes */
cmd = g_abar[(base + PxCMD) / 4];
cmd &= ~AHCI_CMD_W;
g_abar[(base + PxCMD) / 4] = cmd;
/* BUG-P1-1 fix: CI 清除后检查 TFD 错误位。
* TFD 低字节是 STS,正常值 0x50 (DRDY|DRQ) 非零但不是错误。
* 只有 STS bit0 (ERR) 或 ERR 寄存器 (bits15:8) 非零才是真正错误。
* BUG-20260730-P14: 原 `tfd & 0xFF` 误判 STS=0x50 为错误,导致写入
* 实际成功却被报告为失败,FAT32 写路径提前返回错误码。 */
u32 tfd = g_abar[(base + PxTFD) / 4];
u32 is = g_abar[(base + PxIS) / 4];
u8 sts = (u8)(tfd & 0xFF);
u8 err_reg = (u8)((tfd >> 8) & 0xFF);
if ((sts & 0x01) || err_reg) {
g_log->warn("[ahci] write error: TFD ERR");
log_hex("[ahci] TFD=", tfd);
log_hex("[ahci] PxIS=", is);
return -4;
}
return 0;
}
__asm__ volatile("pause");
}
/* timeout: clear PxCMD.W */
cmd = g_abar[(base + PxCMD) / 4];
cmd &= ~AHCI_CMD_W;
g_abar[(base + PxCMD) / 4] = cmd;
g_log->warn("[ahci] WRITE timeout");
log_hex("[ahci] WRITE lba=", lba);
log_hex("[ahci] WRITE count=", count);
@@ -376,6 +403,7 @@ static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm
static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
DKM_STAT_INC(ahci_block_reads);
log_hex("[ahci] block_read lba=", lba);
log_hex("[ahci] block_read count=", count);
while (count > 0) {
@@ -392,6 +420,7 @@ static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
static int ahci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
DKM_STAT_INC(ahci_block_writes);
/* ensure port is in ST+FRE state */
u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE;
u32 cmd = g_abar[(base + PxCMD) / 4];
@@ -445,8 +474,27 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
struct dkm_dma_buffer data;
if (dma->alloc_pages(1, 1024, 0x100000000ULL, &clb) != 0) return -2;
if (dma->alloc_pages(1, 256, 0x100000000ULL, &fis) != 0) return -3;
if (dma->alloc_pages(1, 128, 0x100000000ULL, &table) != 0) return -4;
if (dma->alloc_pages(1, 512, 0x100000000ULL, &data) != 0) return -5;
/* BUG-P0-1: sizeof(ahci_cmd_table)=144, 128B 缓冲区越界 16 字节。
* 改为 256B 满足对齐和大小要求,防止 ahci_zero 越界写。 */
if (dma->alloc_pages(1, 256, 0x100000000ULL, &table) != 0) return -4;
/* BUG-20260730-022: DMA data buffer must hold at least 8 sectors (4096B)
* for ahci_block_write chunked DMA. 512B caused 3584B out-of-bounds
* overwrite of adjacent kernel data on real hardware. */
if (dma->alloc_pages(1, 8192, 0x100000000ULL, &data) != 0) return -5;
/* P0 验证: DMA 缓冲区物理地址必须在 4GB 以下。
* AHCI 控制器 8086:2922 只支持 32 位 DMA,
* PRDT 地址超过 4GB 会导致 DMA 写入截断到错误物理地址。
* dma->alloc_pages 的上限参数 0x100000000ULL 已保证此约束,
* 但添加显式校验以防御 alloc_pages 实现变更。 */
if (clb.phys >= 0x100000000ULL || fis.phys >= 0x100000000ULL ||
table.phys >= 0x100000000ULL || data.phys >= 0x100000000ULL) {
g_log->warn("[ahci] DMA buffer above 4GB — 32-bit DMA will fail on this controller!");
log_hex("[ahci] clb.phys=", clb.phys);
log_hex("[ahci] fis.phys=", fis.phys);
log_hex("[ahci] table.phys=", table.phys);
log_hex("[ahci] data.phys=", data.phys);
}
if (ahci_stop_port(port) != 0) {
g_log->warn("[ahci] port stop timeout");
@@ -476,7 +524,10 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
ahci_start_port(port);
g_abar[(base + PxCI) / 4] = 1;
for (u32 i = 0; i < 5000000; i++) {
/* BUG-P0-3: 固定 500 万次循环在 3GHz CPU 上仅 ~1-2ms,
* 不够 HDD 寻道。改为 TSC deadline 5s 超时,与 read/write 路径一致。 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 5000;
while (dkm_rdtsc() < deadline) {
if ((g_abar[(base + PxCI) / 4] & 1u) == 0) {
log_hex("[ahci] IDENTIFY port=", port);
log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]);
@@ -485,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 word49=", id[49]);
log_hex("[ahci] IDENTIFY word83=", id[83]);
/* 容量必须在 ahci_read_lba0 复用 data 缓冲区前提取,
* 否则 IDENTIFY 数据被 LBA0 内容覆盖后容量丢失。 */
if ((id[83] & 0x0400u) && (id[103] | id[102] | id[101] | id[100])) {
/* LBA48: word100-103 小端 u16 序列拼接 */
g_sector_count = (u64)id[100] | ((u64)id[101] << 16)
| ((u64)id[102] << 32) | ((u64)id[103] << 48);
} else {
/* LBA28: word60-61 */
g_sector_count = (u64)id[60] | ((u64)id[61] << 16);
}
log_hex("[ahci] IDENTIFY sector_count=", g_sector_count);
ahci_read_lba0(port, hdr, &table, &data);
g_ready_port = port;
g_cmd_header = hdr;
@@ -492,6 +554,7 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
g_data = data;
return 1;
}
__asm__ volatile("pause");
}
g_log->warn("[ahci] IDENTIFY timeout");
@@ -510,6 +573,7 @@ int driver_init(const struct dkm_kernel_api *api,
if (!api || !api->log) return -1;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[ahci] init begin");
@@ -536,7 +600,17 @@ int driver_init(const struct dkm_kernel_api *api,
pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[ahci] PCI command=", command);
u64 abar_phys = (u64)(pci_read(bus, dev, func, PCI_BAR5) & 0xFFFFFFF0u);
/* 读取 ABAR: BAR5 可能是 64-bit MMIO BAR,高位在 BAR4 */
u32 bar5_lo = pci_read(bus, dev, func, PCI_BAR5);
u64 abar_phys;
if ((bar5_lo & 0x7) == 0x4) {
/* 64-bit MMIO BAR: 高 32 位在 BAR4 */
u32 bar5_hi = pci_read(bus, dev, func, PCI_BAR4);
abar_phys = ((u64)bar5_hi << 32) | (bar5_lo & 0xFFFFFFF0u);
g_log->info("[ahci] ABAR is 64-bit MMIO BAR");
} else {
abar_phys = bar5_lo & 0xFFFFFFF0u;
}
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[ahci] ABAR phys=", abar_phys);
log_hex("[ahci] PCI IRQ line=", irq_line);
@@ -548,6 +622,7 @@ int driver_init(const struct dkm_kernel_api *api,
}
g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys);
DKM_TS_BEGIN(ts_abar);
log_hex("[ahci] HHDM offset=", api->hhdm_offset);
u32 cap = g_abar[HBA_CAP / 4];
@@ -565,13 +640,32 @@ int driver_init(const struct dkm_kernel_api *api,
log_hex("[ahci] VS=", vs);
log_hex("[ahci] CAP2=", cap2);
log_hex("[ahci] BOHC=", bohc);
/* AHCI 1.3+ BOHC handoff: 如果 BIOS 仍拥有控制器, 请求所有权 */
if (bohc & 0x01) {
g_log->info("[ahci] BIOS owns HBA, requesting handoff");
g_abar[HBA_BOHC / 4] = bohc | 0x02; /* set OOS */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
if (!(g_abar[HBA_BOHC / 4] & 0x01)) break;
__asm__ volatile("pause");
}
if (g_abar[HBA_BOHC / 4] & 0x01) {
g_log->warn("[ahci] BIOS handoff timeout, proceeding anyway");
} else {
g_log->info("[ahci] BIOS handoff complete");
}
}
log_hex("[ahci] ports count=", (cap & 0x1f) + 1);
log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1);
DKM_TS_END(ts_abar, g_log->info, "ahci_abar_mmio");
u32 implemented = 0;
for (u32 port = 0; port < 32; port++) {
if (pi & (1u << port)) {
implemented++;
DKM_STAT_INC(ahci_port_found);
ahci_log_port(port);
}
}
@@ -584,6 +678,7 @@ int driver_init(const struct dkm_kernel_api *api,
int ident = ahci_identify_port(port, api->dma);
if (ident > 0) {
identified++;
DKM_STAT_INC(ahci_identify_ok);
break;
}
}
@@ -596,7 +691,7 @@ int driver_init(const struct dkm_kernel_api *api,
struct dkm_block_device_desc desc;
desc.name = "ahci0";
desc.sector_size = 512;
desc.sector_count = 0;
desc.sector_count = g_sector_count;
desc.ctx = 0;
desc.read = ahci_block_read;
desc.write = ahci_block_write;
+1215 -11
View File
@@ -1,7 +1,52 @@
/* DKM APIC Driver — APIC discovery with PIC fallback
/* DKM APIC Driver — APIC discovery + IOAPIC init + LAPIC enable + 路由接管
* Stage 0, required, depends on "timer", provides "irq" and "apic".
* Current kernel IRQ backend is still PIC; this driver only discovers APIC
* topology and keeps PIC routing active.
*
* 真机路径:
* 1. LAPIC 使能 + Spurious Interrupt Vector 注册
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
* (兼容 PIC 布局), 逐条目 mask
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
* 4. SIPI 多核启动框架 (预留: 调用 dkm_sipi_wake_ap 即 DKM-E03 panic,
* 见 CODE/UTSM/dkm/kernel_api.c 骨架)
*
* B7 路由接管 (apic_route):
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
* 从 8259 PIC 切换到 IOAPIC→LAPIC 路径:
* a. 经 kernel_api.register_apic_eoi 向 UTSM 注册 LAPIC EOI 钩子
* (B7 阶段2 接口; idt_handler 两个 IRQ 分支在分发末尾调用之)
* b. 注册 PIT tick 计数 handler 于 vector 0x20 — 即 idt_handler 的
* PIC 分支 (0x20-0x2F), 与键盘 0x21/鼠标 0x2C/网卡 0x2B 同一代码
* 路径; handler 返回 0 由钩子完成 LAPIC EOI, 重复投递 >=2 即证明
* "legacy vector 分支 + 钩子 EOI" 全链路可持续 (阶段1 用 0xE0 自
* EOI 只证明了 APIC 分支)
* c. 按 ISO 表解析 PIT GSI (QEMU q35: IRQ0→GSI2), 解屏蔽该条目
* d. LAPIC TPR=0 全放行, PIC IMR 全 1 屏蔽 (QEMU 无功能 IMCR 寄存器,
* PIC 全屏蔽即等效切换; 8259 与 IOAPIC 同时监听 IRQ 线, 全屏蔽避免双投递)
* e. PIT ch0 mode3 100Hz 产生真实硬件中断, 500ms 窗口内计数 >=2 次
* 即证明 IOAPIC→LAPIC→IDT→handler→钩子EOI 全链路
* f. 验证失败完整降级: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 handler
* 与 EOI 钩子, 系统行为与切换前完全一致
*
* B7 阶段2 (apic_route_legacy):
* EOI 钩子落地后, legacy IRQ1(键盘 0x21)/IRQ12(鼠标 0x2C)/IRQ11(e1000
* 0x2B) 重定向项可持续投递, 不再是 one-shot。ISA 线按 MADT ISO 极性/触发
* (缺省 edge/active-high); PCI 线 (IRQ11) 无 ISO 覆盖时强制 level/
* active-low (PCI INTx 规范; QEMU 内部 pci irq 电平语义与之匹配,
* Linux/Windows 同配置)。e1000 handler 读 ICR 清源后电平 deassert,
* 由钩子 EOI 完成 LAPIC 侧清扫。
*
* apic_diag=1 诊断:
* - HPET 绝对时基对拍: 解析 ACPI HPET 表 (QEMU q35 有, 100MHz 主计数器),
* 用主计数器实测 tsc_calibrate 的 10ms 窗口与 route 验证的 500ms 窗口,
* 定论 "tick 2x" 根因 (TSC 校准测半 vs PIT mode3 双触发)
* - PS/2 鼠标流: 初始化 AUX 端口 + 注册 IRQ12 诊断 handler, 配合
* QEMU monitor mouse_move 验证 IRQ12 持续投递 (计数 + 观察窗)
* diag 只增观测不改变量路由语义, 默认 0 关闭。
*
* QEMU 兼容:
* - QEMU 默认只提供 1 个 IOAPIC, 24 条 GSI
* - 实机可能有多个 IOAPIC, GSI 范围更大
* - 实机必须处理 MADT ISO (IRQ0→GSI2 等)
*/
#include <stdint.h>
@@ -47,6 +92,15 @@ struct dkm_kernel_api {
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
const void *block;
const void *mmio;
void *(*mm_map_mmio)(u64 phys, u64 size);
void (*mm_unmap_mmio)(void *virt, u64 size);
/* B7 阶段2 尾部追加 (旧内核无此字段 -> 判空使用) */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3 尾部追加 (本驱动不使用, 保持布局与 UTSM 一致) */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
};
struct dkm_driver_handle;
@@ -81,7 +135,7 @@ const struct dkm_driver_desc driver_desc = {
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "apic",
.version = "0.1.0",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 3, /* DKM_CLASS_INTERRUPT */
.stage = 0,
@@ -135,15 +189,102 @@ typedef struct __attribute__((packed)) {
u32 flags;
} acpi_madt;
/* ACPI HPET 表 (signature "HPET") — apic_diag 绝对时基对拍用 */
typedef struct __attribute__((packed)) {
acpi_sdt_header hdr;
u32 event_timer_block_id;
/* ACPI Generic Address Structure (GAS) */
u8 addr_space_id; /* 0 = system memory */
u8 register_bit_width;
u8 register_bit_offset;
u8 access_size;
u64 base_addr; /* HPET MMIO 物理基址 (QEMU: 0xFED00000) */
u8 hpet_number;
u16 min_clock_tick;
u8 page_protection;
} acpi_hpet;
/* Limine boot module 最小类型 (与 bootfs.drv / UTSM limine.h 布局一致) */
struct limine_file_min {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response_min {
u64 revision;
u64 module_count;
struct limine_file_min **modules;
};
/* ---- IOAPIC 信息存储(真机多 IOAPIC 支持) ---- */
#define MAX_IOAPIC 4
#define MAX_ISO 16
#define MAX_LAPIC 256
struct ioapic_info {
u8 id;
u32 gsi_base;
u32 gsi_count; /* 从 IOAPIC VER 寄存器读取: max_redir_entry + 1 */
u64 mmio_phys; /* IOAPIC MMIO 物理基址 */
volatile u32 *mmio; /* HHDM/MMIO 窗口映射后的虚拟地址, 0=未映射 */
};
struct iso_info {
u8 bus_source; /* ISA bus source IRQ (0-15) */
u32 gsi; /* 全局系统中断号 */
u16 flags; /* 极性/触发模式标志 */
};
struct lapic_entry {
u8 apic_id;
u32 flags; /* MADT LAPIC flags: bit0=enabled */
};
struct madt_info {
u64 lapic_phys;
u32 flags;
u32 lapic_count;
u32 ioapic_count;
u32 iso_count;
struct ioapic_info ioapics[MAX_IOAPIC];
struct iso_info isos[MAX_ISO];
struct lapic_entry lapics[MAX_LAPIC];
};
/* ---- B7 路由接管配置 (FUCK [drivers]) ---- */
struct apic_config {
int lapic_enable; /* lapic_enable, 默认 1 */
int ioapic_init; /* ioapic_init, 默认 1 */
int apic_route; /* apic_route, 默认 0 (PIC 路由, 现状) */
int apic_route_legacy; /* apic_route_legacy, 默认 0 */
int apic_diag; /* apic_diag, 默认 0 (HPET 校准/投递计数/鼠标流诊断) */
};
static const struct dkm_log_api *g_log;
static struct apic_config g_cfg = { 1, 1, 0, 0, 0 };
static volatile u32 *g_lapic; /* LAPIC MMIO 映射基址, 0=未映射 */
static u32 g_lapic_id; /* BSP Local APIC ID (IOAPIC 目的 ID) */
static volatile u32 g_tick_count; /* PIT tick 计数 (路由验证) */
static int g_route_active; /* 1=IOAPIC 路由已接管 */
static __inline__ void apic_mb(void) {
__asm__ volatile("" ::: "memory");
}
static int memeq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
@@ -152,6 +293,24 @@ static int memeq(const char *a, const char *b, u32 n) {
return 1;
}
static u32 str_len(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 在 NUL 结尾字符串中查找子串 (cmdline 匹配用) */
static int str_contains(const char *hay, const char *needle) {
u32 nlen = str_len(needle);
if (!nlen) return 1;
for (u32 i = 0; hay[i]; i++) {
u32 j = 0;
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
if (j == nlen) return 1;
}
return 0;
}
static u8 checksum8(const u8 *data, u32 len) {
u8 sum = 0;
for (u32 i = 0; i < len; i++) sum += data[i];
@@ -172,6 +331,37 @@ static void log_hex(const char *prefix, u64 val) {
g_log->info(buf);
}
static void log_dec(const char *prefix, u64 val) {
char buf[24];
u32 pos = 0;
if (!val) {
buf[pos++] = '0';
} else {
char tmp[20];
u32 n = 0;
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
while (n) buf[pos++] = tmp[--n];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ---- I/O 端口 (PIC / PIT) ---- */
static __inline__ void outb(u16 port, u8 value) {
__asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u8 inb(u16 port) {
u8 value;
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static __inline__ void io_wait(void) {
outb(0x80, 0);
}
static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
__asm__ volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
@@ -185,6 +375,12 @@ static u64 rdmsr(u32 msr) {
return ((u64)hi << 32) | lo;
}
static void wrmsr(u32 msr, u64 val) {
u32 lo = (u32)(val & 0xffffffffu);
u32 hi = (u32)(val >> 32);
__asm__ volatile("wrmsr" :: "a"(lo), "d"(hi), "c"(msr));
}
static int cpu_has_lapic(void) {
u32 a, b, c, d;
cpuid(1, &a, &b, &c, &d);
@@ -194,6 +390,131 @@ static int cpu_has_lapic(void) {
return (d & (1u << 9)) != 0;
}
/* ---- FUCK 配置自解析 (DKM 侧, 不依赖 UTSM ini_parser) ---- *
* UTSM 通过 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
* 此处实现最小分区感知 INI 扫描: 只找 [drivers] 分区下的目标键。
* 缺失文件/缺失键一律回退默认值, 行为与无配置时一致。
*/
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
u64 i = start;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
u64 tok_start = i;
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
u64 tok_len = i - tok_start;
const char *tok = line + tok_start;
if (!tok_len) return defval;
if (tok_len == 1 && tok[0] == '1') return 1;
if (tok_len == 1 && tok[0] == '0') return 0;
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
/* 十进制整数 (允许前导 '-') */
int neg = 0;
u64 j = 0;
if (tok[0] == '-') { neg = 1; j = 1; }
int val = 0;
int any = 0;
for (; j < tok_len; j++) {
if (tok[j] < '0' || tok[j] > '9') return defval;
val = val * 10 + (tok[j] - '0');
any = 1;
}
if (!any) return defval;
return neg ? -val : val;
}
static int fuck_get_int(const char *text, u64 size,
const char *section, const char *key, int defval) {
u64 pos = 0;
int in_section = 0;
u32 slen = str_len(section);
u32 klen = str_len(key);
while (pos < size) {
u64 end = pos;
while (end < size && text[end] != '\n') end++;
u64 line_len = end - pos;
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
const char *line = text + pos;
u64 i = 0;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
if (i < line_len) {
char c = line[i];
if (c == '#' || c == ';') {
/* 注释行, 跳过 */
} else if (c == '[') {
in_section = 0;
if (line_len >= (u64)(i + 1 + slen + 1) &&
memeq(line + i + 1, section, slen) &&
line[i + 1 + slen] == ']') {
in_section = 1;
}
} else if (in_section) {
if (line_len >= (u64)(i + klen) &&
memeq(line + i, key, klen)) {
u64 j = i + klen;
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
if (j < line_len && line[j] == '=') {
return parse_scalar(line, j + 1, line_len, defval);
}
}
}
}
pos = end + 1;
}
return defval;
}
static void load_fuck_config(const struct dkm_kernel_api *api) {
/* 编译期默认: 与 FUCK 注释承诺一致 (无配置时行为不变) */
g_cfg.lapic_enable = 1;
g_cfg.ioapic_init = 1;
g_cfg.apic_route = 0;
g_cfg.apic_route_legacy = 0;
g_cfg.apic_diag = 0;
const struct limine_module_response_min *resp =
(const struct limine_module_response_min *)api->boot_modules_response;
if (!resp || !resp->modules) {
g_log->warn("[apic] no boot modules; FUCK defaults in effect");
return;
}
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file_min *f = resp->modules[i];
if (!f || !f->cmdline || !f->address || !f->size) continue;
if (!str_contains(f->cmdline, "fuck:config")) continue;
const char *text = (const char *)f->address;
u64 size = f->size;
g_log->info("[apic] FUCK config module found");
log_hex("[apic] FUCK size=", size);
g_cfg.lapic_enable = fuck_get_int(text, size, "drivers", "lapic_enable", 1);
g_cfg.ioapic_init = fuck_get_int(text, size, "drivers", "ioapic_init", 1);
g_cfg.apic_route = fuck_get_int(text, size, "drivers", "apic_route", 0);
g_cfg.apic_route_legacy = fuck_get_int(text, size, "drivers", "apic_route_legacy", 0);
g_cfg.apic_diag = fuck_get_int(text, size, "drivers", "apic_diag", 0);
log_dec("[apic] cfg lapic_enable=", (u64)g_cfg.lapic_enable);
log_dec("[apic] cfg ioapic_init=", (u64)g_cfg.ioapic_init);
log_dec("[apic] cfg apic_route=", (u64)g_cfg.apic_route);
log_dec("[apic] cfg apic_route_legacy=", (u64)g_cfg.apic_route_legacy);
log_dec("[apic] cfg apic_diag=", (u64)g_cfg.apic_diag);
return;
}
g_log->warn("[apic] FUCK module (fuck:config) not found; defaults in effect");
}
static const acpi_sdt_header *find_table_from_rsdt(const acpi_sdt_header *rsdt, const char sig[4], u32 entry_size) {
if (!rsdt || rsdt->length < sizeof(acpi_sdt_header)) return NULL;
if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL;
@@ -241,6 +562,27 @@ static const acpi_madt *find_madt(const void *rsdp_addr) {
return NULL;
}
/* 查找 HPET 表 (apic_diag 用; 与 find_madt 同构, 仅签名不同) */
static const acpi_hpet *find_hpet(const void *rsdp_addr) {
if (!rsdp_addr) return NULL;
const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)rsdp_addr;
if (!memeq(rsdp->signature, "RSD PTR ", 8)) return NULL;
if (checksum8((const u8 *)rsdp, 20) != 0) return NULL;
if (rsdp->revision >= 2 && rsdp->length >= sizeof(acpi_rsdp_v2) && rsdp->xsdt_addr) {
if (checksum8((const u8 *)rsdp, rsdp->length) == 0) {
const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr;
const acpi_sdt_header *hpet = find_table_from_rsdt(xsdt, "HPET", 8);
if (hpet) return (const acpi_hpet *)hpet;
}
}
if (rsdp->rsdt_addr) {
const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr;
const acpi_sdt_header *hpet = find_table_from_rsdt(rsdt, "HPET", 4);
if (hpet) return (const acpi_hpet *)hpet;
}
return NULL;
}
static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
const u8 *base = (const u8 *)madt;
u32 off = (u32)sizeof(acpi_madt);
@@ -255,19 +597,36 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
}
if (type == 0 && len >= 8) {
/* LAPIC entry */
u8 apic_id = entry[3];
u32 flags = *(const u32 *)(const void *)(entry + 4);
if (flags & 1u) info->lapic_count++;
log_hex("[apic] CPU LAPIC id=", apic_id);
if (flags & 1u) {
info->lapic_count++;
if (info->lapic_count <= MAX_LAPIC) {
info->lapics[info->lapic_count - 1].apic_id = apic_id;
info->lapics[info->lapic_count - 1].flags = flags;
}
}
} else if (type == 1 && len >= 12) {
/* IOAPIC entry */
u8 ioapic_id = entry[2];
u32 addr = *(const u32 *)(const void *)(entry + 4);
u32 gsi = *(const u32 *)(const void *)(entry + 8);
info->ioapic_count++;
log_hex("[apic] IOAPIC id=", ioapic_id);
log_hex("[apic] IOAPIC addr=", addr);
log_hex("[apic] IOAPIC gsi_base=", gsi);
if (info->ioapic_count < MAX_IOAPIC) {
struct ioapic_info *io = &info->ioapics[info->ioapic_count];
io->id = ioapic_id;
io->gsi_base = gsi;
io->mmio_phys = (u64)addr;
io->mmio = (volatile u32 *)(uintptr_t)(u64)addr; /* HHDM 直映射, 后续映射修正 */
io->gsi_count = 0;
info->ioapic_count++;
}
} else if (type == 2 && len >= 10) {
/* ISO (Interrupt Source Override) entry */
u8 source = entry[3];
u32 gsi = *(const u32 *)(const void *)(entry + 4);
u16 flags = *(const u16 *)(const void *)(entry + 8);
@@ -275,6 +634,12 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
log_hex("[apic] ISO source=", source);
log_hex("[apic] ISO gsi=", gsi);
log_hex("[apic] ISO flags=", flags);
if (info->iso_count <= MAX_ISO) {
struct iso_info *iso = &info->isos[info->iso_count - 1];
iso->bus_source = source;
iso->gsi = gsi;
iso->flags = flags;
}
} else if (type == 5 && len >= 12) {
u64 override = *(const u64 *)(const void *)(entry + 4);
info->lapic_phys = override;
@@ -285,6 +650,797 @@ static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) {
}
}
/* ---- IOAPIC MMIO 访问 ---- *
* IOAPIC 寄存器通过 MMIO 访问, 地址空间为 IOREGSEL(0x00) + IOWIN(0x10).
* 真机 IOAPIC 地址通常在 0xFEC00000, HHDM 通常覆盖此低 4G 地址。
* 写入 IOREGSEL 选择寄存器号, 读/写 IOWIN 访问数据。
*/
#define IOAPIC_IOREGSEL 0x00
#define IOAPIC_IOWIN 0x10
static u32 ioapic_read(volatile u32 *base, u8 reg) {
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
apic_mb(); /* 寄存器选择必须先于数据读取 */
return base[IOAPIC_IOWIN / 4];
}
static void ioapic_write(volatile u32 *base, u8 reg, u32 val) {
base[IOAPIC_IOREGSEL / 4] = (u32)reg;
apic_mb(); /* 寄存器选择必须先于数据写入 */
base[IOAPIC_IOWIN / 4] = val;
}
/* IOAPIC 重定向表项 (64-bit, 分两次 32-bit 读写, 82093AA 规范布局)
* bits 0-7: interrupt vector (0x20-0xFF)
* bits 8-10: delivery mode: 0=fixed, 1=lowest, 2=SMI, 4=NMI, 5=INIT, 7=ExtINT
* bit 11: destination mode: 0=physical, 1=logical
* bit 12: delivery status (RO)
* bit 13: polarity: 0=active high, 1=active low
* bit 14: remote IRR (RO, level-triggered only)
* bit 15: trigger mode: 0=edge, 1=level
* bit 16: mask: 0=enabled, 1=masked
* bits 17-55: reserved
* bits 56-63: destination APIC ID (physical mode) or MDA (logical mode)
* 注: 历史版本曾将 polarity/trigger/mask 错置于 bit 11/13/14,
* 导致 mask 写入只读 remote IRR 位而从未生效 —— 勿回退。
*/
static u64 ioapic_read_redir(volatile u32 *base, u8 entry) {
u32 lo = ioapic_read(base, (u8)(0x10 + entry * 2));
u32 hi = ioapic_read(base, (u8)(0x10 + entry * 2 + 1));
return ((u64)hi << 32) | (u64)lo;
}
static void ioapic_write_redir(volatile u32 *base, u8 entry, u64 val) {
ioapic_write(base, (u8)(0x10 + entry * 2), (u32)(val & 0xffffffffu));
ioapic_write(base, (u8)(0x10 + entry * 2 + 1), (u32)(val >> 32));
}
/* ---- B7: GSI 路由辅助 ---- */
/* 合成重定向表项: vector + ISO 极性/触发 + 物理目的 APIC ID + mask 位 */
static u64 redir_compose(u32 vector, u16 iso_flags, u32 dest_apic_id, int masked) {
u64 entry = (u64)(vector & 0xFFu);
/* delivery mode = fixed (0), destination mode = physical (0) */
/* 极性: bit 13. ISA 默认 active-high (0).
* ISO flags bit 0-1: 0=conforms, 1=active-high, 2=reserved, 3=active-low */
if ((iso_flags & 0x03u) == 0x03u) entry |= (1ULL << 13);
/* 触发: bit 15. ISA 默认 edge (0).
* ISO flags bit 2-3: 0=conforms, 1=edge, 2=reserved, 3=level */
if ((iso_flags & 0x0Cu) == 0x0Cu) entry |= (1ULL << 15);
entry |= ((u64)dest_apic_id << 56);
if (masked) entry |= (1ULL << 16);
return entry;
}
/* 找覆盖指定 GSI 的 IOAPIC (多 IOAPIC 按 GSI 范围划分) */
static struct ioapic_info *ioapic_for_gsi(struct madt_info *info, u32 gsi) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (gsi >= io->gsi_base && gsi < io->gsi_base + io->gsi_count) return io;
}
return NULL;
}
/* ISA IRQ → GSI 解析: 优先 MADT ISO override, 缺省恒等 (QEMU ISA 惯例) */
static u32 isa_irq_to_gsi(u8 isa_irq, u16 *flags_out,
const struct iso_info *isos, u32 iso_count) {
for (u32 i = 0; i < iso_count; i++) {
if (isos[i].bus_source == isa_irq) {
*flags_out = isos[i].flags;
return isos[i].gsi;
}
}
*flags_out = 0;
return (u32)isa_irq;
}
/* 编程单个 GSI 的重定向项 (unmask=1 放开投递, 0 保持屏蔽) */
static int ioapic_program_gsi(struct madt_info *info, u32 gsi,
u32 vector, u16 iso_flags, int unmask) {
struct ioapic_info *io = ioapic_for_gsi(info, gsi);
if (!io || !io->mmio) return -1;
u8 pin = (u8)(gsi - io->gsi_base);
ioapic_write_redir(io->mmio, pin,
redir_compose(vector, iso_flags, g_lapic_id, !unmask));
return 0;
}
/* 全 IOAPIC 全条目屏蔽 (降级路径) — 保留 vector 信息, 仅置 mask 位 */
static void ioapic_mask_all(struct madt_info *info) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (!io->mmio) continue;
for (u32 pin = 0; pin < io->gsi_count && pin < 64; pin++) {
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
entry |= (1ULL << 16);
ioapic_write_redir(io->mmio, (u8)pin, entry);
}
}
}
/* 重定向表回读 dump (验证证据) */
static void ioapic_dump_table(struct madt_info *info) {
for (u32 i = 0; i < info->ioapic_count; i++) {
struct ioapic_info *io = &info->ioapics[i];
if (!io->mmio) continue;
g_log->info("[apic] IOREDTBL readback:");
for (u32 pin = 0; pin < io->gsi_count && pin < 24; pin++) {
u64 entry = ioapic_read_redir(io->mmio, (u8)pin);
u32 vec = (u32)(entry & 0xFFu);
u32 masked = (u32)((entry >> 16) & 1u);
log_dec("[apic] pin=", pin);
log_hex("[apic] vec=", vec);
log_dec("[apic] mask=", masked);
}
}
}
/* ---- IOAPIC 初始化 ---- *
* 逐条目编程重定向表: ISA IRQ→vector 映射 + mask all。
* 真机关键: 处理 MADT ISO (IRQ0→GSI2 是最常见的 override)。
* PIC 兼容模式: vector = 0x20 + IRQ (与 PIC remap 布局一致)。
*
* **QEMU 无法测试但真机必需的代码路径**:
* 1. ISO 处理: 真机几乎总有 IRQ0→GSI2 override, QEMU 默认有 1 条
* 2. 极性标志: 真机 ISA IRQ0 (timer) 必须设 active-high,
* 但 ISO 可能指定 active-low (PCIe SCI 等)
* 3. 多 IOAPIC: 真机可能有 2+ IOAPIC, 每个 GSI 范围不同
*/
static void ioapic_init_one(struct ioapic_info *io, u64 hhdm_offset,
const struct iso_info *isos, u32 iso_count,
void *(*mm_map_mmio_fn)(u64, u64)) {
/* 映射 IOAPIC MMIO — 真机地址通常在 0xFECxxxxx, HHDM 通常覆盖,
* 但使用 mm_map_mmio 更安全 (PCD|PWT 属性) */
if (mm_map_mmio_fn) {
void *mapped = mm_map_mmio_fn(io->mmio_phys, 0x1000);
if (mapped) {
io->mmio = (volatile u32 *)mapped;
g_log->info("[apic] IOAPIC mapped via mm_map_mmio");
} else {
g_log->warn("[apic] mm_map_mmio failed for IOAPIC; trying HHDM");
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
}
} else {
io->mmio = (volatile u32 *)(uintptr_t)(hhdm_offset + io->mmio_phys);
}
/* 读取 IOAPIC 版本寄存器获取最大重定向条目数 */
u32 ver = ioapic_read(io->mmio, 0x01);
u32 max_redir = ((ver >> 16) & 0xff) + 1;
io->gsi_count = max_redir;
log_hex("[apic] IOAPIC VER=", ver);
log_hex("[apic] IOAPIC max_redir=", max_redir);
/* 读取 IOAPIC ID 验证 */
u32 id_reg = ioapic_read(io->mmio, 0x00);
log_hex("[apic] IOAPIC ID reg=", id_reg);
/* 构建默认 ISA IRQ→GSI→vector 映射表 (identity map + ISO override)
* 默认: GSI = ISA_IRQ (0-15), vector = 0x20 + ISA_IRQ
* ISO override: 修改特定 IRQ 的 GSI 和极性/触发模式 */
u32 gsi_to_vector[256]; /* GSI → vector 映射 */
u32 gsi_flags[256]; /* GSI → polar/trigger flags from ISO */
for (u32 i = 0; i < 256; i++) {
gsi_to_vector[i] = 0;
gsi_flags[i] = 0;
}
/* 先建立 identity 映射: GSI 0-15 → vector 0x20-0x2F */
for (u32 gsi = 0; gsi < 16; gsi++) {
gsi_to_vector[gsi] = 0x20 + gsi;
}
/* 应用 ISO override */
for (u32 i = 0; i < iso_count; i++) {
const struct iso_info *iso = &isos[i];
u8 src = iso->bus_source;
u32 gsi = iso->gsi;
if (gsi < 256) {
gsi_to_vector[gsi] = 0x20 + src; /* vector 仍基于 ISA IRQ 编号 */
gsi_flags[gsi] = iso->flags;
g_log->info("[apic] ISO: ISA IRQ -> GSI override");
log_hex("[apic] ISA IRQ=", src);
log_hex("[apic] GSI=", gsi);
log_hex("[apic] vector=", 0x20 + src);
}
}
/* 编程重定向表: 全部 mask, 编入正确的 vector/极性/触发模式/目标 APIC */
for (u32 pin = 0; pin < max_redir && pin < 256; pin++) {
u32 gsi = io->gsi_base + pin;
u64 entry = 0;
if (gsi < 256 && gsi_to_vector[gsi] != 0) {
u32 vec = gsi_to_vector[gsi];
u16 flags = gsi_flags[gsi];
/* vector + ISO 极性/触发 + 真实 BSP LAPIC ID, 保持 mask */
entry = redir_compose(vec, flags, g_lapic_id, 1);
} else {
entry = (1ULL << 16); /* 无映射条目: 纯 mask (bit16, vector=0 无害) */
}
ioapic_write_redir(io->mmio, (u8)pin, entry);
}
g_log->info("[apic] IOAPIC redirect table programmed (all masked)");
}
/* ---- LAPIC 使能 ---- *
* 真机必须:
* 1. 写 IA32_APIC_BASE MSR 启用 bit 11 (global enable)
* 2. 写 LAPIC SVR 寄存器设置 Spurious Interrupt Vector (建议 0xFF)
* 3. 设置 SVR bit 8 (APIC software enable)
*
* **QEMU 无法测试但真机必需**: LAPIC SVR 设置 — QEMU 默认已启用 LAPIC,
* 但实机 BIOS 可能留 LAPIC 为 software-disable 状态。
*/
static void lapic_enable(void) {
volatile u32 *lapic = g_lapic;
/* 确保 IA32_APIC_BASE 的 global enable 和 BSP 位已设置 */
u64 apic_base = rdmsr(0x1B);
apic_base |= (1ULL << 11); /* global enable */
/* 保持 BSP 位不变 (bit 8) */
wrmsr(0x1B, apic_base);
g_log->info("[apic] LAPIC global enable set in MSR");
/* 读当前 SVR */
u32 svr = lapic[0xF0 / 4];
log_hex("[apic] LAPIC SVR before=", svr);
/* 设置 Spurious Interrupt Vector = 0xFF + APIC software enable (bit 8) */
svr = 0xFF | (1u << 8);
lapic[0xF0 / 4] = svr;
apic_mb();
/* 回读验证 */
svr = lapic[0xF0 / 4];
log_hex("[apic] LAPIC SVR after=", svr);
if (svr & (1u << 8)) {
g_log->info("[apic] LAPIC software enabled");
} else {
g_log->warn("[apic] LAPIC SVR bit8 not set — hardware may not support");
}
/* 读 LAPIC ID 和 version 做诊断 dump */
log_hex("[apic] LAPIC ID=", lapic[0x20 / 4]);
log_hex("[apic] LAPIC LDR=", lapic[0xD0 / 4]);
log_hex("[apic] LAPIC version=", lapic[0x30 / 4]);
}
/* LAPIC EOI — 写 0 到 EOI 寄存器 (offset 0xB0)。
* B7 阶段2: 本函数作为 EOI 钩子经 kernel_api.register_apic_eoi 注册给 UTSM,
* idt_handler 在两个 IRQ 分支的分发末尾调用 (钩子签名 void(*)(void) 匹配)。
* 调用点: IRQ 上下文, 单核 BSP interrupt gate (IF=0), 无嵌套, 无需锁。 */
static __inline__ void lapic_eoi(void) {
if (g_lapic) {
g_lapic[0xB0 / 4] = 0;
apic_mb();
}
}
/* ---- B7 diag: HPET 绝对时基 (tick 2x 定论实验) ---- *
* 选型理由: TSC 校准 (PIT mode0 回绕法) 与被测对象 (PIT mode3 投递频率)
* 共享 PIT 硬件, 循环论证无法定论 2x 根因。HPET 主计数器是独立时基
* (QEMU q35: 100MHz, period=10000fs), 32 位回绕周期 ~42.9s, 对 ms 级
* 窗口充裕。只设 GEN_CONF bit0 (overall enable); 绝不置 bit1
* (legacy replacement route 会抢 PIT IRQ0/RTC IRQ8 路由, 破坏被测链路)。
*/
static volatile u32 *g_hpet; /* HPET MMIO 基址 (HHDM), 0=不可用 */
static u32 g_hpet_period_fs; /* 主计数器周期 (飞秒), GCAP_ID[63:32] */
#define HPET_GCAP_ID 0x00
#define HPET_GEN_CONF 0x10
#define HPET_MAIN_CNT 0xF0
static u64 hpet_read64(u32 off) {
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
u32 lo = p[0];
u32 hi = p[1];
return ((u64)hi << 32) | lo;
}
static void hpet_write64(u32 off, u64 v) {
volatile u32 *p = (volatile u32 *)((volatile u8 *)g_hpet + off);
p[0] = (u32)(v & 0xffffffffu);
p[1] = (u32)(v >> 32);
}
static __inline__ u32 hpet_main32(void) {
return *(volatile u32 *)((volatile u8 *)g_hpet + HPET_MAIN_CNT);
}
/* 初始化 HPET 时基; 成功返回 0 且 g_hpet/g_hpet_period_fs 有效 */
static int hpet_diag_init(const struct dkm_kernel_api *api) {
const acpi_hpet *hpet = find_hpet(api->rsdp_address);
if (!hpet) {
g_log->warn("[apic] diag: HPET table not found");
return -1;
}
if (hpet->addr_space_id != 0 || !hpet->base_addr) {
g_log->warn("[apic] diag: HPET not memory-mapped");
return -1;
}
g_hpet = (volatile u32 *)(uintptr_t)(api->hhdm_offset + hpet->base_addr);
u64 gcap = hpet_read64(HPET_GCAP_ID);
g_hpet_period_fs = (u32)(gcap >> 32);
if (!g_hpet_period_fs || g_hpet_period_fs > 100000000u) {
g_log->warn("[apic] diag: HPET period implausible");
g_hpet = 0;
return -1;
}
log_hex("[apic] diag: HPET base=", hpet->base_addr);
log_dec("[apic] diag: HPET period(fs)=", g_hpet_period_fs);
/* overall enable (bit0); legacy replacement route (bit1) 保持 0 */
u64 conf = hpet_read64(HPET_GEN_CONF);
if (!(conf & 1u)) {
hpet_write64(HPET_GEN_CONF, conf | 1u);
apic_mb();
}
g_log->info("[apic] diag: HPET main counter enabled");
return 0;
}
/* HPET 主计数器差值 → 微秒 (全程 u64 整数, -msoft-float 环境无浮点) */
static u64 hpet_delta_us(u32 h0, u32 h1) {
/* 无符号回绕安全: u32 减法自动回绕 (42.9s 周期, 窗口 << 此值) */
u64 ticks = (u64)(u32)(h1 - h0);
/* ticks * period_fs / 1e9 = us; period_fs 典型 10000, 500ms 窗口
* ticks≈5e7, 乘积 5e11 << 2^64, 溢出安全 */
return (ticks * (u64)g_hpet_period_fs) / 1000000000ull;
}
/* ---- B7 diag: PS/2 鼠标流 (IRQ12 持续投递验证) ---- *
* UTSM 阶段 IRQ12 无属主 (mouseInit 是 DSK 阶段模块), diag handler 独占;
* DSK mouseInit 之后经 irq_register(12) 覆盖本 handler 并完整复位鼠标,
* 无状态冲突。所有单步带超时, 失败只记日志不阻塞。 */
static volatile u32 g_mouse_pkt_count;
static int ps2_wait_ibf(void) {
u32 t = 100000;
while (t--) { if (!(inb(0x64) & 0x02)) return 0; }
return -1;
}
static int ps2_wait_obf(void) {
u32 t = 100000;
while (t--) { if (inb(0x64) & 0x01) return 0; }
return -1;
}
static void ps2_drain(void) {
while (inb(0x64) & 0x01) { (void)inb(0x60); }
}
/* 经 0xD4 通道写鼠标命令并等 ACK(0xFA); 失败返回 -1 */
static int ps2_aux_cmd(u8 cmd) {
if (ps2_wait_ibf() != 0) return -1;
outb(0x64, 0xD4);
if (ps2_wait_ibf() != 0) return -1;
outb(0x60, cmd);
if (ps2_wait_obf() != 0) return -1;
return inb(0x60) == 0xFA ? 0 : -1;
}
/* IRQ12 诊断 handler: 只读数据计数, EOI 交给 idt_handler 分发路径
* (PIC 模式: 8259 EOI; route 模式: 钩子 LAPIC EOI + 无害 8259 EOI)。
* 返回 0 — 不自负 EOI, 与 ps2kbd/e1000 handler 同一约定。 */
static int apic_mouse_diag_handler(u8 vector) {
(void)vector;
u8 st = inb(0x64);
if ((st & 0x01) && (st & 0x20)) { /* OBF 且 AUX 位置位 = 鼠标数据 */
u8 data = inb(0x60); /* 必须读, 否则 OBF 保持阻塞后续包 */
g_mouse_pkt_count++;
if (g_mouse_pkt_count <= 6) {
g_log->info("[apic] diag: mouse packet byte");
log_hex("[apic] diag: data=", data);
}
}
return 0;
}
/* IRQ1 诊断 handler (键盘流计数): ps2kbd demo 窗口只有 ~10ms 数量级,
* 自动化无法抓住; diag 在 stage0 自建 3s 观察窗验证 IRQ1 持续投递。
* stage3 ps2kbd 注册时覆盖本 handler, 无冲突。 */
static volatile u32 g_kbd_pkt_count;
static int apic_kbd_diag_handler(u8 vector) {
(void)vector;
u8 st = inb(0x64);
if ((st & 0x01) && !(st & 0x20)) { /* OBF 且 AUX 位清零 = 键盘数据 */
u8 data = inb(0x60); /* 读走扫描码 (窗内按键, 无消费者) */
g_kbd_pkt_count++;
if (g_kbd_pkt_count <= 6) {
g_log->info("[apic] diag: kbd scancode byte");
log_hex("[apic] diag: sc=", data);
}
}
return 0;
}
/* AUX 端口初始化: 启用 AUX 时钟 + 采样率 100 + 数据报告, 最后才开 AUX IRQ。
* 顺序要点: AUX IRQ (cfg bit1) 必须在全部命令/ACK 交互完成后才使能 —
* 否则 ACK 字节 (0xFA) 到达即触发 IRQ12 被 diag handler 读走, 轮询侧
* 永远等不到 ACK (实测 F3 nack 即此竞态: handler 日志 data=0xfa)。
* 单步失败只记日志继续 (与 mouseInit 同一安全准则)。 */
static void ps2_aux_enable_stream(void) {
ps2_drain();
if (ps2_wait_ibf() != 0) { g_log->warn("[apic] diag: PS2 ibf timeout"); return; }
outb(0x64, 0x20); /* 读配置字节 */
if (ps2_wait_obf() != 0) { g_log->warn("[apic] diag: PS2 cfg read timeout"); return; }
u8 cfg = inb(0x60);
cfg &= (u8)~0x02; /* bit1=0: AUX IRQ 先关闭 (轮询期) */
cfg &= (u8)~0x20; /* bit5=0: AUX clock enable */
cfg |= 0x01; /* bit0=1: KBD IRQ 保持使能 (键盘流用) */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x60); /* 写配置字节 */
if (ps2_wait_ibf() != 0) return;
outb(0x60, cfg);
log_hex("[apic] diag: PS2 cfg byte=", cfg);
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0xA8); /* 启用 AUX 端口 */
if (ps2_aux_cmd(0xF3) != 0) { g_log->warn("[apic] diag: mouse F3 nack"); }
else if (ps2_aux_cmd(100) != 0) { g_log->warn("[apic] diag: mouse rate nack"); }
if (ps2_aux_cmd(0xF4) != 0) { g_log->warn("[apic] diag: mouse F4 nack"); return; }
g_log->info("[apic] diag: PS/2 mouse data reporting enabled");
ps2_drain(); /* 清 F4 ACK 后可能挂起的首个流字节 */
/* 全部交互完成, 现在才开 AUX IRQ — 之后鼠标字节全走 IRQ12 handler */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x20);
if (ps2_wait_obf() != 0) return;
cfg = inb(0x60);
cfg |= 0x02; /* bit1=1: AUX IRQ enable */
if (ps2_wait_ibf() != 0) return;
outb(0x64, 0x60);
if (ps2_wait_ibf() != 0) return;
outb(0x60, cfg);
log_hex("[apic] diag: PS2 cfg byte(final)=", cfg);
}
/* ---- B7 阶段2: PIT tick 路由验证 ---- *
* vector 0x20 落在 idt_handler 的 PIC 分支 (vector 32-47) — 与键盘 0x21/
* 鼠标 0x2C/网卡 0x2B 同一代码路径。分发顺序: handler(本函数) → 钩子
* LAPIC EOI → 8259 EOI (PIC 全屏蔽下为无害 no-op)。
* 本 handler 返回 0 不自负 EOI: 由 UTSM 钩子写 LAPIC EOI 寄存器清除
* ISR 位; 重复投递 (计数>=2) 即证明"legacy 向量分支 + 钩子 EOI"可持续,
* 这正是键盘/鼠标/网卡将要走的路径 (阶段1 的 0xE0+自EOI 无法证明此路径)。
* handler 是普通 C 函数 (SysV ABI), isr stub 已完成现场保存/iret。
*/
static int apic_tick_handler(u8 vector) {
(void)vector;
g_tick_count++;
if (g_tick_count <= 8) {
g_log->info("[apic] ioapic tick (PIT GSI -> LAPIC -> IDT vec 0x20, hook EOI)");
}
return 0;
}
static u64 rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
static u64 g_tsc_per_ms;
/* PIT ch0 mode 0 单发 (~10ms, 11932 counts) 校准 TSC。
* 必须在把 PIT 改为 mode 3 周期模式之前调用。 */
static void tsc_calibrate(void) {
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
outb(0x40, 0x9C); /* 11932 low = ~10ms */
outb(0x40, 0x2E); /* 11932 high */
u64 tsc_start = rdtsc();
u16 prev = 0;
u64 loops = 0;
for (;;) {
outb(0x43, 0x00);
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
if (cur > prev && loops > 10) break; /* 回绕 = 10ms 到 */
prev = cur;
loops++;
}
u64 tsc_end = rdtsc();
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
}
static void tsc_delay_ms(u32 ms) {
if (!g_tsc_per_ms) {
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
return;
}
u64 target = g_tsc_per_ms * ms;
u64 start = rdtsc();
while (rdtsc() - start < target) __asm__ volatile("pause");
}
/* ---- B7 阶段2: 路由切换主流程 ---- *
* 成功: PIC IMR 全 1, IOAPIC 拥有路由, EOI 钩子保持注册, tick 验证后
* tick 线重新 mask (保留路由表内容), legacy 线按 apic_route_legacy。
* 失败: IOAPIC 全 mask + 恢复原 PIC IMR + 注销 tick handler + 注销 EOI
* 钩子, 与切换前状态完全一致 (现状不回归)。
*/
static void apic_route_switch(const struct dkm_kernel_api *api,
struct madt_info *info) {
g_log->info("[apic] apic_route=1: attempting IOAPIC routing takeover");
if (!g_lapic) {
g_log->warn("[apic] route abort: LAPIC MMIO unavailable; PIC retained");
return;
}
if (info->ioapic_count == 0) {
g_log->warn("[apic] route abort: no IOAPIC; PIC retained");
return;
}
if (!api->irq_register) {
g_log->warn("[apic] route abort: irq_register missing; PIC retained");
return;
}
/* 0) B7 阶段2 前提: EOI 钩子接口必须存在 (旧内核无此字段 → 判空中止)。
* 无钩子时 legacy 向量 (0x20-0x2F) 在 IOAPIC 路由下只能 one-shot,
* 路由接管不成立 — 宁可保留 PIC 也不进入半残状态。 */
if (!api->register_apic_eoi) {
g_log->warn("[apic] route abort: register_apic_eoi missing (old kernel); PIC retained");
return;
}
api->register_apic_eoi(lapic_eoi);
g_log->info("[apic] LAPIC EOI hook registered to UTSM");
/* 1) 保存 PIC IMR (降级恢复用) */
u8 imr1 = inb(0x21);
u8 imr2 = inb(0xA1);
log_hex("[apic] PIC IMR saved master=", imr1);
log_hex("[apic] PIC IMR saved slave=", imr2);
/* 2) TSC 校准 (PIT 尚在 firmware/driver 遗留状态, 先校准再改模式)。
* diag: HPET 绝对时基对拍本窗口 — 校准窗口标称 10ms, HPET 实测
* 若 ~20000us 则 PIT mode0 实际跑了 20ms (TSC 测半), ~10000us
* 则校准正确, tick 2x 根因另在他处。 */
int hpet_ok = 0;
if (g_cfg.apic_diag) {
hpet_ok = (hpet_diag_init(api) == 0);
}
u32 hc0 = 0;
if (hpet_ok) hc0 = hpet_main32();
tsc_calibrate();
if (hpet_ok) {
u32 hc1 = hpet_main32();
u64 calib_us = hpet_delta_us(hc0, hc1);
log_dec("[apic] diag: calib window(us)=", calib_us);
g_log->info("[apic] diag: (nominal 10000us; ~20000 => TSC half-measured)");
}
log_hex("[apic] tsc_per_ms=", g_tsc_per_ms);
/* 3) 注册 tick handler @ vector 0x20 — PIC 分支 (legacy 向量路径),
* handler 返回 0, LAPIC EOI 由 UTSM 钩子完成 */
if (api->irq_register(0x20, (void *)(uintptr_t)apic_tick_handler) != 0) {
g_log->warn("[apic] route abort: irq_register(0x20) failed");
api->register_apic_eoi(NULL);
return;
}
g_log->info("[apic] tick handler registered at vector 0x20 (PIC branch, hook EOI)");
/* 4) PIT tick GSI 解析: MADT ISO source 0 (QEMU q35: GSI2), 缺省恒等 */
u16 tick_flags = 0;
u32 tick_gsi = isa_irq_to_gsi(0, &tick_flags, info->isos, info->iso_count);
log_hex("[apic] tick GSI=", tick_gsi);
log_hex("[apic] tick GSI flags=", tick_flags);
/* 5) 编程 tick 重定向项 (vector 0x20, 放开) */
if (ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 1) != 0) {
g_log->warn("[apic] route abort: tick GSI not covered by any IOAPIC");
api->irq_register(0x20, (void *)0);
api->register_apic_eoi(NULL);
return;
}
g_log->info("[apic] tick redirection unmasked (vec 0x20)");
/* 6) legacy 线 (apic_route_legacy): EOI 钩子已就位, 持续投递成立。
* ISA 线 (IRQ1/12) 按 ISO, 缺省 edge/active-high;
* PCI 线 (IRQ11 e1000) 无 ISO 覆盖时强制 level/active-low
* (PCI INTx 规范; flags=0xF = pol bit0-1=3 active-low + trig bit2-3=3
* level)。e1000 handler 读 ICR 清源 → 电平 deassert → 钩子 EOI。 */
if (g_cfg.apic_route_legacy) {
u16 f1 = 0, f12 = 0, f11 = 0;
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
u32 gsi11 = isa_irq_to_gsi(11, &f11, info->isos, info->iso_count);
if (!f11) f11 = 0x0F;
ioapic_program_gsi(info, gsi1, 0x21, f1, 1); /* 键盘 */
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1); /* 鼠标 */
ioapic_program_gsi(info, gsi11, 0x2B, f11, 1); /* e1000 (QEMU 默认 IRQ11) */
g_log->info("[apic] legacy IRQ1/12/11 unmasked (sustained delivery via EOI hook)");
log_hex("[apic] kbd gsi=", gsi1);
log_hex("[apic] mouse gsi=", gsi12);
log_hex("[apic] e1000 gsi=", gsi11);
log_hex("[apic] e1000 flags=", f11);
}
/* 7) LAPIC TPR=0 全放行 */
g_lapic[0x80 / 4] = 0;
apic_mb();
log_hex("[apic] LAPIC TPR=", g_lapic[0x80 / 4]);
/* 8) PIC 全屏蔽 (先 slave 后 master, 避免 cascade 窗口; QEMU 无功能 IMCR) */
outb(0xA1, 0xFF);
io_wait();
outb(0x21, 0xFF);
io_wait();
g_log->info("[apic] PIC fully masked; IOAPIC owns IRQ routing");
/* 9) PIT ch0 mode 3 周期 ~100Hz (divisor 11932), 产生真实 tick 源 */
outb(0x43, 0x36);
outb(0x40, 0x9C);
outb(0x40, 0x2E);
g_log->info("[apic] PIT ch0 mode3 100Hz started");
/* 10) 500ms 验证窗口 (+ diag: HPET 实测窗口与 tick 频率定论) */
u32 hv0 = 0;
if (hpet_ok) hv0 = hpet_main32();
u32 before = g_tick_count;
tsc_delay_ms(500);
u32 ticks = g_tick_count - before;
if (hpet_ok) {
u32 hv1 = hpet_main32();
u64 win_us = hpet_delta_us(hv0, hv1);
log_dec("[apic] diag: verify window(us)=", win_us);
if (win_us) {
u64 hz_x10 = (u64)ticks * 10000000ull / win_us;
log_dec("[apic] diag: tick rate(Hz x10)=", hz_x10);
}
/* tick 2x 定论分支:
* win≈500ms 且 ticks≈100 → PIT mode3 在 IOAPIC edge 下双触发 (200Hz 真实)
* win≈1000ms 且 ticks≈100 → TSC 校准窗口翻倍 (tick 频率真实 100Hz)
* win≈500ms 且 ticks≈50 → 一切正常 */
if (win_us > 750000) {
g_log->info("[apic] diag: TICK2X VERDICT: TSC calib window doubled (PIT mode0 ran ~20ms); tick rate nominal");
} else if (ticks > 75) {
g_log->info("[apic] diag: TICK2X VERDICT: PIT mode3 double-clocked on IOAPIC edge (~200Hz real)");
} else {
g_log->info("[apic] diag: TICK2X VERDICT: nominal (~100Hz, no 2x)");
}
}
log_dec("[apic] route verify ticks=", ticks);
if (ticks >= 2) {
/* 成功: 重复投递证明钩子 EOI 正确清除 ISR 位, 路由链路完整 */
g_log->info("[apic] ROUTE VERIFY OK — IOAPIC->LAPIC->IDT(vec 0x20)->handler->hook EOI chain proven");
/* diag: 第二窗口持续投递证据 (排除窗口内偶发两次的弱证明) */
if (g_cfg.apic_diag) {
u32 before2 = g_tick_count;
tsc_delay_ms(500);
u32 ticks2 = g_tick_count - before2;
log_dec("[apic] diag: sustain window ticks=", ticks2);
if (ticks2 >= 2) {
g_log->info("[apic] diag: SUSTAINED delivery confirmed (2nd window)");
}
}
/* tick 线重新 mask (保留路由表, vector 保持 0x20), 避免后台周期中断;
* PIT 恢复 mode 3 @ 18.2Hz (BIOS 惯例值, 已被 mask 不会投递)。
* EOI 钩子保持注册 — legacy 线的持续投递依赖它。 */
ioapic_program_gsi(info, tick_gsi, 0x20, tick_flags, 0);
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
g_route_active = 1;
g_log->info("[apic] APIC ROUTING ACTIVE (PIC masked; EOI hook live; tick line re-masked)");
} else {
/* 失败: 完整降级回 PIC (含注销 EOI 钩子) */
g_log->error("[apic] ROUTE VERIFY FAILED — degrading to PIC routing");
ioapic_mask_all(info);
api->irq_register(0x20, (void *)0);
api->register_apic_eoi(NULL);
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
outb(0x21, imr1);
io_wait();
outb(0xA1, imr2);
io_wait();
g_log->info("[apic] PIC routing restored (IMR restored, IOAPIC masked, hook cleared)");
}
}
/* ---- B7 diag: IRQ1/IRQ12 键鼠流观察窗 ---- *
* 在 route switch 之后调用 (route 成功走 IOAPIC; 否则走 PIC IMR)。
* 打锚点日志供测试脚本/人工在窗口内用 QEMU monitor sendkey/mouse_move
* 注入输入; 窗口结束分别报告收包计数。计数 >1 即证明该线在当前路由
* 模式下可持续投递 (一次按键=make+break 两包, 一次移动=多包)。 */
static void apic_diag_mouse_window(const struct dkm_kernel_api *api,
struct madt_info *info) {
g_log->info("[apic] diag: IRQ1/IRQ12 input stream check begin");
/* 确保 TSC 延时可用 (route=0 时未校准: 现场校准并恢复 PIT 惯例状态) */
if (!g_tsc_per_ms) {
tsc_calibrate();
outb(0x43, 0x36);
outb(0x40, 0x00);
outb(0x40, 0x00);
}
if (!api->irq_register) return;
if (g_route_active) {
/* IOAPIC 路由: 确保 IRQ12/IRQ1 重定向项放开 (legacy=1 时幂等重编) */
u16 f12 = 0, f1 = 0;
u32 gsi12 = isa_irq_to_gsi(12, &f12, info->isos, info->iso_count);
u32 gsi1 = isa_irq_to_gsi(1, &f1, info->isos, info->iso_count);
ioapic_program_gsi(info, gsi12, 0x2C, f12, 1);
ioapic_program_gsi(info, gsi1, 0x21, f1, 1);
g_log->info("[apic] diag: IRQ12+IRQ1 via IOAPIC redirection");
} else {
/* PIC 路由: 解 slave IMR bit4 (IRQ12); master IMR bit1 (IRQ1)
* UTSM pic_remap 已留 0xFD (IRQ1 enable), 幂等确认。
* diag 属实验开关, 显式改变 PIC 状态并打日志; UTSM PIC 分支
* 会做 8259 EOI, diag handler 返回 0 即可。 */
outb(0xA1, (u8)(inb(0xA1) & (u8)~0x10u));
outb(0x21, (u8)(inb(0x21) & (u8)~0x02u));
g_log->info("[apic] diag: IRQ12+IRQ1 via PIC (IMR bits cleared)");
}
/* 控制器交互必须全轮询完成: 期间到达的 IRQ (KBD bit0 一直开着)
* 因 handler 未注册只会被 EOI 丢弃, 不会偷走命令响应字节。
* (实测教训: 先注册 handler 时, 0x20 读 cfg 的响应被 kbd handler
* 当扫描码读走, 轮询侧 cfg read timeout) */
ps2_aux_enable_stream();
/* 交互全部完成, 现在才注册诊断 handler */
if (api->irq_register(0x2C, (void *)(uintptr_t)apic_mouse_diag_handler) != 0) {
g_log->warn("[apic] diag: irq_register(0x2C) failed");
return;
}
if (api->irq_register(0x21, (void *)(uintptr_t)apic_kbd_diag_handler) != 0) {
g_log->warn("[apic] diag: irq_register(0x21) failed");
return;
}
/* 6000ms 窗口: 注入方需经 "串口日志落盘(块缓冲) → 测试脚本轮询" 的
* 可见性延迟 (~1s 量级), 3s 窗口实测会被注入延迟吃光 */
g_log->info("[apic] diag: INPUT OBSERVE WINDOW 6000ms — sendkey/mouse_move now");
u32 m_before = g_mouse_pkt_count;
u32 k_before = g_kbd_pkt_count;
tsc_delay_ms(6000);
u32 mpkts = g_mouse_pkt_count - m_before;
u32 kpkts = g_kbd_pkt_count - k_before;
log_dec("[apic] diag: mouse packets observed=", mpkts);
log_dec("[apic] diag: kbd packets observed=", kpkts);
if (mpkts > 1) {
g_log->info("[apic] diag: IRQ12 SUSTAINED delivery confirmed");
} else if (mpkts == 1) {
g_log->warn("[apic] diag: IRQ12 single packet only (check EOI path)");
} else {
g_log->info("[apic] diag: no mouse packets (inject via monitor mouse_move)");
}
if (kpkts > 1) {
g_log->info("[apic] diag: IRQ1 SUSTAINED delivery confirmed");
} else if (kpkts == 1) {
g_log->warn("[apic] diag: IRQ1 single packet only (check EOI path)");
} else {
g_log->info("[apic] diag: no kbd packets (inject via monitor sendkey)");
}
/* handler 保持注册: stage3 ps2kbd 经 irq_register(1) 覆盖键盘 handler,
* DSK mouseInit 经 irq_register(12) 覆盖鼠标 handler, 均无冲突;
* 重定向项/IMR 保持现状 (ps2kbd/mouseInit 有完整初始化序列)。 */
}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
@@ -301,6 +1457,9 @@ int driver_init(const struct dkm_kernel_api *api,
}
g_log->info("[apic] kernel IRQ backend present: PIC");
/* B7: FUCK 配置自解析 (lapic_enable/ioapic_init/apic_route/apic_route_legacy/apic_diag) */
load_fuck_config(api);
if (!cpu_has_lapic()) {
g_log->warn("[apic] CPUID local APIC not present; PIC fallback active");
g_log->info("[apic] driver ready");
@@ -338,15 +1497,60 @@ int driver_init(const struct dkm_kernel_api *api,
}
if (api->hhdm_offset && info.lapic_phys) {
volatile u32 *lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
log_hex("[apic] LAPIC ID reg=", lapic[0x20 / 4]);
log_hex("[apic] LAPIC version reg=", lapic[0x30 / 4]);
log_hex("[apic] LAPIC SVR reg=", lapic[0xF0 / 4]);
g_lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys);
g_lapic_id = g_lapic[0x20 / 4] >> 24;
log_hex("[apic] LAPIC ID reg=", g_lapic[0x20 / 4]);
log_hex("[apic] LAPIC version reg=", g_lapic[0x30 / 4]);
log_hex("[apic] LAPIC SVR reg=", g_lapic[0xF0 / 4]);
log_hex("[apic] LAPIC TPR reg=", g_lapic[0x80 / 4]);
log_hex("[apic] BSP LAPIC ID=", g_lapic_id);
} else {
g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read");
g_lapic = (volatile u32 *)0;
}
g_log->info("[apic] discovery complete; PIC routing retained");
/* ---- LAPIC 使能(真机必需,QEMU 也可安全执行) ---- */
if (g_lapic && g_cfg.lapic_enable) {
lapic_enable();
} else if (!g_cfg.lapic_enable) {
g_log->info("[apic] lapic_enable=0: skip LAPIC software enable");
}
/* ---- IOAPIC 初始化(真机必需,QEMU 也可安全执行) ---- */
if (g_cfg.ioapic_init) {
for (u32 i = 0; i < info.ioapic_count; i++) {
g_log->info("[apic] initializing IOAPIC");
log_hex("[apic] index=", i);
ioapic_init_one(&info.ioapics[i], api->hhdm_offset,
info.isos, info.iso_count,
api->mm_map_mmio);
}
} else {
g_log->info("[apic] ioapic_init=0: skip IOAPIC programming (PIC mode)");
}
/* ---- B7: 路由接管 (默认关, FUCK apic_route=1 启用) ---- */
if (g_cfg.apic_route && g_cfg.ioapic_init) {
apic_route_switch(api, &info);
} else {
g_log->info("[apic] apic_route=0: PIC routing retained (default)");
}
/* ---- B7 diag: IRQ12 鼠标流观察窗 (apic_diag=1, 两种路由模式皆可测) ---- */
if (g_cfg.apic_diag) {
apic_diag_mouse_window(api, &info);
}
/* 重定向表回读 (验证证据; 无 IOAPIC 时无输出) */
if (g_cfg.ioapic_init) {
ioapic_dump_table(&info);
}
if (g_route_active) {
g_log->info("[apic] discovery + init complete; APIC routing active");
} else {
g_log->info("[apic] discovery + init complete; PIC routing retained as fallback");
}
g_log->info("[apic] driver ready");
return 0;
}
+607 -10
View File
@@ -18,6 +18,7 @@
*/
#include "../dkm_shared.h"
#include "wpa2_crypto.h"
static const char *const g_depends[] = {"pci","irq"};
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 int g_scanning;
/* 前向声明:接收路径需要把管理/控制帧交给连接状态机 */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
static void ath9k_handle_eapol(const u8 *eap, u32 len);
static int eapol_find(const u8 *frame, u32 len, u32 *out_off);
static void copy_mac(u8 dst[6], const u8 src[6]);
/* ---- 串口日志(参考 e1000 raw_log) ---- */
#define COM1 0x3F8
@@ -256,6 +263,23 @@ static int ath9k_find(u8 *bus, u8 *dev) {
raw_log(hex); raw_log("\n");
return 0;
}
/* 无匹配 Atheros 设备:打印本机所有无线控制器 (class 02:80) 的
* vendor:device,便于确认实机卡型号(ATH 硬件要求见 CLAUDE.md)。 */
{
u32 found = 0;
for (u8 d = 0; d < 32; d++) {
u32 vd = pci_read(0, d, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u32 cls = pci_read(0, d, 0, 0x08);
if (((cls >> 24) & 0xff) != 0x02 || (((cls >> 16) & 0xff) != 0x80)) continue;
raw_hex("[ath9k.raw] wireless ctrl (non-Atheros) dev=", (u64)d);
raw_hex("[ath9k.raw] vendor:device=", ((u64)(vd & 0xffff) << 16) | (u64)(vd >> 16));
found++;
}
raw_hex("[ath9k.raw] wireless controllers found=", (u64)found);
raw_log("[ath9k.raw] NO SUPPORTED ATHEROS DEVICE (need AR928x PCIe, vendor 168c)\n");
}
return -1;
}
@@ -478,6 +502,20 @@ static int find_bssid(const u8 bssid[6]) {
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) {
if (len < 24) return;
u16 fctl = (u16)buf[0] | ((u16)buf[1] << 8);
u16 ftype = fctl & 0x000C;
/* 连接状态机:管理帧(认证/关联响应)与数据帧中的 EAPOL-Key */
if (ftype == 0x0000) {
if (ath9k_handle_mgmt(buf, len) == 0) return;
} else if (ftype == 0x0008) { /* data */
u32 eoff = 0;
if (eapol_find(buf, len, &eoff) == 0)
ath9k_handle_eapol(buf + eoff, len - eoff);
/* 数据帧继续走 beacon 解析(多数会被拒绝) */
}
struct ath9k_scan_result tmp;
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
if (parse_beacon(buf, len, &tmp) != 0) return;
@@ -531,8 +569,11 @@ static void ath9k_rx_drain(void) {
* 真实硬件验证后增量完善。
*/
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 */
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
dkm_delay_us(5);
@@ -601,12 +642,550 @@ static int ath9k_is_wireless_impl(void *ctx) {
return 1;
}
/* ath9k 当前不支持 TX(仅扫描) */
static int ath9k_tx_stub(void *ctx, const void *packet, u32 length) {
(void)ctx; (void)packet; (void)length;
/* ============================================================
* TX 路径 + WPA2-PSK 连接状态机
*
* 说明:TX 描述符采用 AR9002(AR9280/9285)队列描述符布局的
* 最小可用子集;位域与 HAL initvals 需在实机 bring-up 时用
* 串口日志对拍(日志点见 ath9k_tx_frame)。协议层(认证/关联/
* 4-way/密钥安装)由 wpa2_crypto 提供并通过 wpa2_kat 自检。
* ============================================================ */
/* TX 队列(BE)寄存器 */
#define AR_TXDP(_q) (0x0000u + 0x0800u * (_q)) /* TXQ 描述符基址 */
#define AR_TXE 0x0800u
#define AR_TXCFG 0x0008u
#define AR_TXCFG_TXE 0x00000004u
#define ATH9K_TX_DESC_COUNT 8u
#define ATH9K_TX_BUF_SIZE 2048u
/* AR9002 TX 队列描述符(32 字节,最小字段集) */
struct ath9k_tx_desc {
u32 ds_link; /* 下一描述符物理地址 */
u32 ds_data; /* 数据缓冲物理地址 */
u32 ds_ctl0; /* FRAME_LEN(0:12) | TXQ(12:16) */
u32 ds_ctl1; /* BUF_LEN(0:12) | MORE(12) */
u32 ds_ctl2; /* RTS/CTS + 速率索引(0 = 默认 1M CCK) */
u32 ds_ctl3; /* 分片/重试(0) */
u32 ds_ctl4;
u32 ds_ctl5;
} __attribute__((packed));
#define ATH9K_TXCTL0_LEN_S 0
#define ATH9K_TXCTL0_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_LEN_S 0
#define ATH9K_TXCTL1_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_MORE 0x00001000u
/* ---- 连接状态 ---- */
#define WPA_STATE_IDLE 0
#define WPA_STATE_AUTH_SENT 1
#define WPA_STATE_ASSOC_SENT 2
#define WPA_STATE_M1_WAIT 3
#define WPA_STATE_M3_WAIT 4
#define WPA_STATE_CONNECTED 5
static struct dkm_dma_buffer g_tx_desc_dma;
static struct ath9k_tx_desc *g_tx_desc;
static struct dkm_dma_buffer g_tx_buf_dma;
static u8 *g_tx_buf;
static u32 g_tx_head;
static int g_tx_ready;
static const struct dkm_dma_api *g_dma;
/* WPA2 会话 */
static u8 g_sta_mac[6]; /* SPA */
static u8 g_ap_mac[6]; /* AA / BSSID */
static u8 g_channel;
static char g_target_ssid[ATH9K_SSID_MAX + 1];
static int g_wpa_state;
static u8 g_anonce[32];
static u8 g_snonce[32];
static u8 g_pmk[WPA2_PMK_LEN];
static u8 g_ptk[64]; /* KCK(16) KEK(16) TK(16) + 余量 */
static u64 g_ptk_replay;
static u64 g_tx_pn; /* CCMP 发包序号 */
static u16 g_seq;
static int g_key_installed;
#define ATH9K_STA_MAC_ID0 0x0800u
/* ---- 802.11 管理帧构造 ---- */
#define IEEE80211_STYPE_AUTH 0x00B0u
#define IEEE80211_STYPE_ASSOC_REQ 0x0000u
#define IEEE80211_STYPE_ASSOC_RESP 0x0010u
#define IEEE80211_STYPE_REASSOC_REQ 0x0020u
#define IEEE80211_STYPE_DEAUTH 0x00C0u
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_ERP 42
#define WLAN_EID_RSN 48
static u32 put_le16(u8 *p, u16 v) { p[0]=(u8)v; p[1]=(u8)(v>>8); return 2; }
/* 帧控制字(管理帧,非保护) */
static void mgmt_fc(u8 *f, u16 subtype) {
u16 fc = 0x0000u | subtype; /* proto 0, type=mgmt(0) */
f[0] = (u8)(fc & 0xFF);
f[1] = (u8)(fc >> 8);
f[2] = 0; f[3] = 0; /* duration */
}
/* 填 24 字节管理帧头:DA / SA / BSSID / seq */
static void mgmt_hdr(u8 *f, const u8 *da, const u8 *sa, const u8 *bssid) {
for (int i = 0; i < 6; i++) f[4+i] = da[i];
for (int i = 0; i < 6; i++) f[10+i] = sa[i];
for (int i = 0; i < 6; i++) f[16+i] = bssid[i];
u16 sc = (u16)((++g_seq & 0xFFF) << 4);
f[22] = (u8)(sc & 0xFF); f[23] = (u8)(sc >> 8);
}
/* WPA2-PSK RSN IE(AKM 00-0F-AC:2,CCMP) */
static u32 rsn_ie_psk(u8 *p) {
u8 *q = p;
q[0] = WLAN_EID_RSN;
q[1] = 20; /* 长度 */
q += 2;
q += put_le16(q, 1); /* version */
/* group cipher: OUI 00-0F-AC + type 4 (CCMP) */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* pairwise count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* AKM count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x02; q += 4; /* PSK */
q += put_le16(q, 0); /* RSN caps */
return (u32)(q - p);
}
/* 发送一帧(构造 802.11 头 → TX 环 → 触发) */
static int ath9k_tx_frame(const u8 *frame, u32 len);
/* ---- 认证请求(open system) ---- */
static int ath9k_send_auth(void) {
u8 f[30 + 4];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_AUTH);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
/* auth body: alg(2)=0, seq(2)=1, status(2)=0 */
put_le16(f + 24, 0);
put_le16(f + 26, 1);
put_le16(f + 28, 0);
raw_log("[ath9k.raw] tx auth request\n");
return ath9k_tx_frame(f, 30);
}
/* ---- 关联请求(含 RSN IE) ---- */
static int ath9k_send_assoc(void) {
u8 f[24 + 4 + 2 + 34 + 4 + 24 + 2];
u32 n = 0;
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_ASSOC_REQ);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
n = 24;
n += put_le16(f + n, 0x0431); /* capability: ESS + privacy + short preamble */
n += put_le16(f + n, 10); /* listen interval */
/* SSID IE */
{
u32 sl = 0;
while (g_target_ssid[sl]) sl++;
f[n++] = WLAN_EID_SSID;
f[n++] = (u8)sl;
for (u32 i = 0; i < sl; i++) f[n++] = (u8)g_target_ssid[i];
}
/* supported rates: 1,2,5.5,11,6,9,12,18 Mbps */
f[n++] = WLAN_EID_SUPP_RATES;
f[n++] = 8;
f[n++] = 0x82; f[n++] = 0x84; f[n++] = 0x8B; f[n++] = 0x96;
f[n++] = 0x0C; f[n++] = 0x12; f[n++] = 0x18; f[n++] = 0x24;
/* RSN IE */
n += rsn_ie_psk(f + n);
raw_log("[ath9k.raw] tx assoc request\n");
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key 构造(802.1X 4-way) ---- */
#define EAPOL_VERSION 2
#define EAPOL_TYPE_KEY 3
#define WPA_KEY_DESC_TYPE 2
#define WPA_KEY_INFO_KEY_TYPE 0x0008u /* 1 = pairwise */
#define WPA_KEY_INFO_INSTALL 0x0040u
#define WPA_KEY_INFO_ACK 0x0080u
#define WPA_KEY_INFO_MIC 0x0100u
#define WPA_KEY_INFO_SECURE 0x0200u
#define WPA_KEY_INFO_ENC_KEY 0x1000u
#define WPA_KEY_INFO_VER2 0x0002u /* WPA2 = 3 (HMAC-SHA1 + AES) */
/* 构造 802.11 数据帧(toDS)+ LLC/SNAP + EAPOL-Key,做 MIC 后发送。
* key_info 由调用方给,nonce 指针可为 0(M4 用)。 */
static int ath9k_send_eapol(u16 key_info, const u8 *nonce,
const u8 *key_data, u32 kd_len) {
static u8 f[256];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* 802.11 data 头:toDS(AP 为 RA=A1,SA=A2,DA=A3=BSSID) */
u16 fc = 0x0100u | 0x0008u; /* data + ToDS */
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
u32 n = 24;
/* LLC/SNAP:AA AA 03 00 00 00 88 8E (EAPOL) */
static const u8 snap[8] = {0xAA,0xAA,0x03,0x00,0x00,0x00,0x88,0x8E};
for (int i = 0; i < 8; i++) f[n++] = snap[i];
/* EAPOL 头 */
u32 eapol_off = n;
f[n++] = EAPOL_VERSION;
f[n++] = EAPOL_TYPE_KEY;
u32 len_off = n; n += 2; /* 长度稍后回填 */
/* EAPOL-Key 体 */
u32 mic_off;
f[n++] = WPA_KEY_DESC_TYPE;
n += put_le16(f + n, key_info);
n += put_le16(f + n, 16); /* key length (CCMP) */
for (int i = 0; i < 8; i++) f[n++] = (u8)(g_ptk_replay >> (i * 8));
for (int i = 0; i < 32; i++) f[n++] = nonce ? nonce[i] : 0; /* nonce */
n += 16; /* key IV(全 0) */
n += 8; /* key RSC */
n += 8; /* key ID(全 0) */
mic_off = n; n += 16; /* key MIC(待填) */
n += put_le16(f + n, (u16)kd_len);
for (u32 i = 0; i < kd_len; i++) f[n++] = key_data[i];
/* 回填 EAPOL 长度(含 4 字节 EAPOL 头) */
u16 elen = (u16)(n - eapol_off - 4);
f[len_off] = (u8)(elen & 0xFF); f[len_off + 1] = (u8)(elen >> 8);
/* MIC:若置位,对 EAPOL 帧(MIC 字段已清零)用 KCK 计算 HMAC-SHA1-128 */
if (key_info & WPA_KEY_INFO_MIC) {
u8 mic[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, f + eapol_off, n - eapol_off, mic);
for (int i = 0; i < 16; i++) f[mic_off + i] = mic[i];
}
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key M2(SNonce + MIC,4-way 第 2 条) ---- */
static int ath9k_send_m2(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
u8 kd[2 + 20]; /* RSN IE(WPA2 常见,可省) */
u32 kdn = rsn_ie_psk(kd);
raw_log("[ath9k.raw] tx EAPOL M2\n");
return ath9k_send_eapol(ki, g_snonce, kd, kdn);
}
/* ---- EAPOL-Key M4(MIC + secure,4-way 第 4 条) ---- */
static int ath9k_send_m4(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE |
WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
raw_log("[ath9k.raw] tx EAPOL M4\n");
return ath9k_send_eapol(ki, 0, 0, 0);
}
/* ---- PTK 派生(802.11i H.4) ---- */
static void ath9k_derive_ptk(void) {
u8 seed[64];
const u8 *aa = g_ap_mac, *spa = g_sta_mac;
int swap = 0;
for (int i = 0; i < 6; i++) {
if (aa[i] != spa[i]) { swap = aa[i] > spa[i]; break; }
}
const u8 *m1 = swap ? spa : aa; /* min(AA,SPA) */
const u8 *m2 = swap ? aa : spa; /* max(AA,SPA) */
for (int i = 0; i < 6; i++) { seed[i] = m1[i]; seed[6+i] = m2[i]; }
int nswap = 0;
for (int i = 0; i < 32; i++) {
if (g_anonce[i] != g_snonce[i]) { nswap = g_anonce[i] > g_snonce[i]; break; }
}
const u8 *n1 = nswap ? g_snonce : g_anonce;
const u8 *n2 = nswap ? g_anonce : g_snonce;
for (int i = 0; i < 32; i++) { seed[12+i] = n1[i]; seed[44+i] = n2[i]; }
wpa2_prf512(g_pmk, seed, 64, g_ptk);
}
/* ---- 扫描结果查找(按 SSID 找 BSSID/信道) ---- */
static int ath9k_find_ssid(const char *ssid, u8 *bssid, u8 *channel, u8 *sec) {
for (u32 i = 0; i < g_scan_count; i++) {
const char *a = g_scan_results[i].ssid;
const char *b = ssid;
while (*a && *b && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
for (int j = 0; j < 6; j++) bssid[j] = g_scan_results[i].bssid[j];
*channel = g_scan_results[i].channel;
*sec = g_scan_results[i].security;
return 0;
}
}
return -1;
}
/* ---- 随机 SNonce(TSC 混合,实机 TLS 级随机由上层补充) ---- */
static void ath9k_make_snonce(void) {
u64 t = dkm_rdtsc();
for (int i = 0; i < 32; i++) {
t = t * 6364136223846793005ULL + 1442695040888963407ULL;
g_snonce[i] = (u8)(t >> ((i % 8) * 8));
}
}
/* ---- 连接入口 ---- */
static int ath9k_connect_impl(void *ctx, const char *ssid, const char *password) {
(void)ctx;
if (!ssid) return -1;
raw_log("[ath9k.raw] connect request\n");
if (!g_tx_ready) { raw_log("[ath9k.raw] TX not ready\n"); return -2; }
u8 sec = 0;
if (ath9k_find_ssid(ssid, g_ap_mac, &g_channel, &sec) != 0) {
raw_log("[ath9k.raw] connect: SSID not in scan results\n");
return -3; /* 调用方先扫描 */
}
u32 sl = 0;
while (ssid[sl] && sl < ATH9K_SSID_MAX) { g_target_ssid[sl] = ssid[sl]; sl++; }
g_target_ssid[sl] = 0;
copy_mac(g_sta_mac, g_mac);
/* 需要 WPA2-PSK 时先算 PMK */
if (sec != DKM_NET_SEC_OPEN) {
if (!password || !password[0]) { raw_log("[ath9k.raw] connect: empty PSK\n"); return -4; }
wpa2_pbkdf2_sha1(password, (const u8 *)g_target_ssid, sl, g_pmk);
raw_log("[ath9k.raw] PMK derived\n");
}
ath9k_set_channel(g_channel);
g_key_installed = 0;
g_ptk_replay = 0;
g_tx_pn = 0;
g_wpa_state = WPA_STATE_AUTH_SENT;
if (ath9k_send_auth() != 0) { g_wpa_state = WPA_STATE_IDLE; return -5; }
return 0;
}
static int ath9k_disconnect_impl(void *ctx) {
(void)ctx;
raw_log("[ath9k.raw] disconnect\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
/* ---- 管理/控制帧处理(连接状态机驱动) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
/* ath9k TX:把帧写进 TX 环并触发 DMA。
* 描述符位域按 AR9002 队列描述符最小集构造;实机对拍日志见下方 raw_hex。
* 上层把数据帧交给本驱动时,若尚未安装密钥(未连接)则 panic(ATH-E04)。 */
static int ath9k_tx_frame(const u8 *frame, u32 len) {
if (!g_tx_ready || !g_tx_desc) return -1;
if (len > ATH9K_TX_BUF_SIZE) return -1;
u32 idx = g_tx_head;
u8 *buf = g_tx_buf + (u64)idx * ATH9K_TX_BUF_SIZE;
for (u32 i = 0; i < len; i++) buf[i] = frame[i];
struct ath9k_tx_desc *d = &g_tx_desc[idx];
d->ds_data = (u32)(g_tx_buf_dma.phys + (u64)idx * ATH9K_TX_BUF_SIZE);
d->ds_ctl0 = (len & ATH9K_TXCTL0_LEN_M); /* FRAME_LEN,TXQ=0(BE) */
d->ds_ctl1 = (len & ATH9K_TXCTL1_LEN_M); /* BUF_LEN,无 MORE */
d->ds_ctl2 = 0;
d->ds_ctl3 = 0;
__asm__ volatile("" ::: "memory");
/* 触发 TX:写 TXDP 指向队首描述符并置 TXE */
ath9k_reg_write(AR_TXDP(0), (u32)g_tx_desc_dma.phys);
ath9k_reg_write(AR_TXCFG, AR_TXCFG_TXE);
g_tx_head = (g_tx_head + 1) % ATH9K_TX_DESC_COUNT;
raw_hex("[ath9k] tx kick len=", len);
return 0;
}
/* ---- 数据帧 TX(已连接后由 net 栈调用;CCMP 加密) ---- */
static int ath9k_tx_impl(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_key_installed) {
/* 未安装密钥时不允许数据帧上路:零降级 panic(ATH-E04) */
g_log->panic("ATH-E04 DATA TX BEFORE KEY INSTALL");
return -1;
}
static u8 f[ATH9K_TX_BUF_SIZE];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* toDS 数据帧头:A1=BSSID, A2=STA, A3=DA(未解析,用 BSSID 占位) */
u16 fc = 0x0100u | 0x0008u;
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
int rc = wpa2_ccmp_encrypt(f, sizeof(f), (const u8 *)packet, length,
g_ptk + WPA2_KCK_LEN + WPA2_KEK_LEN, ++g_tx_pn);
if (rc <= 0) return -1;
return ath9k_tx_frame(f, (u32)rc);
}
/* ---- 4-way 消息处理 ---- */
/* 定位 802.11 数据帧中的 EAPOL-Key(返回 EAPOL 起始偏移,-1 未找到) */
static int eapol_find(const u8 *frame, u32 len, u32 *out_off) {
/* 24B MAC 头 + 8B LLC/SNAP,可能带 8B CCMP 头(加密 EAPOL 场景) */
u32 off = 24;
if (len < off + 8) return -1;
if (frame[off] == 0xAA && frame[off+1] == 0xAA) {
/* 明文 EAPOL */
} else if (len >= off + 16) {
off += 8; /* 跳过 CCMP 头 */
if (frame[off] != 0xAA) return -1;
} else {
return -1;
}
if (off + 8 + 4 > len) return -1;
if (frame[off+6] != 0x88 || frame[off+7] != 0x8E) return -1; /* ethertype EAPOL */
if (frame[off+8] != EAPOL_VERSION || frame[off+9] != EAPOL_TYPE_KEY) return -1;
*out_off = off + 8;
return 0;
}
static void ath9k_handle_eapol(const u8 *eap, u32 len) {
/* eap 指向 EAPOL 头(version,type,length,然后 Key 体) */
if (len < 4 + 1 + 2 + 2 + 8 + 32 + 16 + 8 + 8 + 16 + 2) return;
const u8 *k = eap + 4; /* Key descriptor 起始 */
u16 key_info = (u16)k[1] | ((u16)k[2] << 8);
const u8 *nonce = k + 13; /* key nonce 偏移:1(desc)+2(info)+2(len)+8(replay) */
const u8 *mic = k + 13 + 32 + 16 + 8 + 8;
u16 kd_len = (u16)k[13 + 32 + 16 + 8 + 8 + 16] | ((u16)k[13+32+16+8+8+16+1] << 8);
const u8 *kd = mic + 16 + 2;
int pairwise = (key_info & WPA_KEY_INFO_KEY_TYPE) != 0;
int ack = (key_info & WPA_KEY_INFO_ACK) != 0;
int has_mic = (key_info & WPA_KEY_INFO_MIC) != 0;
if (!pairwise) {
/* GTK 消息(M3 附带),此处只做 MIC 校验后记录,实际 GTK 由 M3 的 key data 携带 */
raw_log("[ath9k.raw] EAPOL GTK msg\n");
return;
}
if (ack && !has_mic) {
/* ---- M1:带 ANonce ---- */
raw_log("[ath9k.raw] EAPOL M1 recv\n");
for (int i = 0; i < 32; i++) g_anonce[i] = nonce[i];
/* replay counter 8 字节(小端) */
g_ptk_replay = 0;
for (int i = 0; i < 8; i++) g_ptk_replay |= (u64)k[5 + i] << (i * 8);
ath9k_make_snonce();
ath9k_derive_ptk();
raw_log("[ath9k.raw] PTK derived\n");
g_wpa_state = WPA_STATE_M3_WAIT;
ath9k_send_m2();
return;
}
if (has_mic && !ack) {
/* ---- M3:校验 MIC → 安装 PTK/GTK → 回 M4 ---- */
raw_log("[ath9k.raw] EAPOL M3 recv\n");
u64 rc = 0;
for (int i = 0; i < 8; i++) rc |= (u64)k[5 + i] << (i * 8);
g_ptk_replay = rc;
/* MIC 校验:MIC 字段清零后对整段 EAPOL 计算 HMAC-SHA1-128 再比对。
* 用本地副本避免改写 RX 缓冲(const 语义)。 */
u32 mic_rel = (u32)(mic - eap);
u8 tmp[256];
if (len > sizeof(tmp)) { raw_log("[ath9k.raw] EAPOL too long\n"); return; }
for (u32 i = 0; i < len; i++) tmp[i] = eap[i];
for (int i = 0; i < 16; i++) tmp[mic_rel + i] = 0;
u8 calc[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, tmp, len, calc);
u8 diff = 0;
for (int i = 0; i < 16; i++) diff |= calc[i] ^ mic[i];
if (diff) {
raw_log("[ath9k.raw] M3 MIC MISMATCH (bad PSK?)\n");
g_wpa_state = WPA_STATE_IDLE;
return;
}
raw_log("[ath9k.raw] M3 MIC ok\n");
/* key data 中可能含 GTK(KDE 0xDD + OUI 00-0F-AC + type 1,AES key wrap)。
* 若含 ENC_KEY,用 KEK unwrap 取 GTK。 */
if ((key_info & WPA_KEY_INFO_ENC_KEY) && kd_len > 0) {
/* 遍历 KDE */
u32 p = 0;
while (p + 2 <= kd_len) {
u8 ktype = kd[p];
u8 klen = kd[p+1];
if (p + 2 + klen > kd_len) break;
if (ktype == 0xDD && klen >= 6 &&
kd[p+2] == 0x00 && kd[p+3] == 0x0F && kd[p+4] == 0xAC && kd[p+5] == 0x01) {
const u8 *wrapped = kd + p + 6;
u32 wlen = (u32)klen - 6;
u8 gtk[32];
if (wpa2_key_unwrap(g_ptk + WPA2_KCK_LEN, wrapped, wlen, gtk) == 0) {
raw_log("[ath9k.raw] GTK unwrapped\n");
} else {
raw_log("[ath9k.raw] GTK unwrap FAILED\n");
}
}
p += 2 + klen;
}
}
g_key_installed = 1;
g_wpa_state = WPA_STATE_CONNECTED;
ath9k_send_m4();
raw_log("[ath9k.raw] === CONNECTED (WPA2-PSK) ===\n");
}
}
/* ---- 管理帧处理(认证/关联响应) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len) {
if (len < 24) return -1;
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
if ((fctl & 0x000C) != 0x0000) return -1; /* 非管理帧 */
u16 subtype = fctl & 0x00F0;
/* 只处理来自目标 AP 的帧 */
for (int i = 0; i < 6; i++) if (frame[10+i] != g_ap_mac[i]) return -1;
if (subtype == 0x00B0) { /* Authentication */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+4] | ((u16)frame[24+5] << 8);
if (status != 0) { raw_log("[ath9k.raw] auth FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] auth ok\n");
if (g_wpa_state == WPA_STATE_AUTH_SENT) {
g_wpa_state = WPA_STATE_ASSOC_SENT;
ath9k_send_assoc();
}
return 0;
}
if (subtype == 0x0010) { /* Association Response */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+2] | ((u16)frame[24+3] << 8);
if (status != 0) { raw_log("[ath9k.raw] assoc FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] assoc ok, waiting 4-way\n");
g_wpa_state = WPA_STATE_M1_WAIT;
return 0;
}
if (subtype == 0x00C0) { /* Deauth */
raw_log("[ath9k.raw] deauth from AP\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
return -1;
}
/* ---- TX 环初始化 ---- */
static int ath9k_tx_init(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
g_dma = dma;
/* 描述符环 1 页 + 缓冲 1 页 */
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_desc_dma) != 0) {
raw_log("[ath9k.raw] tx_init: desc alloc failed\n");
return -2;
}
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_buf_dma) != 0) {
raw_log("[ath9k.raw] tx_init: buf alloc failed\n");
return -3;
}
g_tx_desc = (struct ath9k_tx_desc *)g_tx_desc_dma.virt;
g_tx_buf = (u8 *)g_tx_buf_dma.virt;
for (u32 i = 0; i < sizeof(struct ath9k_tx_desc) * ATH9K_TX_DESC_COUNT; i++)
((u8 *)g_tx_desc)[i] = 0;
for (u32 i = 0; i < ATH9K_TX_DESC_COUNT; i++) {
g_tx_desc[i].ds_link = (u32)(g_tx_desc_dma.phys +
(((i + 1) % ATH9K_TX_DESC_COUNT) * sizeof(struct ath9k_tx_desc)));
}
__asm__ volatile("" ::: "memory");
g_tx_head = 0;
g_tx_ready = 1;
raw_log("[ath9k.raw] tx_init: rings allocated\n");
return 0;
}
static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity;
if (out_length) *out_length = 0;
@@ -732,15 +1311,29 @@ int driver_init(const struct dkm_kernel_api *api,
return -5;
}
/* 9. RX 环初始化 */
/* 9. RX/TX 环初始化 */
if (api->dma && api->dma->alloc_pages) {
int rc = ath9k_rx_init(api->dma);
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
if (rc != 0) {
raw_log("[ath9k.raw] RX init failed, scan unavailable\n");
}
int trc = ath9k_tx_init(api->dma);
raw_hex("[ath9k.raw] tx_init rc=", (u64)(i8)trc);
if (trc != 0) raw_log("[ath9k.raw] TX init failed, connect unavailable\n");
} else {
raw_log("[ath9k.raw] no DMA api, scan 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. 注册中断 */
@@ -749,20 +1342,24 @@ int driver_init(const struct dkm_kernel_api *api,
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;
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
static const char devname[] = "ath9k0";
ndesc.name = devname;
copy_mac(ndesc.mac, g_mac);
ndesc.flags = DKM_NET_F_WIRELESS | (g_rx_ready ? DKM_NET_F_RX_READY : 0);
ndesc.flags = DKM_NET_F_WIRELESS |
(g_rx_ready ? DKM_NET_F_RX_READY : 0) |
(g_tx_ready ? DKM_NET_F_TX_READY : 0);
ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */
ndesc.tx = ath9k_tx_stub;
ndesc.tx = ath9k_tx_impl;
ndesc.rx_poll = ath9k_rx_poll_impl;
ndesc.scan_start = ath9k_scan_start_impl;
ndesc.scan_count = ath9k_scan_count_impl;
ndesc.scan_result = ath9k_scan_result_impl;
ndesc.is_wireless = ath9k_is_wireless_impl;
ndesc.connect = ath9k_connect_impl;
ndesc.disconnect = ath9k_disconnect_impl;
if (api->net && api->net->register_device) {
int rc = api->net->register_device(&ndesc);
+639
View File
@@ -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;
}
+67
View File
@@ -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
+58
View File
@@ -26,6 +26,26 @@ struct dkm_log_api {
void (*panic)(const char *msg);
};
/* D6 万物互联:dkm_bus 镜像(布局对齐 UTSM include/utsm/dkm.h) */
struct dkm_bus_service {
char name[48];
u32 version;
u32 flags;
void *fn;
const void *provider;
};
struct dkm_bus_api {
u32 magic;
u32 version;
int (*register_service)(const char *name, u32 version, void *fn);
int (*unregister_service)(const char *name);
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void);
const struct dkm_bus_service *(*at)(u32 index);
};
struct dkm_kernel_api {
u32 version;
u32 size;
@@ -47,6 +67,19 @@ struct dkm_kernel_api {
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
/* —— 尾部追加字段镜像(DKM_ABI v1;到 bus 为止) —— */
void *irq_register; /* int (*)(u8, void*) */
u64 hhdm_offset;
const void *block;
const void *mmio;
void *mm_map_mmio;
void *mm_unmap_mmio;
void *register_apic_eoi;
void *irq_vector_alloc;
void *irq_vector_free;
const void *sched;
const void *kapi;
const struct dkm_bus_api *bus;
};
struct dkm_driver_handle;
@@ -185,6 +218,31 @@ int driver_init(const struct dkm_kernel_api *api,
devfs_file("fb0", info);
}
/* /dev/services — D6 万物互联:总线已注册服务清单 */
if (api->bus && api->bus->magic == 0x53425553u /* "SBUS" */) {
char cnt[24];
cnt[0] = '0'; cnt[1] = 'x';
u64 v = api->bus->count();
u32 p = 2;
if (v == 0) cnt[p++] = '0';
else {
char r[18]; u32 q = 0;
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
while (q) cnt[p++] = r[--q];
}
cnt[p] = 0;
devfs_file("services", cnt);
g_log->info("[devfs] /dev/services/");
for (u32 i = 0; i < api->bus->count(); i++) {
const struct dkm_bus_service *s = api->bus->at(i);
if (s && s->name[0]) {
g_log->info("[devfs] ");
g_log->info(s->name);
}
}
}
/* /dev/modules — count from boot_modules */
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
+87
View File
@@ -0,0 +1,87 @@
/* dkm_instr.h — DKM 驱动专用插桩宏(无 UTSM 头文件依赖)
*
* 设计原则:
* - 使用 dkm_shared.h 已有的 dkm_rdtsc()/dkm_tsc_per_ms
* - 日志通过 dkm_kernel_api->log 输出
* - 统计计数器是 file-local static
* - 禁用时全部宏展开为空(零开销)
* - 需在 driver_init 开头调用 dkm_instr_init(api) 初始化
*
* 使用方式:
* 1. #include "../dkm_instr.h"
* 2. 在 driver_init 开头: dkm_instr_init(api);
* 3. 使用 DKM_STAT_INC / DKM_TS / DKM_LOG_TRACE 宏
*/
#ifndef DKM_INSTR_H
#define DKM_INSTR_H
#include "dkm_shared.h"
/* ---- 全局开关(dkm_instr.c 定义,每个 .drv 引用同一符号) ----
* 注意: DKM .drv 是 ET_REL 可重定位对象,每个驱动有独立的数据段副本。
* 因此 dkm_instr_global_enable 在每个 .drv 中独立控制,由
* dkm_instr_init() 在 driver_init 中根据 kernel_api 设置。
*/
static u32 dkm_instr_global_enable __attribute__((unused)) = 0;
/* ---- DKM_STAT: 轻量级计数器 ----
* DKM_STAT_DECL(name) — 在函数/文件作用域声明计数器
* DKM_STAT_INC(name) — 自增(仅当 enabled)
* DKM_STAT_DUMP(name, log_info_fn) — 输出当前计数值
*/
#define DKM_STAT_DECL(name) static u64 name##_count = 0
#define DKM_STAT_INC(name) do { \
if (dkm_instr_global_enable) { name##_count++; } \
} while(0)
#define DKM_STAT_DUMP(name, log_info_fn) do { \
if (dkm_instr_global_enable && name##_count > 0) { \
log_info_fn("[DKM:STAT] " #name " count="); \
/* 简单 u64 to hex 串口输出 */ \
static const char _hx[] = "0123456789abcdef"; \
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(name##_count>>((15-_i)*4))&0xf]; \
_buf[18]=0; log_info_fn(_buf); \
} \
} while(0)
/* ---- DKM_TS: 时间戳测量 ----
* DKM_TS_DECL(name) — 声明时间戳变量
* DKM_TS_BEGIN(name) — 记录起始 TSC
* DKM_TS_END(name, log_info_fn, label) — 计算耗时并输出
*/
#define DKM_TS_DECL(name) static u64 name##_ts = 0
#define DKM_TS_BEGIN(name) do { name##_ts = dkm_rdtsc(); } while(0)
#define DKM_TS_END(name, log_info_fn, label) do { \
if (dkm_instr_global_enable && dkm_tsc_per_ms) { \
u64 _d = dkm_rdtsc() - name##_ts; \
u64 _ms = _d / dkm_tsc_per_ms; \
log_info_fn("[DKM:TS] " label " elapsed_ms="); \
static const char _hx[] = "0123456789abcdef"; \
char _buf[19]; _buf[0]='0'; _buf[1]='x'; \
for(int _i=0;_i<16;_i++) _buf[2+_i]=_hx[(_ms>>((15-_i)*4))&0xf]; \
_buf[18]=0; log_info_fn(_buf); \
} \
} while(0)
/* ---- DKM_LOG_TRACE: 调试级别日志 ----
* 仅当插桩启用时通过 api->log->info 输出
*/
#define DKM_LOG_TRACE(log_api, msg) do { \
if (dkm_instr_global_enable && (log_api)) (log_api)->info(msg); \
} while(0)
/* ---- 初始化:读取 kernel_api 中的插桩标志 ----
* kernel_api->feature_bits bit0 = 插桩启用标志
* 由 UTSM dkm_fill_platform_info() 根据 FUCK debug.dkm_trace 设置
*/
static __inline__ void dkm_instr_init(const struct dkm_kernel_api *api) {
if (api && (api->feature_bits & 0x01)) {
dkm_instr_global_enable = 1;
}
}
#endif /* DKM_INSTR_H */
+35 -1
View File
@@ -107,6 +107,18 @@ struct dkm_net_device_desc {
int (*scan_count)(void *ctx);
int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(void *ctx);
/* 关联扩展(尾部追加,ABI 兼容;与 UTSM/include/utsm/net.h 布局一致)。 */
int (*connect)(void *ctx, const char *ssid, const char *password);
int (*disconnect)(void *ctx);
};
struct dkm_net_stats {
u64 tx_ok;
u64 tx_err;
u64 rx_ok;
u64 rx_err;
u64 rx_overflow;
u64 link_changes;
};
struct dkm_net_api {
@@ -119,6 +131,9 @@ struct dkm_net_api {
int (*scan_count)(u32 index);
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(u32 index);
int (*connect)(u32 index, const char *ssid, const char *password);
int (*disconnect)(u32 index);
int (*device_stats)(u32 index, struct dkm_net_stats *out);
};
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
@@ -143,6 +158,16 @@ struct dkm_block_api {
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
};
/* 与 UTSM/include/utsm/paging.h 中的 dkm_mmio_api 布局一致 */
struct dkm_mmio_api {
/* 四级页表 walk。返回 0=未映射;1=4K PTE;2=2M PDE;3=1G PDPTE */
int (*is_mapped)(u64 vaddr);
/* 把物理区间映射到 hhdm_offset + phys(4KiB UC 页),返回 0 成功 */
int (*map_mmio)(u64 phys, u64 size);
/* 当前 CR3 物理地址(诊断用) */
u64 (*cr3)(void);
};
/* 与 UTSM/include/utsm/dkm.h 中的 dkm_kernel_api 布局完全一致.
* 字段顺序/类型不可调整 — UTSM 端按此偏移填充. */
struct dkm_kernel_api {
@@ -168,7 +193,16 @@ struct dkm_kernel_api {
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
const struct dkm_block_api *block; /* 末尾字段, 不使用 block 的驱动可不访问 */
const struct dkm_block_api *block; /* 不使用 block 的驱动可不访问 */
const struct dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(HHDM 空洞原地补映射) */
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
/* B7 阶段2 尾部追加(旧内核无此字段 → 判空使用) */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3 尾部追加:动态 IDT 向量分配器(MSI/MSI-X),池 0x40-0xDF */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
};
struct dkm_driver_handle;
+450 -80
View File
@@ -1,9 +1,16 @@
/* DKM e1000 Driver — Intel 8254x Gigabit Ethernet
/* DKM e1000 Driver — Intel 8254x/8257x Gigabit Ethernet
* Stage 3, optional, depends on "pci" and "irq", provides "netdev".
* Finds e1000 via PCI, maps MMIO, reads MAC address.
* Supports classic e1000 (82540EM/82545/82546) and e1000e (82574L/I217/I218/I219).
* Finds device via PCI, maps MMIO, reads MAC (RAL/RAH or EEPROM),
* initializes RX/TX rings, provides link/speed/duplex diagnostics.
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(e1k_irq_count);
DKM_STAT_DECL(e1k_rx_packets);
DKM_STAT_DECL(e1k_tx_packets);
static const char *const g_depends[] = {"pci","irq"};
static const char *const g_provides[] = {"netdev"};
@@ -11,7 +18,7 @@ __attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="e1000",.version="0.1.0",.vendor="Deshab",
.name="e1000",.version="0.2.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=2,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
@@ -41,13 +48,33 @@ const struct dkm_driver_desc driver_desc = {
#define E1000_TXDCTL 0x3828
#define E1000_TCTL 0x0400
#define E1000_TIPG 0x0410
#define E1000_MANC 0x0580
/* STATUS register bits (link/speed/duplex diagnostic) */
#define E1000_STATUS_FD (1u << 0) /* full duplex */
#define E1000_STATUS_LU (1u << 1) /* link up */
#define E1000_STATUS_FUNC_SHIFT 2 /* PCI function number (ro) */
#define E1000_STATUS_TXOFF (1u << 4) /* transmitter paused */
#define E1000_STATUS_SPEEDMASK (3u << 6)
#define E1000_STATUS_SPEED_10 (0u << 6)
#define E1000_STATUS_SPEED_100 (1u << 6)
#define E1000_STATUS_SPEED_1000 (2u << 6) /* classic e1000 only */
/* CTRL register bits */
#define E1000_CTRL_FD (1u << 0)
#define E1000_CTRL_SLU (1u << 6)
#define E1000_CTRL_RST (1u << 26) /* software reset */
#define E1000_CTRL_ASDE (1u << 11) /* auto-speed detect enable */
#define E1000_CTRL_FRCSPD (1u << 12) /* force speed */
#define E1000_CTRL_FRCDPLX (1u << 13) /* force duplex */
/* e1000e (82574L/I217/I219) speed encoding in STATUS differs */
#define E1000_STATUS_SPEED_1000_E1000E (3u << 6)
#define E1000_RX_DESC_COUNT 8u
#define E1000_TX_DESC_COUNT 8u
#define E1000_RX_BUF_SIZE 2048u
#define E1000_TX_BUF_SIZE 2048u
#define E1000_POOL_BYTES(count, size) ((u32)((count) * (size)))
#define E1000_POOL_PAGES(bytes) (((bytes) + 4095u) / 4096u)
#define E1000_RCTL_EN (1u << 1)
#define E1000_RCTL_SBP (1u << 2)
@@ -67,8 +94,97 @@ const struct dkm_driver_desc driver_desc = {
#define E1000_TX_CMD_RS (1u << 3)
#define E1000_TX_STA_DD (1u << 0)
#define E1000_RX_STA_DD (1u << 0)
#define E1000_CTRL_FD (1u << 0)
#define E1000_CTRL_SLU (1u << 6)
/* ---- e1000 device ID table ---- */
/* 分类: 0=经典 e1000 (8254x), 1=e1000e (8257x/I217/I218/I219) */
struct e1000_device_id {
u16 did;
u8 family; /* 0=classic, 1=e1000e */
const char *name;
};
static const struct e1000_device_id g_e1000_ids[] = {
/* 经典 e1000 (8254x 系列) — EERD 接口兼容 */
{0x100E, 0, "82540EM"}, /* QEMU e1000 default */
{0x1004, 0, "82543GC"}, /* Copper */
{0x100F, 0, "82545EM"}, /* Copper */
{0x1010, 0, "82546EB"}, /* Copper dual-port */
{0x1011, 0, "82545EM-Fiber"},
{0x1012, 0, "82546EB-Fiber"},
{0x1013, 0, "82541EI"},
{0x1015, 0, "82540EM-LOM"},
{0x1016, 0, "82540EP"},
{0x1017, 0, "82540EP-LOM"},
{0x1018, 0, "82541EI-LOM"},
{0x1019, 0, "82547EI"},
{0x101A, 0, "82547EI-LOM"},
{0x101D, 0, "82546EB-Quad"},
{0x1026, 0, "82545GM"},
{0x1027, 0, "82545GM-Fiber"},
{0x1028, 0, "82545GM-SerDes"},
{0x1075, 0, "82547GI"},
{0x1076, 0, "82541GI"},
{0x1077, 0, "82541GI-LOM"},
{0x1078, 0, "82541ER"},
{0x1079, 0, "82546GB"},
{0x107A, 0, "82546GB-Fiber"},
{0x107B, 0, "82546GB-SerDes"},
{0x107C, 0, "82541GI-Mobile"},
{0x108B, 0, "82573E"},
{0x108C, 0, "82573E-IAMT"},
{0x109A, 0, "82573L"},
/* e1000e (8257x/I217/I218/I219) 系列 — 寄存器兼容但 EERD/STATUS 编码有差异 */
{0x10D3, 1, "82574L"}, /* QEMU e1000e default */
{0x10F5, 1, "82578DM"},
{0x10F6, 1, "82578DC"},
{0x1502, 1, "82579V"},
{0x1503, 1, "82579V-2"},
{0x1533, 1, "I217-V"},
{0x1534, 1, "I217-LM"},
{0x153A, 1, "I218-V"},
{0x153B, 1, "I218-LM"},
{0x156F, 1, "I219-V"},
{0x1570, 1, "I219-LM"},
{0x157B, 1, "I219-V-2"},
{0x157A, 1, "I219-LM-2"},
{0x15D3, 1, "I219-V-3"},
{0x15D8, 1, "I219-LM-3"},
{0x15E3, 1, "I219-V-4"},
{0x15D6, 1, "I219-V-5"},
{0x15BD, 1, "I219-V-6"},
{0x15B8, 1, "I219-LM-4"},
{0x15BB, 1, "I219-LM-5"},
{0x15DF, 1, "I219-LM-6"},
{0x15E0, 1, "I219-V-7"},
{0x15E1, 1, "I219-LM-7"},
{0x15E2, 1, "I219-LM-8"},
{0x15E4, 1, "I219-V-8"},
{0x15E5, 1, "I219-V-9"},
{0x15F9, 1, "I219-LM-9"},
{0x15FA, 1, "I219-V-10"},
{0x15FB, 1, "I219-LM-10"},
{0x15FC, 1, "I219-LM-11"},
{0x15FD, 1, "I219-V-11"},
{0x15FE, 1, "I219-LM-12"},
{0x15FF, 1, "I219-V-12"},
/* I210/I211 (igb 系列) — 与 e1000e MMIO 兼容 */
{0x1537, 1, "I210"},
{0x1536, 1, "I211-AT"},
/* 82575/82576/82580 (igb 系列, 多队列, 基本兼容) */
{0x10A7, 1, "82575EB"},
{0x10A9, 1, "82575EB-SerDes"},
{0x10C9, 1, "82576"},
{0x10E6, 1, "82576-Fiber"},
{0x10E7, 1, "82576-Quad"},
{0x150D, 1, "82580"},
{0x1516, 1, "82580-Fiber"},
{0x1526, 1, "82576-2"},
{0, 0, 0} /* 终止符 */
};
struct e1000_rx_desc {
u64 addr;
@@ -89,6 +205,36 @@ struct e1000_tx_desc {
u16 special;
} __attribute__((packed));
/* ---- 统计计数器(用于真机诊断) ---- */
static u64 g_stat_tx_ok; /* TX 成功帧数 */
static u64 g_stat_tx_err; /* TX 失败帧数 */
static u64 g_stat_tx_timeout; /* TX 超时次数 */
static u64 g_stat_rx_ok; /* RX 成功帧数 */
static u64 g_stat_rx_err; /* RX 错误帧数 */
static u64 g_stat_rx_overflow; /* RX buffer 太小丢包 */
static u64 g_stat_irq_count; /* IRQ 中断次数 */
/* 前向声明 */
static void raw_log(const char *s);
static void raw_hex(const char *prefix, u64 v);
/* 每 256 次数据路径事件打印一行摘要(避免刷串口)。 */
static void net_stat_tick(void) {
u64 sum = g_stat_tx_ok + g_stat_tx_err + g_stat_tx_timeout +
g_stat_rx_ok + g_stat_rx_err + g_stat_rx_overflow;
if ((sum & 0xFF) != 0) return;
raw_log("[e1000.raw] stat tx_ok=");
raw_hex("", g_stat_tx_ok);
raw_log("[e1000.raw] stat tx_err=");
raw_hex("", g_stat_tx_err);
raw_log("[e1000.raw] stat rx_ok=");
raw_hex("", g_stat_rx_ok);
raw_log("[e1000.raw] stat rx_err=");
raw_hex("", g_stat_rx_err);
raw_log("[e1000.raw] stat rx_overflow=");
raw_hex("", g_stat_rx_overflow);
}
static const struct dkm_log_api *g_log;
static volatile u32 *g_mmio;
static u64 g_bar0_phys;
@@ -103,6 +249,8 @@ static u64 g_tx_buf_phys[E1000_TX_DESC_COUNT];
static u32 g_rx_tail;
static u32 g_tx_tail;
static int g_rings_ready;
static u8 g_device_family; /* 0=classic e1000, 1=e1000e */
static u16 g_device_id; /* PCI device ID */
#define COM1 0x3F8
@@ -132,6 +280,23 @@ static void raw_hex(const char *prefix, u64 v) {
raw_log("\n");
}
/* 打印 MAC 地址 */
static void raw_mac(const char *prefix, const u8 mac[6]) {
static const char h[] = "0123456789abcdef";
raw_log(prefix);
for (int i = 0; i < 6; i++) {
serial_wait_tx();
dkm_outb(COM1, (u8)h[(mac[i] >> 4) & 0xf]);
serial_wait_tx();
dkm_outb(COM1, (u8)h[mac[i] & 0xf]);
if (i < 5) {
serial_wait_tx();
dkm_outb(COM1, ':');
}
}
raw_log("\n");
}
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
@@ -140,40 +305,165 @@ static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
dkm_pci_write(bus, dev, func, reg, v);
}
static int e1000_find(u8 *bus, u8 *dev) {
for (u8 d=0; d<32; d++) {
u32 vd = pci_read(0,d,0,PCI_VENDOR_ID);
if ((vd&0xffff)==0xffff) continue;
if ((vd&0xffff)!=0x8086) continue;
u16 did = (u16)(vd>>16);
/* 82540EM(0x100E) / 82545 等经典 e1000,以及 82574L(0x10D3) e1000e */
if (did==0x100E || did==0x10D3 || did==0x1004 || did==0x100F ||
did==0x10D3 || did==0x153A || did==0x1533) {
*bus=0; *dev=d; return 0;
/* 根据 PCI Device ID 查找设备信息 */
static const struct e1000_device_id *e1000_lookup_id(u16 did) {
for (int i = 0; g_e1000_ids[i].did != 0; i++) {
if (g_e1000_ids[i].did == did) return &g_e1000_ids[i];
}
return 0;
}
/* 多总线扫描 e1000 设备。真机网卡可能在 bus>0 上。 */
static int e1000_find(u8 *out_bus, u8 *out_dev, u8 *out_func,
const struct e1000_device_id **out_id) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
if ((vd & 0xffff) != 0x8086) continue;
/* 检查是否为多功能设备 */
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
if ((vd2 & 0xffff) != 0x8086) continue;
u16 did = (u16)(vd2 >> 16);
const struct e1000_device_id *id = e1000_lookup_id(did);
if (id) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
if (out_id) *out_id = id;
return 0;
}
}
}
}
return -1;
}
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。失败返回 0xffff。 */
/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。
* 经典 e1000: EERD bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位。
* e1000e: EERD 接口类似但地址字段位置不同 (addr<<2, bit1=START, bit2=DONE)。
* 失败返回 0xffff。 */
static u16 e1000_eeprom_read(u16 addr) {
/* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
/* 实机: 100ms TSC 超时 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 v = g_mmio[E1000_EERD/4];
if (v & (1u<<4)) return (u16)(v >> 16);
__asm__ volatile("pause");
if (g_device_family == 1) {
/* e1000e: EERD addr<<2 | START(bit1), DONE=bit2, data=bits[31:16] */
g_mmio[E1000_EERD/4] = ((u32)addr << 2) | 0x2u;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 v = g_mmio[E1000_EERD/4];
if (v & (1u << 2)) return (u16)(v >> 16);
__asm__ volatile("pause");
}
} else {
/* 经典 e1000: EERD addr<<8 | START(bit0), DONE=bit4 */
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (dkm_rdtsc() < deadline) {
u32 v = g_mmio[E1000_EERD/4];
if (v & (1u << 4)) return (u16)(v >> 16);
__asm__ volatile("pause");
}
}
return 0xffff;
}
/* 检测链路状态,返回 0=down, 1=up */
static int e1000_link_up(void) {
if (!g_mmio) return 0;
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_LU) ? 1 : 0;
}
/* 读取当前速度 (Mbps),返回 10/100/1000/0(未知) */
static u32 e1000_link_speed(void) {
if (!g_mmio) return 0;
u32 status = g_mmio[E1000_STATUS/4];
u32 speed_bits = status & E1000_STATUS_SPEEDMASK;
if (g_device_family == 1) {
/* e1000e speed encoding: 00=reserved, 01=100, 10=1000, 11=1000 */
if (speed_bits == E1000_STATUS_SPEED_1000_E1000E ||
speed_bits == (2u << 6))
return 1000;
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
return 0;
} else {
/* 经典 e1000 speed encoding: 00=10, 01=100, 10=1000 */
if (speed_bits == E1000_STATUS_SPEED_1000) return 1000;
if (speed_bits == E1000_STATUS_SPEED_100) return 100;
if (speed_bits == E1000_STATUS_SPEED_10) return 10;
return 0;
}
}
/* 读取当前双工模式,返回 1=全双工, 0=半双工 */
static int e1000_link_full_duplex(void) {
if (!g_mmio) return 0;
return (g_mmio[E1000_STATUS/4] & E1000_STATUS_FD) ? 1 : 0;
}
/* 打印链路诊断信息 */
static void e1000_log_link_status(void) {
int up = e1000_link_up();
if (up) {
u32 speed = e1000_link_speed();
int fd = e1000_link_full_duplex();
const char *speed_str = "unknown";
char speed_buf[8];
if (speed == 1000) speed_str = "1000";
else if (speed == 100) speed_str = "100";
else if (speed == 10) speed_str = "10";
else {
/* 手动转数字 */
speed_buf[0] = '0' + (char)(speed / 1000 % 10);
speed_buf[1] = '0' + (char)(speed / 100 % 10);
speed_buf[2] = '0' + (char)(speed / 10 % 10);
speed_buf[3] = '0' + (char)(speed % 10);
speed_buf[4] = 0;
speed_str = speed_buf;
}
raw_log("[e1000] link=up speed=");
raw_log(speed_str);
raw_log("Mbps duplex=");
raw_log(fd ? "full" : "half");
raw_log("\n");
} else {
raw_log("[e1000] link=down\n");
}
raw_hex("[e1000] STATUS=", g_mmio ? g_mmio[E1000_STATUS/4] : 0);
}
/* 等待链路就绪(最长 2 秒),实机 PHY 自动协商可能需要时间 */
static int e1000_wait_for_link(void) {
if (!g_mmio) return -1;
/* 设置 ASDE (Auto-Speed Detect Enable) 让 PHY 自动协商 */
if (g_device_family == 1) {
/* e1000e: 建议先清除 FRCSPD/FRCDPLX,让自动协商生效 */
u32 ctrl = g_mmio[E1000_CTRL/4];
ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPLX);
ctrl |= E1000_CTRL_SLU | E1000_CTRL_ASDE;
g_mmio[E1000_CTRL/4] = ctrl;
}
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
if (e1000_link_up()) return 0;
__asm__ volatile("pause");
}
return -1; /* 链路未就绪但驱动继续初始化(后续可能 link up) */
}
static int irq_handler(u8 irq) {
(void)irq;
if (!g_mmio) return 0;
u32 icr = g_mmio[E1000_ICR/4];
if (icr) {
g_stat_irq_count++;
DKM_STAT_INC(e1k_irq_count);
g_mmio[E1000_ICR/4] = icr; /* clear */
}
return 0;
@@ -228,7 +518,7 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) {
g_tx_desc[i].status = E1000_TX_STA_DD;
}
dma_fence();
raw_log("[e1000.raw] ring/buffers allocated\n");
raw_log("[e1000] ring/buffers allocated\n");
g_mmio[E1000_RCTL/4] = 0;
g_mmio[E1000_TCTL/4] = 0;
@@ -259,19 +549,23 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) {
mmio_flush();
g_rings_ready = 1;
raw_log("[e1000.raw] RX/TX rings initialized\n");
raw_log("[e1000] RX/TX rings initialized\n");
return 0;
}
static int e1000_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
raw_log("[e1000.raw] tx enter\n");
if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) return -1;
if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) {
g_stat_tx_err++;
return -1;
}
u32 index = g_tx_tail;
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) return -2;
if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) {
g_stat_tx_err++;
return -2; /* TX ring full */
}
copy_bytes(g_tx_buf[index], packet, length);
dma_fence();
raw_log("[e1000.raw] tx copied\n");
g_tx_desc[index].length = (u16)length;
g_tx_desc[index].cso = 0;
g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS;
@@ -287,17 +581,20 @@ static int e1000_tx(void *ctx, const void *packet, u32 length) {
while (dkm_rdtsc() < deadline) {
dma_fence();
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
raw_log("[e1000.raw] tx complete\n");
g_stat_tx_ok++;
DKM_STAT_INC(e1k_tx_packets);
net_stat_tick();
return 0;
}
__asm__ volatile("pause");
}
raw_log("[e1000.raw] tx timeout\n");
raw_hex("[e1000.raw] STATUS=", g_mmio[E1000_STATUS/4]);
raw_hex("[e1000.raw] TCTL=", g_mmio[E1000_TCTL/4]);
raw_hex("[e1000.raw] TDH=", g_mmio[E1000_TDH/4]);
raw_hex("[e1000.raw] TDT=", g_mmio[E1000_TDT/4]);
raw_hex("[e1000.raw] desc.status=", g_tx_desc[index].status);
g_stat_tx_timeout++;
g_stat_tx_err++;
raw_log("[e1000] tx timeout\n");
raw_hex("[e1000] STATUS=", g_mmio[E1000_STATUS/4]);
raw_hex("[e1000] TCTL=", g_mmio[E1000_TCTL/4]);
raw_hex("[e1000] TDH=", g_mmio[E1000_TDH/4]);
raw_hex("[e1000] TDT=", g_mmio[E1000_TDT/4]);
return -3;
}
@@ -307,9 +604,10 @@ static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length)
if (!g_rings_ready || !buffer || !out_length) return -1;
u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT;
dma_fence();
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1;
if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1; /* no data */
u32 len = g_rx_desc[next].length;
if (len > capacity) {
g_stat_rx_overflow++;
g_rx_desc[next].status = 0;
dma_fence();
g_rx_tail = next;
@@ -324,6 +622,9 @@ static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length)
g_rx_tail = next;
g_mmio[E1000_RDT/4] = g_rx_tail;
mmio_flush();
g_stat_rx_ok++;
DKM_STAT_INC(e1k_rx_packets);
net_stat_tick();
return 0;
}
@@ -332,58 +633,81 @@ int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
dkm_tsc_calibrate();
raw_log("[e1000.raw] driver_init entered\n");
raw_log("[e1000] driver_init entered\n");
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
raw_log("[e1000.raw] before find\n");
u8 bus, dev;
if (e1000_find(&bus,&dev)!=0) {
raw_log("[e1000.raw] device not found\n");
u8 bus, dev, func;
const struct e1000_device_id *dev_id = 0;
if (e1000_find(&bus, &dev, &func, &dev_id) != 0) {
raw_log("[e1000] device not found\n");
return 0;
}
raw_log("[e1000.raw] found device\n");
g_device_id = (u16)(pci_read(bus, dev, func, PCI_VENDOR_ID) >> 16);
g_device_family = dev_id ? dev_id->family : 0;
raw_log("[e1000] found device: ");
raw_log(dev_id ? dev_id->name : "unknown");
raw_log(g_device_family == 1 ? " (e1000e)" : " (classic)");
raw_log("\n");
raw_hex("[e1000] PCI bus:dev:func=", ((u64)bus << 16) | ((u64)dev << 8) | func);
u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND);
cmd |= 0x0007u; /* IO Space | Memory Space | Bus Master */
pci_write(bus, dev, 0, PCI_COMMAND, cmd);
raw_log("[e1000.raw] PCI command enabled\n");
/* 检查 PCI class code 确认是网络设备 (class=0x02) */
u32 class_reg = pci_read(bus, dev, func, 0x08);
u8 class_code = (u8)(class_reg >> 24);
if (class_code != 0x02) {
raw_log("[e1000] WARNING: PCI class is not Network\n");
}
u32 bar0 = pci_read(bus,dev,0,PCI_BAR0);
u32 cmd = pci_read(bus, dev, func, PCI_COMMAND);
cmd |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, cmd);
raw_log("[e1000] PCI command enabled\n");
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
if (bar0 & 1u) {
raw_log("[e1000.raw] IO BAR unsupported\n");
raw_log("[e1000] IO BAR unsupported\n");
return 0;
}
g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0ULL);
if ((bar0 & 0x6u) == 0x4u) {
u32 bar1 = pci_read(bus,dev,0,PCI_BAR1);
u32 bar1 = pci_read(bus, dev, func, PCI_BAR1);
g_bar0_phys |= ((u64)bar1 << 32);
}
raw_hex("[e1000.raw] BAR0 raw=", bar0);
raw_hex("[e1000.raw] BAR0 phys=", g_bar0_phys);
raw_hex("[e1000] BAR0 phys=", g_bar0_phys);
if (!g_bar0_phys) {
raw_log("[e1000.raw] BAR0 unavailable\n");
raw_log("[e1000] BAR0 unavailable\n");
return 0;
}
raw_log("[e1000.raw] BAR0 ok\n");
/* 真机 BAR0 可能在 4G 以上(如 I219),需 mm_map_mmio 检查 */
u64 hhdm = api->hhdm_offset;
g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys);
(void)g_mmio[E1000_STATUS/4];
raw_log("[e1000.raw] MMIO status read ok\n");
/* 尝试读取 STATUS 寄存器验证映射是否有效 */
u32 status_val = g_mmio[E1000_STATUS/4];
(void)status_val;
raw_log("[e1000] MMIO status read ok\n");
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26);
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_RST;
/* 实机: 1秒 TSC 超时等待复位完成 */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break;
if (!(g_mmio[E1000_CTRL/4] & E1000_CTRL_RST)) break;
__asm__ volatile("pause");
}
raw_log("[e1000.raw] reset done\n");
if (g_mmio[E1000_CTRL/4] & E1000_CTRL_RST) {
raw_log("[e1000] WARNING: reset did not complete\n");
} else {
raw_log("[e1000] reset done\n");
}
/* 等待 EEPROM 完成(实机复位后 EEPROM 状态机需要时间) */
dkm_delay_ms(1);
/* 读 MAC 地址:先尝试 RAL/RAH,空时走 EEPROM */
u32 ral = g_mmio[E1000_RAL/4];
u32 rah = g_mmio[E1000_RAH/4];
u8 mac[6];
@@ -403,60 +727,106 @@ int driver_init(const struct dkm_kernel_api *api,
mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8);
mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8);
mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8);
raw_log("[e1000] MAC from EEPROM\n");
} else {
raw_log("[e1000] EEPROM read failed (all 0xffff)\n");
}
} else {
raw_log("[e1000] MAC from RAL/RAH\n");
}
/* 仍为全 0 时使用与虚拟机配置一致的回退 MAC,保证收发地址匹配 */
if (mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) {
mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56;
raw_log("[e1000.raw] using fallback MAC 52:54:00:12:34:56\n");
/* 仍为全 0 或多播位错误时——严格错误策略插桩:MAC 读不到 = 网卡
* 身份不可知,零降级 panic(E1K-E01),不再用硬编码 MAC 冒充。 */
if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) ||
(mac[0] & 1)) {
raw_log("[e1000] MAC read failed (zero/multicast)\n");
g_log->panic("E1K-E01 MAC READ FAILED");
/* fallback:本地管理单播地址(bit1=LAA,bit0=0),"DESHB" */
mac[0]=0x02; mac[1]=0x44; mac[2]=0x45; mac[3]=0x53; mac[4]=0x48; mac[5]=0x42;
raw_log("[e1000] fallback: using locally administered MAC\n");
}
raw_mac("[e1000] MAC=", mac);
/* 写入 RAL/RAH 并置 AV(bit31) 使能接收该单播地址 */
g_mmio[E1000_RAL/4] = (u32)mac[0] | ((u32)mac[1]<<8) | ((u32)mac[2]<<16) | ((u32)mac[3]<<24);
g_mmio[E1000_RAH/4] = (u32)mac[4] | ((u32)mac[5]<<8) | (1u<<31);
raw_log("[e1000.raw] MAC read ok\n");
dma_fence();
u32 irq_line = pci_read(bus,dev,0,PCI_IRQ_LINE) & 0xff;
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
raw_hex("[e1000] IRQ line=", irq_line);
/* 设置链路:FD + SLU,并允许自动协商 */
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU;
raw_log("[e1000.raw] link setup\n");
mmio_flush();
g_mmio[E1000_IMC/4] = 0xffffffffu;
(void)g_mmio[E1000_ICR/4];
raw_log("[e1000.raw] interrupts masked\n");
raw_log("[e1000] interrupts masked\n");
/* 等待链路就绪(实机 PHY 自动协商可能需要 1-2 秒) */
int link_rc = e1000_wait_for_link();
if (link_rc == 0) {
raw_log("[e1000] link up after auto-negotiation\n");
} else {
raw_log("[e1000] link not yet up (will retry during operation)\n");
}
/* 打印链路诊断 */
e1000_log_link_status();
int ring_rc = e1000_init_rings(api->dma);
if (ring_rc != 0) raw_log("[e1000.raw] ring init failed\n");
if (ring_rc != 0) {
raw_log("[e1000] ring init failed\n");
raw_hex("[e1000] ring_rc=", (u64)(i32)ring_rc);
}
/* 根据链路状态设置 netdev flags */
u32 netdev_flags = 0;
if (e1000_link_up()) netdev_flags |= DKM_NET_F_LINK_UP;
if (g_rings_ready) netdev_flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "e1000";
for (int i=0;i<6;i++) netdev.mac[i] = mac[i];
netdev.flags = DKM_NET_F_LINK_UP;
if (g_rings_ready) netdev.flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
netdev.flags = netdev_flags;
netdev.ctx = 0;
netdev.tx = g_rings_ready ? e1000_tx : 0;
netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0;
int net_index = api->net->register_device(&netdev);
(void)net_index;
raw_log("[e1000.raw] netdev registered\n");
raw_hex("[e1000] netdev registered, index=", (u64)(i32)net_index);
} else {
raw_log("[e1000.raw] net API unavailable\n");
raw_log("[e1000] net API unavailable\n");
}
g_mmio[E1000_IMC/4] = 0xffffffffu;
if (api->irq_register && irq_line < 16) {
if (api->irq_register && irq_line < 16 && irq_line > 0) {
api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler);
raw_log("[e1000.raw] IRQ registered\n");
raw_log("[e1000] IRQ registered\n");
} else {
raw_log("[e1000] IRQ not registered (polling mode)\n");
}
raw_log("[e1000.raw] driver ready\n");
/* 最终诊断快照 */
e1000_log_link_status();
raw_log("[e1000] driver ready\n");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle; return 0;
(void)handle;
/* 关闭 RX/TX,释放 rings */
if (g_mmio) {
g_mmio[E1000_RCTL/4] = 0;
g_mmio[E1000_TCTL/4] = 0;
g_mmio[E1000_IMC/4] = 0xffffffffu;
mmio_flush();
}
g_rings_ready = 0;
g_mmio = 0;
return 0;
}
+1172
View File
@@ -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(&sectors, &blk) != 0) {
g_log->warn("[ehci] READ CAPACITY failed");
sectors = 0; blk = 512;
}
g_msc.sector_count = sectors;
g_msc.sector_size = blk;
g_msc.ready = 1;
DKM_STAT_INC(ehci_msc_found);
log_hex("[ehci] MSC device ready, sectors=", sectors);
log_hex("[ehci] MSC device block_size=", blk);
break;
}
}
/* 注册 block provider */
if (g_msc.ready && api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "usb2";
desc.sector_size = g_msc.sector_size;
desc.sector_count = g_msc.sector_count;
desc.ctx = 0;
desc.read = ehci_block_read;
desc.write = ehci_block_write;
int index = api->block->register_device(&desc);
log_hex("[ehci] block provider index=", (u64)(i64)index);
} else {
g_log->info("[ehci] no MSC device found; block provider not registered");
}
g_log->info("[ehci] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
if (g_mmio) {
mmio_write32(g_caplen + EHCI_OP_USBCMD,
mmio_read32(g_caplen + EHCI_OP_USBCMD) & ~USBCMD_RUN);
}
return 0;
}
+10 -52
View File
@@ -6,60 +6,15 @@
#include <stdint.h>
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(fat0_reads);
DKM_STAT_DECL(fat0_fat_walks);
/* ---------------------------------------------------------------
* DKM ABI types
* FAT32-specific types (DKM ABI types come from dkm_shared.h)
* --------------------------------------------------------------- */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_block_api {
int (*register_device)(const void *desc);
u32 (*device_count)(void);
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
int (*write)(u32 index, u64 lba, u32 count, const void *buffer);
u64 (*sector_size)(u32 index);
const char *(*device_name)(u32 index);
int (*set_write_fn)(u32 index, int (*fn)(void *ctx, u64 lba, u32 count, const void *buf));
};
struct dkm_kernel_api {
u32 version; u32 size; u64 feature_bits;
const struct dkm_log_api *log;
const void *mem,*utsm,*irq,*pci_api,*dma,*vfs_api,*net,*timer,*drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width,fb_height,fb_pitch; u16 fb_bpp;
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
const struct dkm_block_api *block;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic; u16 abi_version; u16 desc_size;
const char *name,*version,*vendor;
u32 driver_class,stage,flags,priority;
const char *const *depends; u32 depends_count;
const char *const *provides; u32 provides_count;
u64 min_kernel_abi,feature_bits,reserved0,reserved1;
};
struct limine_file {
u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56];
@@ -237,6 +192,7 @@ int driver_init(const struct dkm_kernel_api *api,
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
u64 disk_size = 0;
const u8 *disk = fat32_select_disk_image(api, &disk_size);
@@ -279,6 +235,7 @@ int driver_init(const struct dkm_kernel_api *api,
/* try to read README.TXT content */
if (strneq(dir[e].name,"README TXT",11)) {
DKM_STAT_INC(fat0_reads);
u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low);
u32 f_off = data_start + (f_clus - 2)*cluster_size;
u32 f_size = dir[e].file_size;
@@ -297,6 +254,7 @@ int driver_init(const struct dkm_kernel_api *api,
/* read next cluster from FAT */
u32 fat_ent_off = fat_start + clus * 4;
clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF;
DKM_STAT_INC(fat0_fat_walks);
}
g_log->info("[fat32] driver ready");
+721
View File
@@ -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; }
+1501 -207
View File
@@ -1,76 +1,36 @@
/* DKM NVMe Driver — PCI NVMe discovery and controller register enumeration
/* DKM NVMe Driver — 高位 BAR MMIO 映射 + admin/IO queue + block provider
*
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
* This first version only discovers NVMe controllers and reads BAR0 registers;
* it does not enable queues or submit admin commands.
*
* 实现要点:
* 1. 64-bit BAR 常位于 4G 以上(QEMU/OVMF 把 NVMe BAR0 放到 ~48GiB),
* HHDM 直映射不保证覆盖。访问前先用 kernel_api.mmio->is_mapped 做
* 四级页表 walk 核实;未映射则 map_mmio 原地补建 4KiB UC 页。
* 2. 完成模型: 单 outstanding 命令 + CQE phase bit。B7 阶段3 起 IO 队列
* 默认尝试 MSI/MSI-X 中断驱动(FUCK [drivers] nvme_msi, 默认 1 自动):
* 先 MSI cap 编程, 缺则 MSI-X (QEMU nvme 只暴露 MSI-X) — 表项
* addr=0xFEE00000|dest<<12, data=动态向量, entry unmask + cap Enable;
* Create IOCQ IEN=1/IV=0 + INTMC 解除 IV0; handler 摘 CQE 进完成槽、
* 推进 head、写 CQ doorbell, 自写 LAPIC EOI 并返回 1 (MSI 直投 LAPIC,
* 与 apic_route 开关无关, 不依赖阶段2 的 EOI 钩子注册状态)。
* 提交侧统一等待: 先查完成槽(中断路径), 再在 irqsave 临界区内手动
* 摘取(自适应轮询路径) — IF=0 (DSK 阶段) 或 MSI 未投递时零感知降级,
* 行为与旧版纯轮询一致。计数: g_msi_irq_count / g_msi_slot_hits /
* g_msi_poll_harvests / g_msi_timeouts, init 末尾与压测后打点。
* 3. 数据面: 32 页 (128KiB) 物理连续 DMA bounce buffer + 单页 PRP list。
* PRP 布局按传输页数分档: 1 页单 PRP1; 2 页 PRP2 直连第二页;
* >=3 页 PRP2 指向 PRP list 页 (512 项 u64, 上限 513 页 = 2052KiB)。
* chunk 上限 = min(buffer 页数, 2^MDTS 页, NLB 16-bit)。
* 读写一律 bounce: 调用方缓冲是任意虚拟地址 (DSK BSS / vmm dbuf),
* 物理底账未知, 统一经 DMA 缓冲中转, 一次命令读满 chunk 再 memcpy。
* WRITE 路径 memcpy 后 wbinvd (沿用 ahci BUG-P0-2 防陈旧 cache 先例),
* 每次写调用末尾随一个 FLUSH。admin/IO queue 各 64 项, 均每队列一页。
*/
#include <stdint.h>
#include "../dkm_shared.h"
#include "../dkm_instr.h"
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version;
u32 size;
u64 feature_bits;
const struct dkm_log_api *log;
const void *mem;
const void *utsm;
const void *irq;
const void *pci;
const void *dma;
const void *vfs;
const void *net;
const void *timer;
const void *drr;
const void *rsdp_address;
const void *fb_address;
u64 fb_width;
u64 fb_height;
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
u64 hhdm_offset;
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic;
u16 abi_version;
u16 desc_size;
const char *name;
const char *version;
const char *vendor;
u32 driver_class;
u32 stage;
u32 flags;
u32 priority;
const char *const *depends;
u32 depends_count;
const char *const *provides;
u32 provides_count;
u64 min_kernel_abi;
u64 feature_bits;
u64 reserved0;
u64 reserved1;
};
DKM_STAT_DECL(nvm0_pci_found);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
@@ -81,7 +41,7 @@ const struct dkm_driver_desc driver_desc = {
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "nvme",
.version = "0.1.0",
.version = "0.4.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
@@ -97,78 +57,87 @@ const struct dkm_driver_desc driver_desc = {
.reserved1 = 0
};
#define PCI_ADDR 0xCF8
#define PCI_DATA 0xCFC
#define PCI_VENDOR_ID 0x00
#define PCI_COMMAND 0x04
#define PCI_CLASS_REG 0x08
#define PCI_HEADER 0x0E
#define PCI_BAR0 0x10
#define PCI_BAR1 0x14
#define PCI_IRQ_LINE 0x3C
#define PCI_CMD_MEM (1u << 1)
#define PCI_CMD_BUSM (1u << 2)
/* ---- NVMe 控制器寄存器 ---- */
#define NVME_CAP 0x00 /* u64 capabilities */
#define NVME_VS 0x08 /* u32 version */
#define NVME_INTMS 0x0C /* interrupt mask set */
#define NVME_INTMC 0x10 /* interrupt mask clear */
#define NVME_CC 0x14 /* controller configuration */
#define NVME_CSTS 0x1C /* controller status */
#define NVME_AQA 0x24 /* admin queue attributes */
#define NVME_ASQ 0x28 /* u64 admin SQ base */
#define NVME_ACQ 0x30 /* u64 admin CQ base */
#define NVME_DB_BASE 0x1000 /* doorbell 起始偏移 */
#define NVME_CLASS_STORAGE 0x01
#define NVME_SUBCLASS_NVM 0x08
#define NVME_PROGIF_NVME 0x02
#define NVME_CAP 0x00
#define NVME_VS 0x08
#define NVME_INTMS 0x0C
#define NVME_INTMC 0x10
#define NVME_CC 0x14
#define NVME_CSTS 0x1C
#define NVME_NSSR 0x20
#define NVME_AQA 0x24
#define NVME_ASQ 0x28
#define NVME_ACQ 0x30
#define NVME_CMBLOC 0x38
#define NVME_CMBSZ 0x3C
/* admin 命令 opcode */
#define NVME_ADM_DELETE_IOSQ 0x00
#define NVME_ADM_CREATE_IOSQ 0x01
#define NVME_ADM_DELETE_IOCQ 0x04
#define NVME_ADM_CREATE_IOCQ 0x05
#define NVME_ADM_IDENTIFY 0x06
/* IO 命令 opcode(NVM 命令集: 00h=Flush, 01h=Write, 02h=Read) */
#define NVME_IO_FLUSH 0x00
#define NVME_IO_READ 0x02
#define NVME_IO_WRITE 0x01
#define ADMIN_QSIZE 64 /* entry 数(SQ 64*64B=4K 一页, CQ 64*16B=1K) */
#define IO_QSIZE 64
#define NVME_IDENT_BYTES 4096 /* identify 数据页 */
#define NVME_DATA_PAGES 32u /* IO bounce: 128KiB = 512B×256 扇区,
* 正好覆盖 DSK FAT32 的 256 扇区批量读 */
#define NVME_PRP_LIST_ENTRIES 512u /* 单 list 页 4KiB / 8B = 512 项 */
static const struct dkm_log_api *g_log;
static volatile u8 *g_regs;
static volatile u8 *g_regs; /* MMIO 基址(虚拟) */
static u32 g_db_stride; /* doorbell 步长 = 4 << CAP.DSTRD */
static u64 g_timeout_ms; /* CAP.TO * 500ms */
static __inline__ void outl(u16 port, u32 value) {
__asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port));
}
static struct dkm_dma_buffer g_asq; /* admin SQ */
static struct dkm_dma_buffer g_acq; /* admin CQ */
static struct dkm_dma_buffer g_ident; /* identify 数据页 */
static struct dkm_dma_buffer g_data; /* IO 读写 bounce (NVME_DATA_PAGES 页连续) */
static struct dkm_dma_buffer g_prp; /* PRP list 页 */
static struct dkm_dma_buffer g_iosq; /* IO SQ (qid=1) */
static struct dkm_dma_buffer g_iocq; /* IO CQ (qid=1) */
static __inline__ u32 inl(u16 port) {
u32 value;
__asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static u32 g_asq_tail;
static u32 g_acq_head;
static u32 g_acq_phase;
static u32 g_iosq_tail;
static u32 g_iocq_head;
static u32 g_iocq_phase;
static u16 g_cid;
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
u32 addr = (1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC);
outl(PCI_ADDR, addr);
return inl(PCI_DATA);
}
static u64 g_sector_size;
static u64 g_sector_count;
static u32 g_mdts; /* Identify Controller byte 77 */
static u32 g_chunk_max_sectors; /* 单命令扇区上限 (buffer/MDTS/NLB 取小) */
static int g_io_ready;
static int g_test_disk; /* LBA0 带 DESHABNVME0 签名的测试盘 */
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
u32 addr = (1u << 31)
| ((u32)bus << 16)
| ((u32)dev << 11)
| ((u32)func << 8)
| ((u32)reg & 0xFC);
outl(PCI_ADDR, addr);
outl(PCI_DATA, value);
}
/* ---- B7 阶段3: MSI 状态 ---- */
static int g_cfg_nvme_msi = 1; /* FUCK [drivers] nvme_msi, 默认 1 自动尝试 */
static int g_msi_enabled; /* MSI/MSI-X 编程成功且 handler 已注册 */
static int g_msix_mode; /* 1=MSI-X 表项模式, 0=MSI cap 模式 */
static int g_msi_vector = -1; /* 动态分配的 IDT 向量 (0x40-0xDF 池) */
/* 完成槽: 单 outstanding 前提下, handler 是唯一写者、提交侧是唯一读者,
* 且下一命令提交前不可能再有新 CQE, 故读-清无需互斥; valid 最后写/最先读,
* x86 TSO store-store / load-load 天然有序, nvme_mb 只挡编译器重排。 */
static volatile u32 g_slot_valid;
static volatile u32 g_slot_status; /* sc | (sct << 8), 0 = 成功 */
static volatile u32 g_slot_dw0; /* CQE dw0 (命令特定返回) */
static volatile u32 g_slot_cid; /* CQE dw3 低 16 位 CID, 提交侧比对防错位 */
static u64 g_msi_irq_count; /* MSI handler 进入次数 (投递证明) */
static u64 g_msi_slot_hits; /* 提交侧经完成槽完成的命令数 (中断路径) */
static u64 g_msi_poll_harvests; /* 提交侧 irqsave 手动摘取数 (轮询路径) */
static u64 g_msi_timeouts; /* 完成超时计数 (真故障, 非降级) */
static u64 g_msi_cid_mismatch; /* 摘到 CID 与提交 cid 不符的 CQE 计数 (队列错位警报) */
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_regs + off);
}
static u64 mmio_read64(u32 off) {
u32 lo = mmio_read32(off);
u32 hi = mmio_read32(off + 4);
return ((u64)hi << 32) | lo;
}
/* ---- 基础工具 ---- */
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
@@ -184,19 +153,126 @@ static void log_hex(const char *prefix, u64 value) {
g_log->info(buf);
}
/* 打印有界 ASCII 串(identify 数据中的 SN/MN 等定长字段) */
static void log_str(const char *prefix, const u8 *src, u32 len) {
char buf[48];
if (len > 40) len = 40;
for (u32 i = 0; i < len; i++) {
char c = (char)src[i];
buf[i] = (c >= 32 && c < 127) ? c : ' ';
}
buf[len] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void nvme_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static __inline__ void nvme_mb(void) {
/* x86 TSO 保证 store-store 顺序, 这里只需阻断编译器重排 */
__asm__ volatile("" ::: "memory");
}
/* 十进制打印 (MSI 计数器打点用, 比 hex 直观) */
static void log_dec(const char *prefix, u64 val) {
char buf[24];
u32 pos = 0;
if (!val) {
buf[pos++] = '0';
} else {
char tmp[20];
u32 n = 0;
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
while (n) buf[pos++] = tmp[--n];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static int memeq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
if (a[i] != b[i]) return 0;
}
return 1;
}
static u32 str_len(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 在 NUL 结尾字符串中查找子串 (boot module cmdline 匹配用) */
static int str_contains(const char *hay, const char *needle) {
u32 nlen = str_len(needle);
if (!nlen) return 1;
for (u32 i = 0; hay[i]; i++) {
u32 j = 0;
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
if (j == nlen) return 1;
}
return 0;
}
static void nvme_cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
__asm__ volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(leaf), "c"(0));
}
/* irqsave 临界区: 提交侧手动摘取 CQE 时屏蔽中断, 与 MSI handler 互斥。
* pushf/popf 配对恢复 IF — DSK 阶段 IF=0, 绝不能无条件 sti。 */
static __inline__ u64 nvme_irqsave(void) {
u64 flags;
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
return flags;
}
static __inline__ void nvme_irqrestore(u64 flags) {
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory", "cc");
}
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_regs + off);
}
static void mmio_write32(u32 off, u32 value) {
*(volatile u32 *)(void *)(g_regs + off) = value;
}
static u64 mmio_read64(u32 off) {
u32 lo = mmio_read32(off);
u32 hi = mmio_read32(off + 4);
return ((u64)hi << 32) | lo;
}
static void mmio_write64(u32 off, u64 value) {
mmio_write32(off, (u32)(value & 0xffffffffu));
mmio_write32(off + 4, (u32)(value >> 32));
}
static u32 sq_tail_db(u16 qid) { return NVME_DB_BASE + (2u * qid) * g_db_stride; }
static u32 cq_head_db(u16 qid) { return NVME_DB_BASE + (2u * qid + 1u) * g_db_stride; }
/* ---- PCI ---- */
static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
u32 vd = dkm_pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 header = (u8)(dkm_pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
u32 vd2 = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u32 class_reg = dkm_pci_read(bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
@@ -215,37 +291,1077 @@ static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
return -1;
}
static void log_nvme_version(u32 vs) {
log_hex("[nvme] version major=", (vs >> 16) & 0xffff);
log_hex("[nvme] version minor=", (vs >> 8) & 0xff);
log_hex("[nvme] version tertiary=", vs & 0xff);
/* ---- FUCK 配置自解析 (与 apic.c 同一范式, 不依赖 UTSM ini_parser) ----
* UTSM 经 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
* 此处只找 [drivers] 分区下的 nvme_msi 键; 缺失文件/键一律默认 1
* (自动尝试 MSI, 失败回轮询), 与无配置时行为一致。
*/
/* Limine boot module 最小类型 (与 apic.c / bootfs.c / UTSM limine.h 布局一致) */
struct limine_file_min {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response_min {
u64 revision;
u64 module_count;
struct limine_file_min **modules;
};
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
u64 i = start;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
u64 tok_start = i;
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
u64 tok_len = i - tok_start;
const char *tok = line + tok_start;
if (!tok_len) return defval;
if (tok_len == 1 && tok[0] == '1') return 1;
if (tok_len == 1 && tok[0] == '0') return 0;
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
int neg = 0;
u64 j = 0;
if (tok[0] == '-') { neg = 1; j = 1; }
int val = 0;
int any = 0;
for (; j < tok_len; j++) {
if (tok[j] < '0' || tok[j] > '9') return defval;
val = val * 10 + (tok[j] - '0');
any = 1;
}
if (!any) return defval;
return neg ? -val : val;
}
static void log_cap_fields(u64 cap) {
log_hex("[nvme] CAP.MQES=", cap & 0xffff);
log_hex("[nvme] CAP.CQR=", (cap >> 16) & 1);
log_hex("[nvme] CAP.AMS=", (cap >> 17) & 3);
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
log_hex("[nvme] CAP.NSSRS=", (cap >> 36) & 1);
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
static int fuck_get_int(const char *text, u64 size,
const char *section, const char *key, int defval) {
u64 pos = 0;
int in_section = 0;
u32 slen = str_len(section);
u32 klen = str_len(key);
while (pos < size) {
u64 end = pos;
while (end < size && text[end] != '\n') end++;
u64 line_len = end - pos;
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
const char *line = text + pos;
u64 i = 0;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
if (i < line_len) {
char c = line[i];
if (c == '#' || c == ';') {
/* 注释行, 跳过 */
} else if (c == '[') {
in_section = 0;
if (line_len >= (u64)(i + 1 + slen + 1) &&
memeq(line + i + 1, section, slen) &&
line[i + 1 + slen] == ']') {
in_section = 1;
}
} else if (in_section) {
if (line_len >= (u64)(i + klen) &&
memeq(line + i, key, klen)) {
u64 j = i + klen;
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
if (j < line_len && line[j] == '=') {
return parse_scalar(line, j + 1, line_len, defval);
}
}
}
}
pos = end + 1;
}
return defval;
}
static void nvme_load_config(const struct dkm_kernel_api *api) {
/* 编译期默认: 自动尝试 MSI (任何前提缺失都会回退纯轮询, 零回归) */
g_cfg_nvme_msi = 1;
const struct limine_module_response_min *resp =
(const struct limine_module_response_min *)api->boot_modules_response;
if (!resp || !resp->modules) {
g_log->warn("[nvme] no boot modules; FUCK defaults in effect");
return;
}
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file_min *f = resp->modules[i];
if (!f || !f->cmdline || !f->address || !f->size) continue;
if (!str_contains(f->cmdline, "fuck:config")) continue;
g_cfg_nvme_msi = fuck_get_int((const char *)f->address, f->size,
"drivers", "nvme_msi", 1);
g_log->info("[nvme] FUCK config module found");
log_dec("[nvme] cfg nvme_msi=", (u64)g_cfg_nvme_msi);
return;
}
g_log->warn("[nvme] FUCK module (fuck:config) not found; defaults in effect");
}
/* ---- CSTS 等待 ---- */
static int nvme_wait_csts(u32 mask, u32 expect, u64 timeout_ms) {
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
for (;;) {
u32 csts = mmio_read32(NVME_CSTS);
if (csts & (1u << 1)) { /* CFS: controller fatal status */
g_log->error("[nvme] CSTS.CFS set");
log_hex("[nvme] CSTS=", csts);
return -2;
}
if ((csts & mask) == expect) return 0;
if (dkm_rdtsc() > deadline) {
g_log->error("[nvme] CSTS wait timeout");
log_hex("[nvme] CSTS=", csts);
log_hex("[nvme] wait mask=", mask);
return -1;
}
__asm__ volatile("pause");
}
}
/* 前向声明: nvme_dump_regs 在 nvme_submit 中使用 */
static void nvme_dump_regs(const char *ctx);
/* ---- B7 阶段3: IO CQ 完成摘取 (MSI handler 与提交侧轮询共用) ----
* 返回 1 = 摘到一枚 CQE; 0 = 无新完成。
* out_cid 返回 CQE dw3 低 16 位的命令 ID, 供调用方与提交 cid 比对 —
* 单 outstanding 模型下 CQ 任何时刻只应有当前命令的一枚 CQE, CID 不匹配
* 即队列状态错位的硬证据, 必须显式报错而非静默当作当前命令完成
* (route=0 曾观测 stress readback MISMATCH @0x9000, 疑 CQE 提前消费)。
* 并发模型: 单生产者(控制器 DMA 写 CQ) + 单逻辑消费者, 消费者有两个入口 ——
* 1. MSI handler (IRQ 上下文): 同一 vector 在 EOI 前不会被 LAPIC 再次
* 投递, handler 对同向量天然不可重入, 无需自锁;
* 2. 提交侧统一等待 (主上下文): 必须在 nvme_irqsave 临界区内调用,
* 屏蔽中断期间 handler 不可能插入, 消除 "phase 判定后、head 推进前
* 被 handler 抢摘同一 CQE" 的双消费竞态。
* head 推进 + CQ doorbell 只在此函数内发生, 是两个入口的唯一汇合点。
*/
static int nvme_iocq_harvest(u32 *out_status, u32 *out_dw0, u32 *out_cid) {
volatile u32 *cq = (volatile u32 *)g_iocq.virt;
u32 dw3 = cq[g_iocq_head * 4 + 3];
if (((dw3 >> 16) & 1u) != g_iocq_phase) return 0;
if (out_dw0) *out_dw0 = cq[g_iocq_head * 4 + 0];
if (out_cid) *out_cid = dw3 & 0xffffu;
u32 sc = (dw3 >> 17) & 0xffu;
u32 sct = (dw3 >> 25) & 0x7u;
/* SC 与 SCT 联合编码 (与 nvme_submit 经典路径同一约定):
* SCT!=0 且 SC==0 时只返回 sc 会被误判成功。 */
*out_status = sc | (sct << 8);
g_iocq_head = (g_iocq_head + 1) % IO_QSIZE;
if (g_iocq_head == 0) g_iocq_phase ^= 1u;
nvme_mb(); /* head 先于 doorbell 可见 */
mmio_write32(cq_head_db(1), g_iocq_head);
return 1;
}
/* MSI 中断 handler: 摘 CQE 进完成槽。
* 返回 1 = 自负 LAPIC EOI (抑制 idt_handler 的钩子补 EOI)。
* 为何自写 EOI 而不依赖钩子: MSI 是设备向 LAPIC 直投的消息中断,
* 投递路径不经过 IOAPIC/PIC, 与 apic_route 开关无关; 但 EOI 钩子只在
* route=1 时注册, route=0 下钩子为 NULL 会漏写 EOI — LAPIC ISR 位
* 残留将永久阻塞同优先级后续中断, MSI 静默退化为 one-shot。
* handler 内直写 LAPIC EOI 寄存器后返回 1, 两种路由模式下行为一致。
* LAPIC 未软件使能时该写为无害 no-op (此时 MSI 本就不会投递)。
* 地址取 x86 默认 LAPIC base 0xFEE00000 + EOI 偏移 0xB0 (QEMU/MADT
* 一致; SAS-R0 恒等映射覆盖 4G 以下, apic.drv 已实证可直访)。
* 时序说明: 即使提交侧轮询抢先摘走 CQE, 控制器中断信号早已发出,
* 本次 handler 仍会执行并计入 g_msi_irq_count — 投递证据不受竞态影响。 */
static int nvme_msi_handler(u8 vector) {
(void)vector;
g_msi_irq_count++;
u32 st = 0, dw0 = 0, cid = 0;
if (nvme_iocq_harvest(&st, &dw0, &cid)) {
g_slot_status = st;
g_slot_dw0 = dw0;
g_slot_cid = cid;
nvme_mb(); /* payload 先于 valid 对提交侧可见 */
g_slot_valid = 1;
}
*(volatile u32 *)(uintptr_t)0xFEE000B0u = 0; /* LAPIC EOI */
return 1;
}
/* IO 命令统一完成等待 (MSI 模式): 完成槽(中断路径)优先, irqsave 手动摘取
* (自适应轮询路径)兜底。MSI 健康时 handler 在 CQE 落盘瞬间填槽; MSI 未投递
* (LAPIC 未启用/IF=0/信号丢失) 时手动摘取沿原 phase 语义直接完成, 调用方
* 零感知。超时语义与经典路径一致 (CAP.TO*500ms*10, 下限 5s)。 */
static int nvme_io_wait_completion(u16 cid, u32 opcode, u32 *out_cqe_dw0) {
u64 submit_timeout_ms = g_timeout_ms * 10;
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
for (;;) {
/* 中断路径: handler 已摘取 CQE 进完成槽 */
if (g_slot_valid) {
u32 st = g_slot_status;
u32 dw0 = g_slot_dw0;
u32 scid = g_slot_cid;
nvme_mb();
g_slot_valid = 0;
g_msi_slot_hits++;
if (scid != (u32)cid) {
/* CID 错位: 槽内 CQE 不属于本命令 — 队列状态已损,
* 显式报错并继续等真 CQE (超时兜底), 绝不静默当成功 */
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (slot)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", scid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed (msi slot)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
/* 自适应轮询路径: irqsave 临界区内手动摘取 (与 handler 互斥) */
u32 st = 0, dw0 = 0, hcid = 0;
u64 flags = nvme_irqsave();
int got = nvme_iocq_harvest(&st, &dw0, &hcid);
nvme_irqrestore(flags);
if (got) {
g_msi_poll_harvests++;
if (hcid != (u32)cid) {
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (poll)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", hcid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_msi_timeouts++;
g_log->error("[nvme] completion timeout (msi mode)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
nvme_dump_regs("completion timeout");
return -100;
}
__asm__ volatile("pause");
}
}
/* ---- 命令提交(同步轮询, 单 outstanding) ----
* dw[16] 为完整 SQE(dw0 低 16 位不含 CID, 由本函数填入)。
* 返回 0 成功;>0 = NVMe status code;<0 超时/内部错误。
*/
static int nvme_submit(volatile u32 *sq, u32 *sq_tail, u16 sq_qid,
volatile u32 *cq, u32 *cq_head, u32 *cq_phase,
u16 cq_qid, u32 qsize, u32 *dw, u32 *out_cqe_dw0) {
dw[0] = (dw[0] & 0xffffu) | ((u32)g_cid << 16);
u32 cid = g_cid;
g_cid++;
/* CID=0 保留不用: QEMU 在 MSI-X unmask 边界会向 IO CQ 补投一枚
* CID=0/phase 正确/status 成功的幽灵 CQE (实测稳定复现), g_cid 回绕
* 跳过 0 可保证该幽灵 CQE 永远被 CID 校验拒绝, 绝无误判窗口。 */
if (g_cid == 0) g_cid = 1;
/* 逐 dword 写入 SQE(64B),避免 freestanding 下编译器生成 memcpy 调用 */
u32 pos = *sq_tail;
for (u32 i = 0; i < 16; i++) sq[pos * 16 + i] = dw[i];
*sq_tail = (pos + 1) % qsize;
nvme_mb(); /* SQ 内容先于 doorbell 可见 */
mmio_write32(sq_tail_db(sq_qid), *sq_tail);
/* B7 阶段3: IO 队列 MSI 模式 → 完成槽 + irqsave 摘取统一等待。
* admin 队列保持纯轮询 (仅 init 期使用, 且 INTMC 解除前 IV0 处于
* 屏蔽态, admin 完成不会触发 MSI)。 */
if (cq_qid == 1 && g_msi_enabled) {
return nvme_io_wait_completion(cid, dw[0] & 0xffu, out_cqe_dw0);
}
/* 真机超时: 使用 CAP.TO * 500ms * 10 (留 10 倍余量), 下限 5s。
* 真机 NVMe 控制器在重负载下可能需要更长的完成时间。 */
u64 submit_timeout_ms = g_timeout_ms * 10;
if (submit_timeout_ms < 5000) submit_timeout_ms = 5000;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms;
for (;;) {
u32 dw3 = cq[*cq_head * 4 + 3];
if (((dw3 >> 16) & 1u) == *cq_phase) {
u32 sc = (dw3 >> 17) & 0xffu;
u32 sct = (dw3 >> 25) & 0x7u;
if (out_cqe_dw0) *out_cqe_dw0 = cq[*cq_head * 4 + 0];
*cq_head = (*cq_head + 1) % qsize;
if (*cq_head == 0) *cq_phase ^= 1u;
nvme_mb();
mmio_write32(cq_head_db(cq_qid), *cq_head);
if (sc || sct) {
g_log->error("[nvme] command failed");
log_hex("[nvme] opcode=", dw[0] & 0xffu);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] SC=", sc);
log_hex("[nvme] SCT=", sct);
/* SC 与 SCT 必须联合编码: SCT!=0 且 SC==0 时只返回 sc
* 会返回 0 被调用方误判为成功(CQID_INVALID 正是此类)。 */
return (int)(sc | (sct << 8));
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_log->error("[nvme] completion timeout");
log_hex("[nvme] opcode=", dw[0] & 0xffu);
nvme_dump_regs("completion timeout");
return -100;
}
__asm__ volatile("pause");
}
}
static int nvme_admin_submit(u32 *dw, u32 *out_cqe_dw0) {
return nvme_submit((volatile u32 *)g_asq.virt, &g_asq_tail, 0,
(volatile u32 *)g_acq.virt, &g_acq_head, &g_acq_phase,
0, ADMIN_QSIZE, dw, out_cqe_dw0);
}
static int nvme_io_submit(u32 *dw, u32 *out_cqe_dw0) {
return nvme_submit((volatile u32 *)g_iosq.virt, &g_iosq_tail, 1,
(volatile u32 *)g_iocq.virt, &g_iocq_head, &g_iocq_phase,
1, IO_QSIZE, dw, out_cqe_dw0);
}
/* ---- Identify ---- */
static int nvme_identify_controller(u32 *out_nn) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
dw[0] = NVME_ADM_IDENTIFY;
dw[1] = 0; /* NSID=0 for controller */
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
dw[7] = (u32)(g_ident.phys >> 32);
dw[10] = 1; /* CNS=1: identify controller */
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) return rc;
u8 *id = (u8 *)g_ident.virt;
log_str("[nvme] ctrl SN=", id + 4, 20);
log_str("[nvme] ctrl MN=", id + 24, 40);
log_str("[nvme] ctrl FR=", id + 64, 8);
/* Identify Controller 布局锚点核对(NVMe 1.4, CNS=1 数据结构):
* byte 77 MDTS u8 最大传输 = 2^MDTS × CAP.MPSMIN 页, 0=无限制
* byte 512 SQES/CQES/MAXCMD —— QEMU 实测 0x00004466, 证明偏移基正确
* byte 516 NN u32 Number of Namespaces
* NN 核实结论 (QEMU 11.0 串口实证): NN=0x100 不是解析错位。NN 的 spec
* 语义是"控制器支持的最大合法 NSID 数"而非已挂载数量; QEMU hw/nvme
* 填的是 NVME_MAX_NAMESPACES=256 (0x100)。实际挂载的 NSID=1 由后续
* Identify Namespace(CNS=0) 成功返回确认。解析偏移 516 与 spec 一致,
* 无需修正。 */
u32 anchor512 = (u32)id[512] | ((u32)id[513] << 8) |
((u32)id[514] << 16) | ((u32)id[515] << 24);
log_hex("[nvme] ID dword@512(SQES/CQES/MAXCMD)=", anchor512);
u32 nn = (u32)id[516] | ((u32)id[517] << 8) | ((u32)id[518] << 16) | ((u32)id[519] << 24);
log_hex("[nvme] NN(max valid NSID)=", nn);
log_hex("[nvme] MDTS=", id[77]);
g_mdts = id[77];
*out_nn = nn;
return 0;
}
static int nvme_identify_namespace(u32 nsid, u64 *out_nsze, u32 *out_lbads) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
nvme_zero(g_ident.virt, NVME_IDENT_BYTES);
dw[0] = NVME_ADM_IDENTIFY;
dw[1] = nsid;
dw[6] = (u32)(g_ident.phys & 0xffffffffu);
dw[7] = (u32)(g_ident.phys >> 32);
dw[10] = 0; /* CNS=0: identify namespace */
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) return rc;
u8 *id = (u8 *)g_ident.virt;
u64 nsze = 0;
for (u32 i = 0; i < 8; i++) nsze |= ((u64)id[i]) << (i * 8);
u8 flbas = id[26] & 0x0f;
u32 lbaf_off = 128 + (u32)flbas * 4;
u32 lbaf = (u32)id[lbaf_off] | ((u32)id[lbaf_off + 1] << 8) |
((u32)id[lbaf_off + 2] << 16) | ((u32)id[lbaf_off + 3] << 24);
u32 lbads = (lbaf >> 16) & 0xff;
log_hex("[nvme] NSZE=", nsze);
log_hex("[nvme] FLBAS=", flbas);
log_hex("[nvme] LBADS=", lbads);
*out_nsze = nsze;
*out_lbads = lbads;
return 0;
}
/* ---- B7 阶段3: PCI MSI 能力编程 ----
* MSI cap (ID 0x05) 布局:
* +0x00 u8 Cap ID (=0x05) +0x01 u8 Next Ptr
* +0x02 u16 Message Control: bit0 Enable, bit[3:1] MMC(可申请向量数-1),
* bit[6:4] MME(已分配向量数-1), bit7 64-bit Capable, bit8 PVM
* +0x04 u32 Message Address
* 64-bit 时: +0x08 u32 Message Upper Address, +0x0C u16 Message Data
* 32-bit 时: +0x08 u16 Message Data
* PVM(bit8)=1 时另有 Mask/Pending 寄存器, 复位值 0 (全部 unmask)。
* x86 LAPIC 投递公式 (physical destination, fixed delivery):
* Address = 0xFEE00000 | (dest_apic_id << 12) (RH=0, DM=0)
* Data = vector | (0<<8 delivery=fixed) (level/trigger 对 MSI 无效)
*/
#define PCI_CAP_ID_MSI 0x05
#define PCI_CAP_ID_MSIX 0x11
#define PCI_STATUS_CAPLIST 0x10u
static int nvme_find_pci_cap(u8 bus, u8 dev, u8 func, u8 cap_id) {
u32 reg04 = dkm_pci_read(bus, dev, func, PCI_COMMAND);
if (!((reg04 >> 16) & PCI_STATUS_CAPLIST)) return -1; /* Status.CapList=0 */
u8 ptr = (u8)(dkm_pci_read(bus, dev, func, PCI_CAP_PTR) & 0xFC);
int guard = 0;
while (ptr && guard++ < 64) { /* guard 防配置空间坏链死循环 */
u32 dw = dkm_pci_read(bus, dev, func, ptr);
u8 id = (u8)(dw & 0xff);
u8 next = (u8)((dw >> 8) & 0xff);
log_hex("[nvme] pci cap id=", id);
log_hex("[nvme] pci cap at=", ptr);
if (id == cap_id) return (int)ptr;
ptr = next & 0xFC;
}
return -1;
}
/* MSI cap (0x05) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* 失败路径完整回滚 (恢复 msg_ctl + 释放向量), 不留半编程状态。 */
static int nvme_msi_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSI);
if (cap < 0) {
g_log->info("[nvme] MSI capability not present");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
int cap64 = (int)((msg_ctl >> 7) & 1u);
int pvm = (int)((msg_ctl >> 8) & 1u);
log_hex("[nvme] MSI msg_ctl=", msg_ctl);
log_hex("[nvme] MSI 64bit=", (u64)cap64);
log_hex("[nvme] MSI pvm=", (u64)pvm);
/* 动态向量: UTSM 位图池 0x40-0xDF, 与静态向量 (tick 0xE0/spurious 0xFF)
* 及 legacy PIC 段 (0x20-0x2F) 无交叠。 */
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
g_log->warn("[nvme] MSI: vector alloc failed; polling");
return 0;
}
log_dec("[nvme] MSI vector=", (u64)vector);
/* 目的 APIC ID: CPUID.1:EBX[31:24] = BSP initial APIC ID (QEMU=0) */
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
log_hex("[nvme] MSI dest apic id=", dest);
u32 addr = 0xFEE00000u | (dest << 12);
u32 data = (u32)vector & 0xffu; /* fixed delivery, edge */
/* 先写 Address/Data, 最后置 Enable — 避免半成品消息被发出 */
dkm_pci_write(bus, dev, func, (u8)(cap + 4), addr);
if (cap64) {
dkm_pci_write(bus, dev, func, (u8)(cap + 8), 0); /* upper addr */
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 12));
dw = (dw & 0xffff0000u) | data; /* data 为低 u16 */
dkm_pci_write(bus, dev, func, (u8)(cap + 12), dw);
} else {
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 8));
dw = (dw & 0xffff0000u) | data;
dkm_pci_write(bus, dev, func, (u8)(cap + 8), dw);
}
/* Message Control: MME=0 (单向量), Enable=1; 低 u16 (id/next) 回写原值 */
u32 mc = msg_ctl;
mc &= ~(0x7u << 4); /* MME=0 → 1 vector */
mc |= 1u; /* MSI Enable */
cdw = (cdw & 0x0000ffffu) | (mc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* PVM: 显式清 mask bit0 (复位值即为 0, 此处防御性 RMW) */
if (pvm) {
int mask_off = cap64 ? cap + 16 : cap + 12;
u32 mask = dkm_pci_read(bus, dev, func, (u8)mask_off);
mask &= ~1u;
dkm_pci_write(bus, dev, func, (u8)mask_off, mask);
log_hex("[nvme] MSI mask cleared=", mask);
}
/* 回读验证: Enable 置位 + Address/Data 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_addr = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 rb_data = dkm_pci_read(bus, dev, func,
(u8)(cap64 ? cap + 12 : cap + 8)) & 0xffffu;
log_hex("[nvme] MSI readback ctl=", rb_ctl);
log_hex("[nvme] MSI readback addr=", rb_addr);
log_hex("[nvme] MSI readback data=", rb_data);
if (!(rb_ctl & 1u) || rb_addr != addr || (rb_data & 0xffu) != data) {
g_log->warn("[nvme] MSI: readback mismatch; rolling back");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
api->irq_vector_free(vector);
return 0;
}
/* 注册 handler (irq_register 已接受向量命名空间, >0x2F 直挂分发表) */
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI enabled (IO CQ IEN=1, IV=0)");
return 1;
}
/* 按 BIR 读取 BAR 物理基址 (处理 64-bit BAR; IO BAR 返回 0) */
static u64 nvme_bar_phys(u8 bus, u8 dev, u8 func, u32 bir) {
u8 reg = (u8)(PCI_BAR0 + bir * 4u);
u32 lo = dkm_pci_read(bus, dev, func, reg);
if (lo & 1u) return 0; /* IO BAR */
if ((lo & 0x6u) == 0x4u) { /* 64-bit MMIO BAR */
u32 hi = dkm_pci_read(bus, dev, func, (u8)(reg + 4));
return ((u64)hi << 32) | (u64)(lo & 0xFFFFFFF0u);
}
return (u64)(lo & 0xFFFFFFF0u);
}
/* MSI-X cap (0x11) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* QEMU nvme 只暴露 MSI-X (无 0x05), 本路径是 QEMU 下中断驱动化的唯一通道。
* 与 MSI 的差异仅在消息存储位置: MSI-X 的 addr/data 存放在 BAR 内的
* 表项 (16B/项), 需先把表区 MMIO 映射进 CPU 地址空间再编程 entry 0。
* 失败路径完整回滚 (清 Enable + 表项重新 mask + 释放向量)。 */
static int nvme_msix_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSIX);
if (cap < 0) {
g_log->warn("[nvme] MSI-X capability not present; polling");
return 0;
}
if (!api->mm_map_mmio) {
g_log->warn("[nvme] MSI-X: mm_map_mmio unavailable; polling");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
u32 table_size = (msg_ctl & 0x7ffu) + 1u; /* N-1 encoded */
u32 tbl_dw = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 bir = tbl_dw & 7u;
u32 tbl_off = tbl_dw & ~7u;
log_hex("[nvme] MSI-X msg_ctl=", msg_ctl);
log_dec("[nvme] MSI-X table size=", table_size);
log_hex("[nvme] MSI-X table BIR=", bir);
log_hex("[nvme] MSI-X table offset=", tbl_off);
if (bir > 5u) {
g_log->warn("[nvme] MSI-X: invalid BIR; polling");
return 0;
}
u64 bar_phys = nvme_bar_phys(bus, dev, func, bir);
if (!bar_phys) {
g_log->warn("[nvme] MSI-X: table BAR invalid; polling");
return 0;
}
u64 tbl_phys = bar_phys + tbl_off;
log_hex("[nvme] MSI-X table BAR phys=", bar_phys);
log_hex("[nvme] MSI-X table phys=", tbl_phys);
/* 映射表区首页 (含 entry0 的 16B)。mm_map_mmio 返回含页内偏移的指针,
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
if (!tbl) {
/* 严格错误策略插桩:MSI-X 表映射失败不允许静默转轮询(NVME-E01) */
g_log->error("[nvme] MSI-X: table MMIO map failed");
g_log->panic("NVME-E01 MSIX TABLE MAP FAILED");
return 0;
}
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
/* 严格错误策略插桩:向量分配失败不允许静默转轮询(NVME-E02) */
g_log->error("[nvme] MSI-X: vector alloc failed");
g_log->panic("NVME-E02 VECTOR ALLOC FAILED");
return 0;
}
log_dec("[nvme] MSI-X vector=", (u64)vector);
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
u32 addr = 0xFEE00000u | (dest << 12);
/* 先写表项 (addr/data/mask=0), 再置 cap Enable — 避免半成品消息 */
volatile u32 *e = tbl; /* entry 0 */
e[0] = addr; /* Msg Addr Lo */
e[1] = 0; /* Msg Addr Hi */
e[2] = (u32)vector & 0xffu; /* Msg Data = 向量 */
e[3] = 0; /* Vector Control: unmask */
nvme_mb();
u32 nc = (msg_ctl | 0x8000u) & ~(1u << 14); /* Enable=1, FunctionMask=0 */
cdw = (cdw & 0x0000ffffu) | (nc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* 回读验证: cap Enable + 表项 addr/data/mask 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_a0 = e[0], rb_a1 = e[1], rb_d = e[2], rb_vc = e[3];
log_hex("[nvme] MSI-X readback ctl=", rb_ctl);
log_hex("[nvme] MSI-X readback entry addr=", ((u64)rb_a1 << 32) | rb_a0);
log_hex("[nvme] MSI-X readback entry data=", rb_d);
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
/* 严格错误策略插桩:MSI-X 编程回读不符 = 硬件/虚拟机拒绝消息中断,
* 不允许静默回滚转轮询(NVME-E03)。 */
g_log->error("[nvme] MSI-X: readback mismatch");
g_log->panic("NVME-E03 MSIX READBACK MISMATCH");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
e[3] = 1; /* 表项重新 mask */
api->irq_vector_free(vector);
return 0;
}
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msix_mode = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI-X enabled (table entry0, IO CQ IEN=1, IV=0)");
return 1;
}
/* 中断驱动化入口: 先 MSI (0x05), 缺则 MSI-X (0x11), 皆不可则纯轮询。
* 前提检查集中在此; 两条编程路径各自保证失败完整回滚。 */
static int nvme_msi_setup(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
if (!g_cfg_nvme_msi) {
g_log->info("[nvme] MSI disabled by FUCK (nvme_msi=0); polling mode");
return 0;
}
if (!api->irq_vector_alloc || !api->irq_vector_free || !api->irq_register) {
g_log->warn("[nvme] MSI: kernel vector allocator unavailable; polling");
return 0;
}
if (nvme_msi_program(api, bus, dev, func)) return 1;
return nvme_msix_program(api, bus, dev, func);
}
/* ---- IO queue 创建 ---- */
static int nvme_create_io_queues(void) {
u32 dw[16];
/* Create IO CQ (qid=1): PC=1; MSI 就绪时 IEN=1 + IV=0
* CDW10 = QSIZE[31:16](0's based) | QID[15:0] —— QID 必须在低半字
* CDW11 = IV[31:16] | IEN[1] | PC[0] —— IV=0 与 INTMS/INTMC bit0 对应 */
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_ADM_CREATE_IOCQ;
dw[6] = (u32)(g_iocq.phys & 0xffffffffu);
dw[7] = (u32)(g_iocq.phys >> 32);
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
dw[11] = 1u;
if (g_msi_enabled) dw[11] = 1u | (1u << 1); /* PC=1, IEN=1, IV=0 */
log_hex("[nvme] create IOCQ CDW11=", dw[11]);
int rc = nvme_admin_submit(dw, 0);
if (rc != 0) {
g_log->error("[nvme] create IO CQ failed");
return rc;
}
/* Create IO SQ (qid=1, cqid=1): PC=1
* CDW10 同 Create IOCQ: QSIZE[31:16] | QID[15:0]; CDW11 = CQID[31:16] | PC */
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_ADM_CREATE_IOSQ;
dw[6] = (u32)(g_iosq.phys & 0xffffffffu);
dw[7] = (u32)(g_iosq.phys >> 32);
dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u;
dw[11] = (1u << 16) | 1;
rc = nvme_admin_submit(dw, 0);
if (rc != 0) {
g_log->error("[nvme] create IO SQ failed");
return rc;
}
g_iosq_tail = 0;
g_iocq_head = 0;
g_iocq_phase = 1;
return 0;
}
/* ---- IO Read/Write(PRP1 + PRP2 直连 / PRP list 分档) ----
* buf_phys 必须 4KiB 对齐且物理连续(本驱动只传 g_data.phys)。
* 传输页数由 nlb × sector_size 推出,决定 PRP 布局:
* 1 页 : PRP2 = 0(单 PRP1,等价旧 bounce 路径)
* 2 页 : PRP2 = 第二页物理地址(spec 规定的两页直连形式)
* >=3 页: PRP2 = g_prp list 页物理地址, list[i] = 第 i+2 页物理地址
* 单 list 页 512 项, 上限 513 页 (2052KiB), 远大于 chunk 上限 32 页,
* 无需 list 链页。PRP list 由 CPU 写、控制器 DMA 读: x86 TSO + 编译屏障
* 保证 list 先于 doorbell 可见(与 SQE 提交路径同一约定)。
*/
static int nvme_io_rw(u8 opcode, u64 slba, u32 nlb, u64 buf_phys) {
u32 bytes = nlb * (u32)g_sector_size;
u32 pages = (bytes + 4095u) >> 12;
if (pages == 0 || (pages - 1u) > NVME_PRP_LIST_ENTRIES) return -3;
u32 dw[16];
nvme_zero(dw, sizeof(dw));
dw[0] = opcode;
dw[1] = 1; /* nsid=1 */
dw[6] = (u32)(buf_phys & 0xffffffffu);
dw[7] = (u32)(buf_phys >> 32);
if (pages == 2) {
u64 p2 = buf_phys + 4096u;
dw[8] = (u32)(p2 & 0xffffffffu);
dw[9] = (u32)(p2 >> 32);
} else if (pages >= 3) {
volatile u64 *list = (volatile u64 *)g_prp.virt;
for (u32 i = 0; i < pages - 1u; i++)
list[i] = buf_phys + 4096ULL * (u64)(i + 1u);
nvme_mb(); /* list 内容先于 doorbell 可见 */
dw[8] = (u32)(g_prp.phys & 0xffffffffu);
dw[9] = (u32)(g_prp.phys >> 32);
}
dw[10] = (u32)(slba & 0xffffffffu);
dw[11] = (u32)(slba >> 32);
dw[12] = nlb - 1u; /* NLB 0-based */
return nvme_io_submit(dw, 0);
}
/* NVM Flush (opcode 0x00): 无数据字段, 把 volatile write cache 刷到介质 */
static int nvme_io_flush(void) {
u32 dw[16];
nvme_zero(dw, sizeof(dw));
dw[0] = NVME_IO_FLUSH;
dw[1] = 1; /* nsid=1 */
return nvme_io_submit(dw, 0);
}
/* ---- block provider ----
* 调用方缓冲为任意虚拟地址(DSK 静态 BSS、vmm virtio_blk dbuf 等),
* 物理底账未知且不一定连续/对齐,不能直接进 PRP。一律 bounce 经
* g_data(物理连续 DMA 区): 一次命令读/写满 chunk,再 memcpy 中转。
*/
static int nvme_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (!g_io_ready || !buffer || count == 0) return -1;
u8 *out = (u8 *)buffer;
while (count > 0) {
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
int rc = nvme_io_rw(NVME_IO_READ, lba, n, g_data.phys);
if (rc != 0) return rc;
u32 bytes = n * (u32)g_sector_size;
u8 *src = (u8 *)g_data.virt;
for (u32 i = 0; i < bytes; i++) out[i] = src[i];
out += bytes;
lba += n;
count -= n;
}
return 0;
}
static int nvme_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (!g_io_ready || !buffer || count == 0) return -1;
const u8 *in = (const u8 *)buffer;
while (count > 0) {
u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count;
u32 bytes = n * (u32)g_sector_size;
u8 *dst = (u8 *)g_data.virt;
for (u32 i = 0; i < bytes; i++) dst[i] = in[i];
/* 沿用 ahci BUG-P0-2 先例: 写前 flush CPU cache, 防 DMA 读到陈旧数据 */
__asm__ volatile("wbinvd" ::: "memory");
int rc = nvme_io_rw(NVME_IO_WRITE, lba, n, g_data.phys);
if (rc != 0) return rc;
in += bytes;
lba += n;
count -= n;
}
/* 每次写调用末尾 FLUSH 一次,保证 volatile write cache 落盘 */
return nvme_io_flush();
}
/* ---- 数据面自测(仅签名测试盘运行写测试,防实机盘数据损坏) ---- */
/* 读回 LBA0 并核对 DESHABNVME0 签名(写测试后复查无串扰用) */
static int nvme_lba0_sig_ok(void) {
static const char sig[] = "DESHABNVME0";
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
if (rc != 0) return 0;
u8 *d = (u8 *)g_data.virt;
for (u32 i = 0; i < 11; i++) {
if (d[i] != (u8)sig[i]) return 0;
}
return 1;
}
/* 多页单命令读: LBA0 起 32 扇区 = 16KiB = 4 页 → PRP list 路径。
* 测试盘内容已知: LBA0 为 "DESHABNVME0" 重复填满 512B, LBA1..31 全零。 */
static void nvme_selftest_read_multipage(void) {
static u8 rbuf[16384];
static const char sig[] = "DESHABNVME0";
int rc = nvme_block_read(0, 0, 32, rbuf);
log_hex("[nvme] PRPLIST read 32sec rc=", (u64)(i64)rc);
if (rc != 0) return;
u32 bad = 0;
for (u32 i = 0; i < 512; i++) {
if (rbuf[i] != (u8)sig[i % 11]) {
bad = 1;
log_hex("[nvme] sig mismatch idx=", i);
break;
}
}
if (!bad) {
for (u32 i = 512; i < 16384; i++) {
if (rbuf[i] != 0) {
bad = 1;
log_hex("[nvme] nonzero idx=", i);
break;
}
}
}
if (bad) {
g_log->error("[nvme] PRPLIST read verify FAILED");
} else {
g_log->info("[nvme] PRPLIST read verify OK (LBA0 sig + LBA1-31 zero)");
}
}
/* 写 → FLUSH → 读回逐字节校验。sectors=16 走 PRP2 直连(2页),
* sectors=32 走 PRP list(4页)。pattern 为首 8 字节 "NVMEWRIT" + 全缓冲
* 确定式伪随机,重跑幂等(与上次写入内容一致)。 */
static void nvme_selftest_rw(u64 lba, u32 sectors) {
static u8 wpat[16384];
static u8 rbuf[16384];
u32 bytes = sectors * (u32)g_sector_size;
if (bytes > sizeof(wpat)) return;
for (u32 i = 0; i < bytes; i++) wpat[i] = (u8)(i * 31u + 17u);
wpat[0] = 'N'; wpat[1] = 'V'; wpat[2] = 'M'; wpat[3] = 'E';
wpat[4] = 'W'; wpat[5] = 'R'; wpat[6] = 'I'; wpat[7] = 'T';
log_hex("[nvme] selftest rw lba=", lba);
log_hex("[nvme] selftest rw sectors=", sectors);
int rc = nvme_block_write(0, lba, sectors, wpat);
log_hex("[nvme] write+flush rc=", (u64)(i64)rc);
if (rc != 0) return;
for (u32 i = 0; i < bytes; i++) rbuf[i] = 0;
rc = nvme_block_read(0, lba, sectors, rbuf);
log_hex("[nvme] readback rc=", (u64)(i64)rc);
if (rc != 0) return;
for (u32 i = 0; i < bytes; i++) {
if (rbuf[i] != wpat[i]) {
g_log->error("[nvme] readback MISMATCH");
log_hex("[nvme] first bad idx=", i);
log_hex("[nvme] expect=", wpat[i]);
log_hex("[nvme] actual=", rbuf[i]);
return;
}
}
g_log->info("[nvme] write+flush+readback verify OK");
}
/* B7 阶段3 压测: 128 扇区 (64KiB, PRP list 路径) × 50 次 写+读。
* 统计总耗时与 MSI 计数器增量, 供中断/轮询两模式粗粒度对比。
* 仅签名测试盘运行 (写 LBA3000 起的 50 个块, 不碰 LBA0 签名区)。 */
static void nvme_selftest_stress(void) {
static u8 wbuf[65536];
static u8 rbuf[65536];
for (u32 i = 0; i < sizeof(wbuf); i++) wbuf[i] = (u8)(i * 17u + 3u);
u64 irq0 = g_msi_irq_count, slot0 = g_msi_slot_hits, poll0 = g_msi_poll_harvests;
u64 t0 = dkm_rdtsc();
u32 fails = 0;
for (u32 i = 0; i < 50; i++) {
u64 lba = 3000 + (u64)i * 128;
if (nvme_block_write(0, lba, 128, wbuf) != 0) { fails++; break; }
if (nvme_block_read(0, lba, 128, rbuf) != 0) { fails++; break; }
}
u64 elapsed_ms = dkm_tsc_per_ms ? (dkm_rdtsc() - t0) / dkm_tsc_per_ms : 0;
/* 末次读回全量校验 (数据通路在 MSI 模式下完整性证明) */
if (!fails) {
for (u32 i = 0; i < sizeof(wbuf); i++) {
if (rbuf[i] != wbuf[i]) {
g_log->error("[nvme] stress readback MISMATCH");
log_hex("[nvme] first bad idx=", i);
log_hex("[nvme] expect=", wbuf[i]);
log_hex("[nvme] actual=", rbuf[i]);
log_hex("[nvme] cq head=", g_iocq_head);
log_hex("[nvme] cq phase=", g_iocq_phase);
log_hex("[nvme] slot valid=", g_slot_valid);
fails++;
break;
}
}
}
log_dec("[nvme] stress 50x(128sec W+R) ms=", elapsed_ms);
log_dec("[nvme] stress fails=", fails);
log_dec("[nvme] stress irq delta=", g_msi_irq_count - irq0);
log_dec("[nvme] stress slot delta=", g_msi_slot_hits - slot0);
log_dec("[nvme] stress poll delta=", g_msi_poll_harvests - poll0);
if (!fails) g_log->info("[nvme] stress verify OK");
}
static void nvme_msi_stats_dump(const char *tag) {
g_log->info(tag);
log_dec("[nvme] msi enabled=", (u64)g_msi_enabled);
log_dec("[nvme] msix mode=", (u64)g_msix_mode);
log_dec("[nvme] msi vector=", (u64)(g_msi_vector < 0 ? 0 : g_msi_vector));
log_dec("[nvme] msi irq count=", g_msi_irq_count);
log_dec("[nvme] msi slot hits=", g_msi_slot_hits);
log_dec("[nvme] msi poll harvests=", g_msi_poll_harvests);
log_dec("[nvme] msi timeouts=", g_msi_timeouts);
log_dec("[nvme] msi cid mismatch=", g_msi_cid_mismatch);
}
/* ---- 真机诊断: dump 关键寄存器 ---- */
static void nvme_dump_regs(const char *ctx) {
g_log->info("[nvme] --- register dump: ");
g_log->info(ctx);
log_hex("[nvme] CAP =", mmio_read64(NVME_CAP));
log_hex("[nvme] VS =", mmio_read32(NVME_VS));
log_hex("[nvme] CC =", mmio_read32(NVME_CC));
log_hex("[nvme] CSTS =", mmio_read32(NVME_CSTS));
log_hex("[nvme] AQA =", mmio_read32(NVME_AQA));
log_hex("[nvme] ASQ =", mmio_read64(NVME_ASQ));
log_hex("[nvme] ACQ =", mmio_read64(NVME_ACQ));
/* doorbell 区域首个 entry 诊断 */
log_hex("[nvme] SQ0DB=", mmio_read32(NVME_DB_BASE));
log_hex("[nvme] CQ0DB=", mmio_read32(NVME_DB_BASE + g_db_stride));
}
/* ---- 高位 BAR MMIO 接入 ---- */
/* 真机 NVMe 控制器寄存器 + doorbell 空间可能远大于 8K。
* 典型实机: CAP.MQES 决定队列深度,doorbell 偏移 = DB_BASE + 2*QID*DSTRD*4,
* 需要映射足够大的范围覆盖至少 admin + 1 个 IO queue 对。
* 安全映射大小: 至少 64K(覆盖 QID 0-7 的 SQ/CQ doorbell)。 */
#define NVME_MAP_SIZE 0x10000
static volatile u8 *nvme_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
u64 bar_virt = api->hhdm_offset + bar_phys;
/* 优先使用 mm_map_mmio(独立窗口映射, PCD|PWT, 不与 HHDM 空洞竞争) */
if (api->mm_map_mmio) {
void *mapped = api->mm_map_mmio(bar_phys, NVME_MAP_SIZE);
if (mapped) {
g_log->info("[nvme] BAR mapped via mm_map_mmio window");
return (volatile u8 *)mapped;
}
g_log->warn("[nvme] mm_map_mmio failed; trying HHDM fallback");
}
if (!api->mmio || !api->mmio->is_mapped) {
/* 内核无页表服务: 仅当 BAR 位于低 4G(HHDM 经验上覆盖)时冒险直用 */
if (bar_phys < 0x100000000ULL) {
g_log->warn("[nvme] no mmio api; assume HHDM covers low BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
g_log->warn("[nvme] no mmio api and BAR above 4G; cannot access");
return (volatile u8 *)0;
}
log_hex("[nvme] CR3=", api->mmio->cr3 ? api->mmio->cr3() : 0);
/* 核实: HHDM 是否直接覆盖该 BAR(四级页表只读 walk, 无 #PF 风险) */
int level = api->mmio->is_mapped(bar_virt);
log_hex("[nvme] HHDM is_mapped(BAR)=", (u64)(i64)level);
if (level == 0) {
g_log->info("[nvme] HHDM does NOT cover BAR; mapping via page tables");
if (!api->mmio->map_mmio) return (volatile u8 *)0;
int rc = api->mmio->map_mmio(bar_phys, NVME_MAP_SIZE);
log_hex("[nvme] map_mmio rc=", (u64)(i64)rc);
if (rc != 0) return (volatile u8 *)0;
level = api->mmio->is_mapped(bar_virt);
log_hex("[nvme] post-map is_mapped=", (u64)(i64)level);
if (level == 0) {
g_log->error("[nvme] BAR still unmapped after map_mmio");
return (volatile u8 *)0;
}
} else {
g_log->info("[nvme] HHDM already covers BAR; direct access");
}
return (volatile u8 *)(uintptr_t)bar_virt;
}
/* ---- driver entry ---- */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[nvme] init begin");
nvme_load_config(api);
u8 bus = 0;
u8 dev = 0;
u8 func = 0;
u8 bus = 0, dev = 0, func = 0;
if (nvme_find(&bus, &dev, &func) != 0) {
g_log->warn("[nvme] NVMe controller not found");
g_log->info("[nvme] driver ready");
@@ -253,89 +1369,267 @@ int driver_init(const struct dkm_kernel_api *api,
}
g_log->info("[nvme] NVMe controller found");
DKM_STAT_INC(nvm0_pci_found);
log_hex("[nvme] bus=", bus);
log_hex("[nvme] dev=", dev);
log_hex("[nvme] func=", func);
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[nvme] vendor=", vd & 0xffff);
log_hex("[nvme] device=", vd >> 16);
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, command);
/* 启用 MEM + BUSM, 并禁用 INTx(轮询模式不需要中断) */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[nvme] PCI command=", command);
u32 bar0_lo = pci_read(bus, dev, func, PCI_BAR0);
u32 bar1_hi = pci_read(bus, dev, func, PCI_BAR1);
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
u64 bar_phys;
if (bar0_lo & 1u) {
g_log->warn("[nvme] BAR0 is IO space; unsupported");
g_log->info("[nvme] driver ready");
return 0;
}
if ((bar0_lo & 0x6u) == 0x4u) {
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
} else {
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
}
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[nvme] BAR0 phys=", bar_phys);
log_hex("[nvme] PCI IRQ line=", irq_line);
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[nvme] missing HHDM or BAR0; skip MMIO enumeration");
g_log->info("[nvme] driver ready");
return 0;
}
if (bar_phys >= 0x100000000ULL) {
g_log->warn("[nvme] BAR0 is above 4G; current HHDM MMIO map cannot safely read it");
g_log->info("[nvme] discovery only; MMIO enumeration skipped");
g_log->info("[nvme] driver ready");
return 0;
}
g_regs = (volatile u8 *)(uintptr_t)(api->hhdm_offset + bar_phys);
log_hex("[nvme] HHDM offset=", api->hhdm_offset);
u64 cap = mmio_read64(NVME_CAP);
u32 vs = mmio_read32(NVME_VS);
u32 intms = mmio_read32(NVME_INTMS);
u32 intmc = mmio_read32(NVME_INTMC);
u32 cc = mmio_read32(NVME_CC);
u32 csts = mmio_read32(NVME_CSTS);
u32 nssr = mmio_read32(NVME_NSSR);
u32 aqa = mmio_read32(NVME_AQA);
u64 asq = mmio_read64(NVME_ASQ);
u64 acq = mmio_read64(NVME_ACQ);
u32 cmbloc = mmio_read32(NVME_CMBLOC);
u32 cmbsz = mmio_read32(NVME_CMBSZ);
log_hex("[nvme] CAP=", cap);
log_cap_fields(cap);
log_hex("[nvme] VS=", vs);
log_nvme_version(vs);
log_hex("[nvme] INTMS=", intms);
log_hex("[nvme] INTMC=", intmc);
log_hex("[nvme] CC=", cc);
log_hex("[nvme] CSTS=", csts);
log_hex("[nvme] NSSR=", nssr);
log_hex("[nvme] AQA=", aqa);
log_hex("[nvme] ASQ=", asq);
log_hex("[nvme] ACQ=", acq);
log_hex("[nvme] CMBLOC=", cmbloc);
log_hex("[nvme] CMBSZ=", cmbsz);
if (cc & 1u) {
g_log->warn("[nvme] controller already enabled; leaving state unchanged");
} else {
g_log->info("[nvme] controller disabled; queues not configured");
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[nvme] missing HHDM or BAR0; skip");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] discovery only; admin queues disabled");
g_regs = nvme_map_bar(api, bar_phys);
if (!g_regs) {
g_log->error("[nvme] BAR MMIO unavailable; driver inactive");
g_log->info("[nvme] driver ready");
return 0;
}
/* ---- 寄存器枚举(验证 MMIO 可读) ---- */
u64 cap = mmio_read64(NVME_CAP);
u32 vs = mmio_read32(NVME_VS);
u32 cc = mmio_read32(NVME_CC);
u32 csts = mmio_read32(NVME_CSTS);
log_hex("[nvme] CAP=", cap);
log_hex("[nvme] VS=", vs);
log_hex("[nvme] CC=", cc);
log_hex("[nvme] CSTS=", csts);
log_hex("[nvme] CAP.MQES=", (cap & 0xffff) + 1);
log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf);
log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff);
log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff);
log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf);
log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf);
if (cap == 0 || cap == 0xFFFFFFFFFFFFFFFFULL) {
g_log->error("[nvme] CAP invalid; MMIO read broken");
g_log->info("[nvme] driver ready");
return 0;
}
if (!((cap >> 37) & 1u)) {
g_log->error("[nvme] NVM command set not supported");
g_log->info("[nvme] driver ready");
return 0;
}
g_db_stride = 4u << ((cap >> 32) & 0xf);
g_timeout_ms = ((cap >> 24) & 0xff) * 500;
if (!g_timeout_ms) g_timeout_ms = 500;
log_hex("[nvme] doorbell stride=", g_db_stride);
log_hex("[nvme] timeout ms=", g_timeout_ms);
if (!api->dma) {
g_log->warn("[nvme] DMA API unavailable; queues not configured");
g_log->info("[nvme] driver ready");
return 0;
}
/* ---- 分配队列与数据缓冲(低 4G, 4K 对齐, 清零, 物理连续) ---- */
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_asq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_acq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_ident) != 0) goto dma_fail;
if (api->dma->alloc_pages(NVME_DATA_PAGES, 4096, 0x100000000ULL, &g_data) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_prp) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iosq) != 0) goto dma_fail;
if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iocq) != 0) goto dma_fail;
goto dma_ok;
dma_fail:
g_log->error("[nvme] DMA alloc failed");
g_log->info("[nvme] driver ready");
return 0;
dma_ok:
log_hex("[nvme] ASQ phys=", g_asq.phys);
log_hex("[nvme] ACQ phys=", g_acq.phys);
log_hex("[nvme] DATA phys=", g_data.phys);
log_hex("[nvme] PRP list phys=", g_prp.phys);
log_hex("[nvme] IOSQ phys=", g_iosq.phys);
log_hex("[nvme] IOCQ phys=", g_iocq.phys);
/* ---- 控制器复位: EN=0 等 RDY=0 ---- */
mmio_write32(NVME_INTMS, 0xFFFFFFFFu); /* 屏蔽全部中断向量 */
cc = mmio_read32(NVME_CC);
if (cc & 1u) {
mmio_write32(NVME_CC, cc & ~1u);
}
if (nvme_wait_csts(1u, 0u, g_timeout_ms) != 0) {
g_log->error("[nvme] disable failed");
nvme_dump_regs("controller disable timeout");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] controller disabled");
/* ---- 配置 admin queue ---- */
mmio_write32(NVME_AQA, ((u32)(ADMIN_QSIZE - 1) << 16) | (u32)(ADMIN_QSIZE - 1));
mmio_write64(NVME_ASQ, g_asq.phys);
mmio_write64(NVME_ACQ, g_acq.phys);
g_asq_tail = 0;
g_acq_head = 0;
g_acq_phase = 1;
g_cid = 1;
/* ---- 使能: EN=1, CSS=NVM, MPS=4K, AMS=RR, IOSQES=6, IOCQES=4 ---- */
mmio_write32(NVME_CC, 0x00460001u);
if (nvme_wait_csts(1u, 1u, g_timeout_ms) != 0) {
g_log->error("[nvme] enable failed");
nvme_dump_regs("controller enable timeout");
g_log->info("[nvme] driver ready");
return 0;
}
g_log->info("[nvme] controller enabled, admin queue live");
/* ---- Identify ---- */
u32 nn = 0;
if (nvme_identify_controller(&nn) != 0) {
g_log->error("[nvme] identify controller failed");
g_log->info("[nvme] driver ready");
return 0;
}
if (nn == 0) {
g_log->warn("[nvme] no namespace");
g_log->info("[nvme] driver ready");
return 0;
}
u64 nsze = 0;
u32 lbads = 0;
if (nvme_identify_namespace(1, &nsze, &lbads) != 0) {
g_log->error("[nvme] identify namespace failed");
g_log->info("[nvme] driver ready");
return 0;
}
if (lbads < 9 || lbads > 12 || nsze == 0) {
g_log->warn("[nvme] unsupported LBA format or empty NS");
log_hex("[nvme] LBADS=", lbads);
g_log->info("[nvme] driver ready");
return 0;
}
g_sector_size = 1ULL << lbads;
g_sector_count = nsze;
log_hex("[nvme] sector size=", g_sector_size);
log_hex("[nvme] sector count=", g_sector_count);
/* ---- 单命令扇区上限: min(bounce 缓冲, 2^MDTS 页, NLB 16-bit) ----
* QEMU MDTS=7 → 512KiB, bounce 32 页=128KiB 更紧, 故 chunk=256 扇区,
* 恰好覆盖 DSK FAT32 的 256 扇区批量读(一次命令读满)。 */
{
u64 buf_bytes = (u64)NVME_DATA_PAGES * 4096u;
u64 mdts_bytes = g_mdts ? ((1ULL << g_mdts) * 4096u) : buf_bytes;
u64 cap = buf_bytes < mdts_bytes ? buf_bytes : mdts_bytes;
g_chunk_max_sectors = (u32)(cap / g_sector_size);
if (g_chunk_max_sectors > 65536u) g_chunk_max_sectors = 65536u;
if (g_chunk_max_sectors == 0) g_chunk_max_sectors = 1;
log_hex("[nvme] chunk max sectors=", g_chunk_max_sectors);
}
/* ---- B7 阶段3: IO queue 创建前尝试 MSI (任何前提缺失回退纯轮询) ---- */
nvme_msi_setup(api, bus, dev, func);
/* ---- IO queue ---- */
if (nvme_create_io_queues() != 0) {
g_log->error("[nvme] IO queue creation failed");
g_log->info("[nvme] driver ready");
return 0;
}
g_io_ready = 1;
g_log->info("[nvme] IO queue live");
/* MSI 就绪: INTMC 解除 IV0 屏蔽 (此前 IV0 保持屏蔽, admin 完成不产生
* MSI; 此后 IO CQ 每次完成经 MSI 投递到 g_msi_vector) */
if (g_msi_enabled) {
mmio_write32(NVME_INTMC, 1u);
log_hex("[nvme] INTMC unmask IV0, INTMS=", mmio_read32(NVME_INTMS));
}
/* ---- LBA0 读取验证 ---- */
{
int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys);
log_hex("[nvme] READ LBA0 rc=", (u64)(i64)rc);
if (rc == 0) {
u8 *d = (u8 *)g_data.virt;
u32 *w = (u32 *)g_data.virt;
log_hex("[nvme] LBA0 dword0=", w[0]);
log_hex("[nvme] LBA0 dword1=", w[1]);
log_hex("[nvme] LBA0 dword2=", w[2]);
log_hex("[nvme] LBA0 dword3=", w[3]);
static const char sig[] = "DESHABNVME0";
u32 match = 1;
for (u32 i = 0; i < 11; i++) {
if (d[i] != (u8)sig[i]) { match = 0; break; }
}
if (match) {
g_log->info("[nvme] LBA0 signature OK (DESHABNVME0)");
g_test_disk = 1;
} else {
g_log->info("[nvme] LBA0 data read (no test signature)");
}
}
}
/* ---- 注册 block provider(读 + 写) ---- */
if (api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "nvme0";
desc.sector_size = g_sector_size;
desc.sector_count = g_sector_count;
desc.ctx = 0;
desc.read = nvme_block_read;
desc.write = nvme_block_write;
int index = api->block->register_device(&desc);
log_hex("[nvme] block provider index=", (u64)(i64)index);
} else {
g_log->warn("[nvme] block API unavailable; provider not registered");
}
/* ---- 数据面自测: 写测试仅在签名测试盘上运行(防实机盘数据损坏) ---- */
if (g_test_disk) {
nvme_selftest_read_multipage(); /* 32 扇区 16KiB: PRP list 读 */
nvme_selftest_rw(1000, 16); /* 8KiB: PRP2 直连 (2 页) 写+读回 */
nvme_selftest_rw(2000, 32); /* 16KiB: PRP list (4 页) 写+读回 */
nvme_selftest_stress(); /* B7: 128 扇区×50 压测 + MSI 计数 */
if (nvme_lba0_sig_ok()) {
g_log->info("[nvme] LBA0 sig intact after write tests");
} else {
g_log->error("[nvme] LBA0 sig CORRUPTED after write tests");
}
} else {
g_log->info("[nvme] not a test disk; write self-test skipped");
}
nvme_msi_stats_dump("[nvme] --- MSI stats ---");
g_log->info("[nvme] driver ready");
return 0;
}
+177 -1
View File
@@ -1,6 +1,11 @@
/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC)
* Stage 0, required, depends on "irq", provides "pci".
* No IRQ needed for enumeration. Scans all buses/devices/functions.
*
* 真机增强:
* - 多总线扫描(桥接设备下游 bus > 0)
* - 网卡型号识别(常见 Intel/Realtek 网卡芯片自动标注)
* - PCI 桥检测和副总线号读取
*/
#include <stdint.h>
@@ -82,6 +87,15 @@ struct dkm_driver_desc {
#define PCI_HEADER_TYPE 0x0E
#define PCI_BAR0 0x10
/* PCI 桥配置 (header type 1) */
#define PCI_PRI_BUS 0x18
#define PCI_SEC_BUS 0x19
#define PCI_SUB_BUS 0x1A
#define PCI_CMD_IO (1u << 0)
#define PCI_CMD_MEM (1u << 1)
#define PCI_CMD_BUSM (1u << 2)
/* ---------------------------------------------------------------
* Driver descriptor
* --------------------------------------------------------------- */
@@ -94,7 +108,7 @@ const struct dkm_driver_desc driver_desc = {
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "pci",
.version = "0.1.0",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 2, /* DKM_CLASS_BUS */
.stage = 0,
@@ -164,6 +178,114 @@ static void log_hex(const char *prefix, u64 val) {
g_log->info(buf);
}
/* ---- 网卡型号识别 ---- */
struct netchip_id {
u16 vendor;
u16 device;
const char *name;
u8 driver_hint; /* 0=e1000, 1=e1000e, 2=r8169, 3=igb, 4=ath9k */
};
static const struct netchip_id g_netchips[] = {
/* Intel e1000 经典 (8254x) */
{0x8086, 0x100E, "82540EM", 0},
{0x8086, 0x1004, "82543GC", 0},
{0x8086, 0x100F, "82545EM", 0},
{0x8086, 0x1010, "82546EB", 0},
{0x8086, 0x1013, "82541EI", 0},
{0x8086, 0x1019, "82547EI", 0},
{0x8086, 0x1026, "82545GM", 0},
{0x8086, 0x1075, "82547GI", 0},
{0x8086, 0x1076, "82541GI", 0},
{0x8086, 0x1079, "82546GB", 0},
{0x8086, 0x108B, "82573E", 0},
{0x8086, 0x109A, "82573L", 0},
/* Intel e1000e (8257x/I217/I218/I219) */
{0x8086, 0x10D3, "82574L", 1},
{0x8086, 0x10F5, "82578DM", 1},
{0x8086, 0x10F6, "82578DC", 1},
{0x8086, 0x1502, "82579V", 1},
{0x8086, 0x1533, "I217-V", 1},
{0x8086, 0x1534, "I217-LM", 1},
{0x8086, 0x153A, "I218-V", 1},
{0x8086, 0x153B, "I218-LM", 1},
{0x8086, 0x156F, "I219-V", 1},
{0x8086, 0x1570, "I219-LM", 1},
{0x8086, 0x157B, "I219-V-2", 1},
{0x8086, 0x15D3, "I219-V-3", 1},
{0x8086, 0x15D8, "I219-LM-3", 1},
{0x8086, 0x15BD, "I219-V-6", 1},
/* Intel I210/I211 (igb) */
{0x8086, 0x1537, "I210", 3},
{0x8086, 0x1536, "I211-AT", 3},
/* Intel 82575/82576/82580 (igb) */
{0x8086, 0x10A7, "82575EB", 3},
{0x8086, 0x10C9, "82576", 3},
{0x8086, 0x150D, "82580", 3},
{0x8086, 0x1526, "82576-2", 3},
/* Realtek RTL8111/8168/8125 (r8169) — 常见桌面/笔记本有线网卡 */
{0x10EC, 0x8167, "RTL8111/8168B", 2},
{0x10EC, 0x8168, "RTL8111/8168", 2},
{0x10EC, 0x8169, "RTL8169", 2},
{0x10EC, 0x8125, "RTL8125", 2},
{0x10EC, 0x3000, "RTL8169S", 2},
{0x10EC, 0x8136, "RTL8101E", 2},
/* Qualcomm Atheros WiFi (ath9k) */
{0x168C, 0x0023, "AR5416", 4},
{0x168C, 0x0024, "AR5418", 4},
{0x168C, 0x0027, "AR9160", 4},
{0x168C, 0x0029, "AR9280", 4},
{0x168C, 0x002A, "AR9285", 4},
{0x168C, 0x002B, "AR9287", 4},
{0x168C, 0x002D, "AR9287-2", 4},
{0x168C, 0x002E, "AR9287-3", 4},
{0x168C, 0x0030, "AR9300", 4},
{0x168C, 0x0032, "AR9485", 4},
{0x168C, 0x0034, "AR9462", 4},
{0x168C, 0x0036, "AR9485-2", 4},
{0x168C, 0x0037, "AR1111", 4},
/* Broadcom WiFi — 目前无驱动,标注识别 */
{0x14E4, 0x4315, "BCM4312-WiFi", 0xFF},
{0x14E4, 0x4365, "BCM4365-WiFi", 0xFF},
{0x14E4, 0x43A0, "BCM4360-WiFi", 0xFF},
{0x14E4, 0x43C3, "BCM4366-WiFi", 0xFF},
/* Qualcomm WiFi — 无驱动 */
{0x168C, 0x003E, "QCA6174-WiFi", 0xFF},
{0x168C, 0x0042, "QCA6390-WiFi", 0xFF},
/* Intel WiFi — 无驱动 */
{0x8086, 0x24FD, "Intel-8265-WiFi", 0xFF},
{0x8086, 0x2526, "Intel-AX200-WiFi",0xFF},
{0x8086, 0x2725, "Intel-AX210-WiFi",0xFF},
{0x8086, 0x51F0, "Intel-AX211-WiFi",0xFF},
{0x8086, 0x7A70, "Intel-BE200-WiFi",0xFF},
{0, 0, 0, 0} /* 终止符 */
};
static const char *netchip_name(u16 vendor, u16 device) {
for (int i = 0; g_netchips[i].name != 0; i++) {
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
return g_netchips[i].name;
}
return 0;
}
static u8 netchip_driver(u16 vendor, u16 device) {
for (int i = 0; g_netchips[i].name != 0; i++) {
if (g_netchips[i].vendor == vendor && g_netchips[i].device == device)
return g_netchips[i].driver_hint;
}
return 0xFF;
}
static const char *pci_class_name(u8 base, u8 sub) {
switch (base) {
case 0x00: return "Legacy";
@@ -190,9 +312,26 @@ static const char *pci_class_name(u8 base, u8 sub) {
(void)sub;
}
/* PCI subclass names for Network class */
static const char *net_subclass_name(u8 sub) {
switch (sub) {
case 0x00: return "Ethernet";
case 0x01: return "TokenRing";
case 0x02: return "FDDI";
case 0x03: return "ATM";
case 0x04: return "ISDN";
case 0x05: return "WorldFIP";
case 0x06: return "PICMG";
case 0x80: return "Other";
default: return "Net-?";
}
}
/* ---------------------------------------------------------------
* Enumeration
* --------------------------------------------------------------- */
static u32 g_device_count;
static void pci_scan_bus(u8 bus) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID);
@@ -212,7 +351,9 @@ static void pci_scan_bus(u8 bus) {
u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS);
u8 class_code = (u8)(cc >> 24);
u8 subclass = (u8)((cc >> 16) & 0xff);
u8 prog_if = (u8)((cc >> 8) & 0xff);
g_device_count++;
g_log->info("[pci]");
log_hex("[pci] bus=", bus);
log_hex("[pci] dev=", dev);
@@ -223,6 +364,39 @@ static void pci_scan_bus(u8 bus) {
g_log->info(pci_class_name(class_code, subclass));
log_hex("[pci] class_hex=", class_code);
log_hex("[pci] subclass=", subclass);
/* 网卡自动识别与驱动匹配提示 */
if (class_code == 0x02) {
const char *chip_name = netchip_name(vendor2, device);
u8 drv = netchip_driver(vendor2, device);
g_log->info("[pci] NetChip");
if (chip_name) {
g_log->info(chip_name);
} else {
g_log->info("unknown");
}
g_log->info("[pci] NetSub");
g_log->info(net_subclass_name(subclass));
switch (drv) {
case 0: g_log->info("[pci] -> driver: e1000"); break;
case 1: g_log->info("[pci] -> driver: e1000 (e1000e compat)"); break;
case 2: g_log->info("[pci] -> driver: r8169 (NOT YET IMPLEMENTED)"); break;
case 3: g_log->info("[pci] -> driver: igb (e1000 compat)"); break;
case 4: g_log->info("[pci] -> driver: ath9k"); break;
case 0xFF: g_log->info("[pci] -> driver: NONE (unsupported)"); break;
default: break;
}
}
/* PCI-PCI 桥检测: 递归扫描下游总线 */
if (class_code == 0x06 && (subclass == 0x04 || subclass == 0x09)) {
u8 sec_bus = (u8)pci_cfg_read(bus, dev, func, PCI_SEC_BUS);
if (sec_bus > bus && sec_bus < 255) {
g_log->info("[pci] bridge -> secondary bus");
log_hex("[pci] sec_bus=", sec_bus);
pci_scan_bus(sec_bus);
}
}
}
}
}
@@ -238,8 +412,10 @@ int driver_init(const struct dkm_kernel_api *api,
if (!api || !api->log) return -1;
g_log = api->log;
g_device_count = 0;
g_log->info("[pci] scanning PCI bus");
pci_scan_bus(0);
log_hex("[pci] total devices=", g_device_count);
g_log->info("[pci] driver ready");
return 0;
}
+5 -36
View File
@@ -5,43 +5,10 @@
#include <stdint.h>
/* DKM ABI types */
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
#include "../dkm_shared.h"
#include "../dkm_instr.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define NULL ((void *)0)
struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
void (*error)(const char *msg);
void (*panic)(const char *msg);
};
struct dkm_kernel_api {
u32 version; u32 size; u64 feature_bits;
const struct dkm_log_api *log;
const void *mem,*utsm,*irq_api,*pci_api,*dma,*vfs_api,*net,*timer,*drr;
const void *rsdp_address,*fb_address;
u64 fb_width,fb_height,fb_pitch; u16 fb_bpp;
const void *boot_modules_response;
int (*irq_register)(u8 irq, void *handler);
};
struct dkm_driver_handle;
struct dkm_driver_desc {
u32 magic; u16 abi_version; u16 desc_size;
const char *name,*version,*vendor;
u32 driver_class,stage,flags,priority;
const char *const *depends; u32 depends_count;
const char *const *provides; u32 provides_count;
u64 min_kernel_abi,feature_bits,reserved0,reserved1;
};
DKM_STAT_DECL(ps2kbd_irq_count);
/* Driver descriptor */
static const char *const g_depends[] = {"irq"};
@@ -86,6 +53,7 @@ static int kbd_handler(u8 irq) {
(void)irq;
g_last_sc = inb(0x60);
g_has_key = 1;
DKM_STAT_INC(ps2kbd_irq_count);
return 0; /* let kernel send EOI */
}
@@ -95,6 +63,7 @@ int driver_init(const struct dkm_kernel_api *api,
(void)handle;
if (!api||!api->log) return -1;
g_log=api->log;
dkm_instr_init(api);
if (!api->irq_register) {
g_log->warn("[kbd] irq_register not available");
+713 -44
View File
@@ -1,7 +1,19 @@
/* DKM virtio-net Driver — PCI virtio network discovery
/* DKM virtio-net Driver — virtio network device (legacy/transitional PCI)
* Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev".
* This first version discovers virtio-net devices and logs PCI capabilities;
* it does not negotiate features, create virtqueues, or transmit packets.
*
* 数据路径实现(virtio spec 1.0 legacy interface,transitional device 1AF4:1001):
* - PCI 探测 → BAR0 legacy I/O 窗口
* - reset → ACKNOWLEDGE → DRIVER → feature negotiation(MAC/STATUS 子集)
* → FEATURES_OK → virtqueue 建立(RXQ0/TXQ1, legacy split-ring 布局)
* → DRIVER_OK
* - RX: 预填 32×2048B buffer 到 RXQ avail;rx_poll 收割 used ring,
* 剥 10B virtio_net_hdr 后把以太网帧交给上层协议栈,buffer 回收重填
* - TX: 8×2048B buffer 池(desc 索引与 buffer 1:1 映射),hdr+帧单描述符,
* QueueNotify kick,与 e1000 一致的同步等待完成(TSC 超时兜底)
* - boot 自测: 经真实 QEMU user-net 后端向 10.0.2.2 DHCP server 发 DISCOVER、
* 收 OFFER,一次跑通证明 TX/RX 全链路(DMA/notify/used ring)
* - IRQ 按 IDT vector(irq_line+0x20)注册做 ISR 统计;PIC 未解屏蔽时
* 自动退化为纯轮询,不依赖中断正确性
*/
#include "../dkm_shared.h"
@@ -10,11 +22,7 @@
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
#define PCI_VENDOR_VIRTIO 0x1AF4u
#define VIRTIO_TRANS_DEVICE_MIN 0x1000u
#define VIRTIO_TRANS_DEVICE_MAX 0x103Fu
#define VIRTIO_MODERN_DEVICE_MIN 0x1040u
#define VIRTIO_MODERN_DEVICE_MAX 0x107Fu
#define VIRTIO_NET_DEVICE_ID 1u
/* PCI device ID 约定见 virtio_is_net_device(): transitional net=0x1000, modern net=0x1041 */
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
@@ -22,6 +30,76 @@
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
#define VIRTIO_PCI_CAP_PCI_CFG 5u
/* ---- legacy I/O 寄存器偏移(virtio spec 1.0 §4.1.4, BAR0 I/O space) ---- */
#define VP_HOST_FEATURES 0x00u /* u32 RO: device feature bits 0..31 */
#define VP_GUEST_FEATURES 0x04u /* u32 WO: driver 接受的 feature 子集 */
#define VP_QUEUE_PFN 0x08u /* u32 WO: 队列物理地址 >> 12 */
#define VP_QUEUE_NUM 0x0Cu /* u16 RO: 队列容量(2 的幂) */
#define VP_QUEUE_SEL 0x0Eu /* u16 WO: 选择操作的队列 */
#define VP_QUEUE_NOTIFY 0x10u /* u16 WO: kick 指定队列 */
#define VP_STATUS 0x12u /* u8 RW: device status */
#define VP_ISR 0x13u /* u8 RO: ISR,读即清除 */
#define VP_NET_CFG_MAC 0x14u /* 6B RO: MAC(device config 区起始) */
/* ---- device status bits ---- */
#define VS_ACKNOWLEDGE 0x01u
#define VS_DRIVER 0x02u
#define VS_DRIVER_OK 0x04u
#define VS_FEATURES_OK 0x08u
/* ---- virtio-net feature bits(spec §5.1.6),只取最小子集 ---- */
#define VNET_F_MAC (1u << 5) /* device config 提供 MAC */
#define VNET_F_STATUS (1u << 16) /* device config 提供 link status */
/* ---- vring ---- */
#define VRING_DESC_F_WRITE 2u /* descriptor 由 device 写(RX) */
#define VNET_HDR_SIZE 10u /* 未协商 MRG_RXBUF → virtio_net_hdr 为 10 字节 */
#define RX_BUF_COUNT 32u /* RX 预填 buffer 数(不必填满整个队列) */
#define TX_BUF_COUNT 8u /* TX buffer 池大小 */
#define VNET_BUF_SIZE 2048u /* 10B hdr + 最大 1518B 以太网帧 */
/* split-ring 结构(legacy,x86 上字段为 guest 原生小端序) */
struct vring_desc {
u64 addr;
u32 len;
u16 flags;
u16 next;
};
struct vring_used_elem {
u32 id;
u32 len;
};
struct vring_avail {
u16 flags;
u16 idx;
u16 ring[];
};
struct vring_used {
u16 flags;
u16 idx;
struct vring_used_elem ring[];
};
/* 单个 virtqueue 的运行时视图。
* legacy 布局(spec §2.4.2):
* base+0 : descriptor table(16B × size)
* base+16*size : avail ring(4 + 2×size + 2B used_event)
* align4K(16*size + 6 + 2*size): used ring(4 + 8×size + 2B avail_event)
* 整段必须物理连续且 4KiB 对齐(QueuePFN 只接受页帧号)。 */
struct virtq {
u16 qsel; /* queue select 索引(0=RX, 1=TX) */
u16 size; /* device 报告的队列容量 */
u64 phys; /* 队列内存物理基址(页对齐) */
struct vring_desc *desc;
struct vring_avail *avail;
struct vring_used *used;
u16 last_used_idx; /* driver 侧 used ring 收割游标 */
};
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "netdev" };
@@ -31,7 +109,7 @@ const struct dkm_driver_desc driver_desc = {
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "virtio_net",
.version = "0.1.0",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 9, /* DKM_CLASS_NET */
.stage = 3,
@@ -49,6 +127,48 @@ const struct dkm_driver_desc driver_desc = {
static const struct dkm_log_api *g_log;
/* ---- 全局设备状态 ---- */
static u16 g_io_base; /* legacy I/O BAR 基址,0=未初始化 */
static struct virtq g_rxq;
static struct virtq g_txq;
static u8 *g_rx_buf[RX_BUF_COUNT];
static u64 g_rx_buf_phys[RX_BUF_COUNT];
static u8 *g_tx_buf[TX_BUF_COUNT];
static u64 g_tx_buf_phys[TX_BUF_COUNT];
static u8 g_tx_free[TX_BUF_COUNT]; /* 1=空闲可提交 */
static int g_ready; /* 数据路径就绪标志 */
static u8 g_mac[6];
/* ---- 统计计数器(串口可观测) ---- */
static u64 g_stat_tx_ok;
static u64 g_stat_tx_err;
static u64 g_stat_tx_timeout;
static u64 g_stat_rx_ok;
static u64 g_stat_rx_err;
static u64 g_stat_rx_overflow;
static u64 g_stat_irq_count;
/* ---- 自测缓冲区(静态 bss,避免内核栈压力) ---- */
static u8 g_st_frame[360]; /* DHCP DISCOVER 帧(342B) */
static u8 g_st_buf[2048]; /* RX 收割暂存 */
/* ---- 16-bit I/O port 访问(dkm_shared.h 只提供 8/32 位) ---- */
static __inline__ void vn_outw(u16 port, u16 value) {
__asm__ volatile ("outw %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u16 vn_inw(u16 port) {
u16 value;
__asm__ volatile ("inw %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
/* device DMA 内存屏障:描述符/ring 写入必须先于 idx 更新对设备可见。
* x86 store-store 有序,compiler fence + mfence 双保险(控制路径,开销可忽略)。 */
static __inline__ void vn_fence(void) {
__asm__ volatile("mfence" ::: "memory");
}
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
@@ -61,6 +181,17 @@ static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read8(bus, dev, func, reg);
}
static void copy_bytes(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
static void zero_bytes(void *dst, u32 len) {
u8 *d = (u8 *)dst;
for (u32 i = 0; i < len; i++) d[i] = 0;
}
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
@@ -75,14 +206,51 @@ static void log_hex(const char *prefix, u64 value) {
g_log->info(buf);
}
static u16 virtio_subsystem_id(u16 device_id) {
if (device_id >= VIRTIO_TRANS_DEVICE_MIN && device_id <= VIRTIO_TRANS_DEVICE_MAX) {
return (u16)(device_id - VIRTIO_TRANS_DEVICE_MIN);
static u32 dec_u32(u32 v, char *out) {
char tmp[10];
u32 n = 0;
if (v == 0) { out[0] = '0'; return 1; }
while (v && n < 10) { tmp[n++] = (char)('0' + (v % 10)); v /= 10; }
for (u32 i = 0; i < n; i++) out[i] = tmp[n - 1 - i];
return n;
}
/* 打印 IPv4 地址。ip 为主机序(高字节 = 第一个 octet,如 0x0A00020F = 10.0.2.15)。 */
static void log_ipv4(const char *prefix, u32 ip) {
char buf[24];
u32 p = 0;
for (u32 i = 0; i < 4; i++) {
u8 octet = (u8)(ip >> ((3 - i) * 8));
p += dec_u32(octet, buf + p);
if (i < 3) buf[p++] = '.';
}
if (device_id >= VIRTIO_MODERN_DEVICE_MIN && device_id <= VIRTIO_MODERN_DEVICE_MAX) {
return (u16)(device_id - VIRTIO_MODERN_DEVICE_MIN);
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void log_mac(const char *prefix, const u8 mac[6]) {
static const char hex[] = "0123456789abcdef";
char buf[18];
u32 p = 0;
for (u32 i = 0; i < 6; i++) {
buf[p++] = hex[(mac[i] >> 4) & 0xf];
buf[p++] = hex[mac[i] & 0xf];
if (i < 5) buf[p++] = ':';
}
return 0xffff;
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* virtio-net PCI device ID 判定。
* QEMU 编号约定(virtio 1.0 spec §4.1.2.1 + QEMU hw/virtio/virtio-pci.c):
* - transitional/legacy: 0x1000=net, 0x1001=blk, 0x1002=balloon, ...
* (历史 table,不是 0x1000+virtio_id;virtio device id 由 PCI Subsystem ID 携带)
* - modern-only: 0x1040+virtio_id → net = 0x1041
* class code (0x02=Network) 作为第二判据,与 virtio-blk(0x1001/Storage) 区分。 */
static int virtio_is_net_device(u16 device_id) {
return (device_id == 0x1000u || device_id == 0x1041u) ? 1 : 0;
}
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
@@ -99,10 +267,9 @@ static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
u16 device = (u16)(vd2 >> 16);
if (vendor != PCI_VENDOR_VIRTIO) continue;
u16 virtio_id = virtio_subsystem_id(device);
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 class_code = (u8)(class_reg >> 24);
if (virtio_id == VIRTIO_NET_DEVICE_ID && class_code == 0x02) {
if (virtio_is_net_device(device) && class_code == 0x02) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
@@ -178,28 +345,457 @@ static void log_virtio_caps(u8 bus, u8 dev, u8 func) {
}
}
/* ---- tx/rx_poll stub: 安全占位, 不实际收发, 防止 netdev 调用 NULL ---- */
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
(void)packet;
/* 仅记录日志, 避免每次发包刷屏: 仅记录长度 */
static u32 s_tx_log_guard = 0;
if (s_tx_log_guard < 4) {
g_log->info("[virtio_net] tx stub: packet dropped (virtqueues not enabled)");
log_hex("[virtio_net] tx stub len=", length);
s_tx_log_guard++;
/* ================= legacy device 初始化序列 ================= */
/* device status 寄存器读写(8 位) */
static void vdev_set_status(u8 status) {
dkm_outb((u16)(g_io_base + VP_STATUS), status);
}
static u8 vdev_get_status(void) {
return dkm_inb((u16)(g_io_base + VP_STATUS));
}
/* reset → ACKNOWLEDGE → DRIVER → feature 协商 → FEATURES_OK。
* 返回 0 成功;失败返回负数(调用方回退 stub 注册)。 */
static int vdev_negotiate(void) {
/* 1. reset: 写 0 并等待设备读回 0(spec §4.1.4.3 要求轮询确认) */
vdev_set_status(0);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (vdev_get_status() != 0) {
if (dkm_rdtsc() > deadline) {
g_log->error("[virtio_net] device reset timeout");
return -1;
}
__asm__ volatile("pause");
}
/* 2/3. ACKNOWLEDGE → DRIVER(status 位按序累积写入) */
vdev_set_status(VS_ACKNOWLEDGE);
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER);
/* 4. feature negotiation: 只取 MAC + STATUS 子集。
* 不接受 MRG_RXBUF/CTRL_VQ/CSUM/GSO 等复杂特性 →
* virtio_net_hdr 固定 10 字节、无控制队列、无卸载,数据路径最简。 */
u32 dev_feat = dkm_inl((u16)(g_io_base + VP_HOST_FEATURES));
log_hex("[virtio_net] device features=", dev_feat);
u32 subset = dev_feat & (VNET_F_MAC | VNET_F_STATUS);
dkm_outl((u16)(g_io_base + VP_GUEST_FEATURES), subset);
log_hex("[virtio_net] guest features=", subset);
/* 5. FEATURES_OK 并回读确认(spec §3.1.1)。
* 纯 legacy(pre-1.0)设备没有该位概念;QEMU transitional 实现 1.0 语义会置位。
* 未置位时按 legacy 兼容策略 warn 后继续(与 Linux legacy 驱动一致)。 */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK);
u8 st = vdev_get_status();
log_hex("[virtio_net] status after FEATURES_OK=", st);
if (!(st & VS_FEATURES_OK)) {
g_log->warn("[virtio_net] FEATURES_OK not echoed; continuing as pre-1.0 legacy device");
} else {
g_log->info("[virtio_net] FEATURES_OK confirmed");
}
return 0;
}
/* QueuePFN = 物理地址 >> 12(legacy 队列地址寄存器语义) */
static void vn_outl_pfn(const struct virtq *q) {
dkm_outl((u16)(g_io_base + VP_QUEUE_PFN), (u32)(q->phys >> 12));
}
/* 建立单个 virtqueue:读容量 → 计算 legacy 布局 → 分配连续页 → 写 QueuePFN。 */
static int vq_setup(const struct dkm_dma_api *dma, struct virtq *q, u16 qsel) {
q->qsel = qsel;
q->last_used_idx = 0;
vn_outw((u16)(g_io_base + VP_QUEUE_SEL), qsel);
q->size = vn_inw((u16)(g_io_base + VP_QUEUE_NUM));
if (q->size == 0) {
g_log->error("[virtio_net] queue size is 0");
return -1;
}
/* legacy 布局(spec §2.4.2):
* desc @ 0,avail @ 16*size,used @ 4KiB 对齐边界,整段物理连续。 */
u64 avail_off = 16ULL * q->size;
u64 used_off = (avail_off + 6ULL + 2ULL * q->size + 4095ULL) & ~4095ULL;
u64 total = used_off + 6ULL + 8ULL * q->size;
u64 pages = (total + 4095ULL) / 4096ULL;
struct dkm_dma_buffer buf;
/* 低 4G + 页对齐:QueuePFN 只接受 phys>>12,alloc_pages 返回已清零页 */
if (dma->alloc_pages(pages, 4096, 0x100000000ULL, &buf) != 0) {
g_log->error("[virtio_net] queue memory alloc failed");
return -2;
}
q->phys = buf.phys;
q->desc = (struct vring_desc *)buf.virt;
q->avail = (struct vring_avail *)((u8 *)buf.virt + avail_off);
q->used = (struct vring_used *)((u8 *)buf.virt + used_off);
vn_outl_pfn(q);
return 0;
}
/* kick 队列:写 queue index 到 QueueNotify */
static void vq_notify(const struct virtq *q) {
vn_outw((u16)(g_io_base + VP_QUEUE_NOTIFY), q->qsel);
}
/* 把描述符挂到 avail ring 并推进 idx。
* 顺序约束:desc 字段必须先写好(调用方责任),ring 写入 → fence → idx 推进。 */
static void vq_push_avail(struct virtq *q, u16 desc_idx) {
u16 slot = (u16)(q->avail->idx % q->size);
q->avail->ring[slot] = desc_idx;
vn_fence();
q->avail->idx = (u16)(q->avail->idx + 1u);
vn_fence();
}
/* 从 used ring 取一个完成项。返回 0=取到,1=空。 */
static int vq_pop_used(struct virtq *q, u16 *out_id, u32 *out_len) {
vn_fence();
if (q->used->idx == q->last_used_idx) return 1;
struct vring_used_elem *e = &q->used->ring[q->last_used_idx % q->size];
*out_id = (u16)e->id;
*out_len = e->len;
vn_fence();
q->last_used_idx = (u16)(q->last_used_idx + 1u);
return 0;
}
/* ================= RX 路径 ================= */
/* 回收 RX buffer:重置描述符并重新挂回 avail ring,保持 RX 水位。 */
static void rx_requeue(u16 id) {
g_rxq.desc[id].addr = g_rx_buf_phys[id];
g_rxq.desc[id].len = VNET_BUF_SIZE;
g_rxq.desc[id].flags = VRING_DESC_F_WRITE;
g_rxq.desc[id].next = 0;
vq_push_avail(&g_rxq, id);
vq_notify(&g_rxq);
}
/* 预填全部 RX buffer(单描述符覆盖 hdr+帧,device 可写)。 */
static int rx_prefill(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < RX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] rx buffer alloc failed");
log_hex("[virtio_net] filled=", i);
return -1;
}
g_rx_buf[i] = (u8 *)b.virt;
g_rx_buf_phys[i] = b.phys;
g_rxq.desc[i].addr = b.phys;
g_rxq.desc[i].len = VNET_BUF_SIZE;
g_rxq.desc[i].flags = VRING_DESC_F_WRITE;
g_rxq.desc[i].next = 0;
g_rxq.avail->ring[i] = (u16)i;
}
vn_fence();
g_rxq.avail->idx = (u16)RX_BUF_COUNT;
vn_fence();
log_hex("[virtio_net] rx buffers prefilled=", RX_BUF_COUNT);
return 0;
}
/* netdev rx_poll 回调(契约与 e1000 一致):
* 返回 0 且 *out_length>0 = 收到一帧;1 = 无数据;负数 = 错误。 */
static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx;
(void)buffer;
(void)capacity;
if (out_length) *out_length = 0;
return 0; /* 无数据, virtqueues 未启用 */
if (!g_ready || !buffer || !out_length) return -1;
u16 id;
u32 total;
if (vq_pop_used(&g_rxq, &id, &total) != 0) return 1; /* 无完成项 */
/* 防御:id 越界或总长小于 hdr → 丢帧(id 无效时无法回收,只计数) */
if (id >= RX_BUF_COUNT) {
g_stat_rx_err++;
return -3;
}
if (total < VNET_HDR_SIZE) {
g_stat_rx_err++;
rx_requeue(id);
return -3;
}
u32 frame_len = total - VNET_HDR_SIZE; /* 剥掉 virtio_net_hdr */
if (frame_len > capacity) {
g_stat_rx_overflow++;
rx_requeue(id);
return -2;
}
copy_bytes(buffer, g_rx_buf[id] + VNET_HDR_SIZE, frame_len);
*out_length = frame_len;
rx_requeue(id);
g_stat_rx_ok++;
return 0;
}
/* ================= TX 路径 ================= */
/* 收割 TX used ring,把完成的 buffer 标回空闲。 */
static void tx_reclaim(void) {
u16 id;
u32 len;
while (vq_pop_used(&g_txq, &id, &len) == 0) {
(void)len; /* TX 的 used.len 无语义,仅 id 用于回收 */
if (id < TX_BUF_COUNT) g_tx_free[id] = 1;
}
}
static int tx_buffers_alloc(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] tx buffer alloc failed");
return -1;
}
g_tx_buf[i] = (u8 *)b.virt;
g_tx_buf_phys[i] = b.phys;
g_tx_free[i] = 1;
}
return 0;
}
/* netdev tx 回调:hdr(10B 全零,无卸载) + 帧单描述符提交,kick 后同步等待完成。
* 与 e1000 行为一致:TSC 1 秒超时兜底,避免上层协议栈无限阻塞。 */
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_ready || !packet || length == 0 || length + VNET_HDR_SIZE > VNET_BUF_SIZE) {
g_stat_tx_err++;
return -1;
}
tx_reclaim();
int slot = -1;
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
if (g_tx_free[i]) { slot = (int)i; break; }
}
if (slot < 0) {
g_stat_tx_err++;
g_log->warn("[virtio_net] tx ring full");
return -2;
}
u8 *buf = g_tx_buf[slot];
zero_bytes(buf, VNET_HDR_SIZE); /* virtio_net_hdr: flags/gso 全 0 */
copy_bytes(buf + VNET_HDR_SIZE, packet, length);
/* desc 索引与 buffer 1:1 映射,回收时 used.id 即 buffer 索引 */
g_txq.desc[slot].addr = g_tx_buf_phys[slot];
g_txq.desc[slot].len = length + VNET_HDR_SIZE;
g_txq.desc[slot].flags = 0; /* device 只读 */
g_txq.desc[slot].next = 0;
g_tx_free[slot] = 0;
vq_push_avail(&g_txq, (u16)slot);
vq_notify(&g_txq);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
tx_reclaim();
if (g_tx_free[slot]) {
g_stat_tx_ok++;
return 0;
}
__asm__ volatile("pause");
}
g_stat_tx_timeout++;
g_log->warn("[virtio_net] tx timeout");
log_hex("[virtio_net] txq used idx=", g_txq.used->idx);
log_hex("[virtio_net] txq last_used=", g_txq.last_used_idx);
return -3;
}
/* ================= IRQ(辅助统计,数据路径不依赖) ================= */
static int virtio_net_irq(u8 irq) {
(void)irq;
if (!g_io_base) return 0;
/* ISR 读即清除;bit0=queue used buffer 通知,bit1=config change */
u8 isr = dkm_inb((u16)(g_io_base + VP_ISR));
if (isr & 0x1u) g_stat_irq_count++;
return 0; /* 不 suppress EOI,由 IDT 发 PIC EOI */
}
/* ================= boot 自测:DHCP DISCOVER/OFFER ================= */
#define ST_XID 0x4B1D3A5Fu
#define ST_FRAME_LEN 342u /* 14 eth + 20 ip + 8 udp + 300 bootp */
/* 构造 DHCP DISCOVER(全手工大端字段,避免 packed struct 字节序歧义)。
* BOOTP 载荷补齐到 300 字节(RFC 1542 最小值,slirp DHCP server 容忍度最高)。 */
static void st_build_discover(const u8 mac[6]) {
u8 *f = g_st_frame;
zero_bytes(f, ST_FRAME_LEN);
/* Ethernet: 广播 DISCOVER */
for (u32 i = 0; i < 6; i++) f[i] = 0xff;
for (u32 i = 0; i < 6; i++) f[6 + i] = mac[i];
f[12] = 0x08; f[13] = 0x00; /* IPv4 */
/* IPv4 @14 */
u8 *ip = f + 14;
ip[0] = 0x45; /* v4, ihl=5 */
ip[1] = 0x00;
ip[2] = 0x01; ip[3] = 0x48; /* total len = 328 */
ip[4] = 0x00; ip[5] = 0x00; /* id */
ip[6] = 0x00; ip[7] = 0x00; /* flags/frag */
ip[8] = 64; /* ttl */
ip[9] = 17; /* UDP */
ip[10] = 0x00; ip[11] = 0x00; /* cksum 占位 */
/* src 0.0.0.0 @12..15 保持 0 */
ip[16] = 0xff; ip[17] = 0xff; ip[18] = 0xff; ip[19] = 0xff;
/* IP header checksum(标准 1's complement,大端语义直接按字节对累加) */
u32 sum = 0;
for (u32 i = 0; i < 20; i += 2) sum += ((u32)ip[i] << 8) | ip[i + 1];
while (sum >> 16) sum = (sum & 0xffffu) + (sum >> 16);
u16 ck = (u16)(~sum & 0xffffu);
ip[10] = (u8)(ck >> 8); ip[11] = (u8)ck;
/* UDP @34: 68 → 67,校验和置 0(IPv4 下合法) */
u8 *udp = f + 34;
udp[0] = 0x00; udp[1] = 68;
udp[2] = 0x00; udp[3] = 67;
udp[4] = 0x01; udp[5] = 0x34; /* len = 308 */
udp[6] = 0x00; udp[7] = 0x00;
/* BOOTP @42(固定 236B + magic 4B + options,pad 到 300B) */
u8 *bp = f + 42;
bp[0] = 1; /* op = BOOTREQUEST */
bp[1] = 1; /* htype = Ethernet */
bp[2] = 6; /* hlen */
bp[3] = 0; /* hops */
bp[4] = (u8)(ST_XID >> 24);
bp[5] = (u8)(ST_XID >> 16);
bp[6] = (u8)(ST_XID >> 8);
bp[7] = (u8)ST_XID;
/* secs @8..9 = 0 */
bp[10] = 0x80; bp[11] = 0x00; /* flags = broadcast */
/* ciaddr/yiaddr/siaddr/giaddr @12..27 = 0 */
for (u32 i = 0; i < 6; i++) bp[28 + i] = mac[i]; /* chaddr(16B,后 10B 为 0) */
/* sname @44..107, file @108..235 = 0 */
bp[236] = 0x63; bp[237] = 0x82; bp[238] = 0x53; bp[239] = 0x63; /* magic cookie */
u8 *opt = bp + 240;
opt[0] = 53; opt[1] = 1; opt[2] = 1; /* DHCP msg type = DISCOVER */
opt[3] = 55; opt[4] = 4; /* param request list */
opt[5] = 1; opt[6] = 3; opt[7] = 6; opt[8] = 15; /* subnet/router/dns/domain */
opt[9] = 255; /* end */
}
/* 解析帧是否为匹配 XID 的 DHCP OFFER。
* 返回 2=OFFER(*out_yiaddr 有效,主机序),5=ACK,0=无关帧。 */
static int st_parse_reply(const u8 *f, u32 len, u32 *out_yiaddr) {
if (len < 14 + 20 + 8 + 240) return 0;
if (f[12] != 0x08 || f[13] != 0x00) return 0; /* 非 IPv4 */
u32 ihl = (u32)(f[14] & 0x0f) * 4u;
if (ihl < 20) return 0;
if (f[14 + 9] != 17) return 0; /* 非 UDP */
u32 udp = 14 + ihl;
if (len < udp + 8 + 240) return 0;
u16 dport = (u16)(((u16)f[udp + 2] << 8) | f[udp + 3]);
if (dport != 68) return 0; /* 非 DHCP 回包 */
u32 bp = udp + 8;
if (f[bp] != 2) return 0; /* 非 BOOTREPLY */
u32 xid = ((u32)f[bp + 4] << 24) | ((u32)f[bp + 5] << 16) |
((u32)f[bp + 6] << 8) | f[bp + 7];
if (xid != ST_XID) return 0; /* XID 不匹配 */
if (f[bp + 236] != 0x63 || f[bp + 237] != 0x82 ||
f[bp + 238] != 0x53 || f[bp + 239] != 0x63) return 0;
u32 yiaddr = ((u32)f[bp + 16] << 24) | ((u32)f[bp + 17] << 16) |
((u32)f[bp + 18] << 8) | f[bp + 19];
/* options 区扫描 msg type(53) */
u32 i = bp + 240;
while (i < len) {
u8 o = f[i];
if (o == 255) break;
if (o == 0) { i++; continue; }
if (i + 1 >= len) break;
u8 olen = f[i + 1];
if (o == 53 && olen == 1 && i + 2 < len) {
u8 mtype = f[i + 2];
if (mtype == 2 || mtype == 5) {
*out_yiaddr = yiaddr;
return mtype;
}
return 0;
}
i += 2u + olen;
}
return 0;
}
/* boot 自测:经真实网络后端验证 TX(desc+notify+used 回收)与
* RX(avail 预填 + used 收割 + hdr 剥离)全链路。
* 任何失败只记日志,绝不阻塞/崩溃启动流程。 */
static void virtio_net_selftest(void) {
if (!g_ready) return;
g_log->info("[virtio_net] selftest begin: DHCP DISCOVER -> expect OFFER from backend");
st_build_discover(g_mac);
int txrc = virtio_net_tx(0, g_st_frame, ST_FRAME_LEN);
log_hex("[virtio_net] selftest tx rc=", (u64)(i64)txrc);
if (txrc != 0) {
g_log->warn("[virtio_net] selftest: TX path FAILED");
return;
}
g_log->info("[virtio_net] selftest: TX path ok (descriptor consumed by device)");
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
u32 frames = 0;
while (dkm_rdtsc() < deadline) {
u32 flen = 0;
int rc = virtio_net_rx_poll(0, g_st_buf, sizeof(g_st_buf), &flen);
if (rc == 0 && flen > 0) {
frames++;
u32 ethertype = ((u32)g_st_buf[12] << 8) | g_st_buf[13];
log_hex("[virtio_net] selftest rx frame len=", flen);
log_hex("[virtio_net] ethertype=", ethertype);
u32 yiaddr = 0;
int t = st_parse_reply(g_st_buf, flen, &yiaddr);
if (t == 2) {
log_ipv4("[virtio_net] selftest: DHCP OFFER yiaddr=", yiaddr);
g_log->info("[virtio_net] selftest PASS: RX path ok (frame from real backend)");
return;
}
}
__asm__ volatile("pause");
}
g_log->warn("[virtio_net] selftest: no DHCP OFFER within 2000ms");
log_hex("[virtio_net] selftest rx frames seen=", frames);
}
/* ================= stub(初始化失败时的安全占位) =================
* 严格错误策略插桩:占位不再静默丢包/静默无数据——一旦被上层调用
* (即有人真的把流量交给未协商 virtqueue 的设备),立即莲花 panic。
* 正常路径 netman 因 flags 缺 LINK_UP 不会选中本驱动,stub 不触发。 */
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
(void)ctx; (void)packet;
log_hex("[virtio_net] tx stub hit, len=", length);
g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)");
return -1;
}
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity;
g_log->panic("VNET-E02 RX VIRTQUEUE NOT IMPLEMENTED (stub rx called)");
if (out_length) *out_length = 0;
return 0;
}
/* ================= driver 入口 ================= */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
@@ -230,19 +826,11 @@ int driver_init(const struct dkm_kernel_api *api,
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor = (u16)(vd & 0xffff);
u16 device = (u16)(vd >> 16);
u16 virtio_id = virtio_subsystem_id(device);
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 revision = (u8)(class_reg & 0xff);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
log_hex("[virtio_net] vendor=", vendor);
log_hex("[virtio_net] device=", device);
log_hex("[virtio_net] virtio_id=", virtio_id);
log_hex("[virtio_net] class=", class_code);
log_hex("[virtio_net] subclass=", subclass);
log_hex("[virtio_net] prog_if=", prog_if);
log_hex("[virtio_net] revision=", revision);
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
@@ -256,21 +844,96 @@ int driver_init(const struct dkm_kernel_api *api,
log_bars(bus, dev, func);
log_virtio_caps(bus, dev, func);
/* ---- legacy I/O BAR 检查:transitional 设备 BAR0 必须是 I/O space ---- */
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
int datapath_ok = 0;
if (!(bar0 & 1u)) {
g_log->warn("[virtio_net] BAR0 is not I/O space (modern-only device?); legacy path unavailable");
} else if (!api->dma || !api->dma->alloc_pages) {
g_log->warn("[virtio_net] dma API unavailable");
} else {
g_io_base = (u16)(bar0 & 0xfffcU);
log_hex("[virtio_net] legacy io_base=", g_io_base);
if (vdev_negotiate() != 0) {
g_log->warn("[virtio_net] feature negotiation failed");
} else if (vq_setup(api->dma, &g_rxq, 0) != 0) {
g_log->warn("[virtio_net] RXQ setup failed");
} else if (vq_setup(api->dma, &g_txq, 1) != 0) {
g_log->warn("[virtio_net] TXQ setup failed");
} else if (rx_prefill(api->dma) != 0) {
g_log->warn("[virtio_net] RX prefill failed");
} else if (tx_buffers_alloc(api->dma) != 0) {
g_log->warn("[virtio_net] TX buffer alloc failed");
} else {
log_hex("[virtio_net] rxq size=", g_rxq.size);
log_hex("[virtio_net] rxq phys=", g_rxq.phys);
log_hex("[virtio_net] txq size=", g_txq.size);
log_hex("[virtio_net] txq phys=", g_txq.phys);
/* MAC: device config @ io_base+0x14(协商 MAC feature 后有效) */
for (u32 i = 0; i < 6; i++) {
g_mac[i] = dkm_inb((u16)(g_io_base + VP_NET_CFG_MAC + i));
}
u8 mac_sum = 0;
for (u32 i = 0; i < 6; i++) mac_sum |= g_mac[i];
if (mac_sum == 0 || (g_mac[0] & 1u)) {
/* 全 0 或组播位异常 → fallback */
g_mac[0] = 0x52; g_mac[1] = 0x54; g_mac[2] = 0x00;
g_mac[3] = 0x12; g_mac[4] = 0x34; g_mac[5] = 0x57;
g_log->warn("[virtio_net] invalid device MAC, using fallback");
}
log_mac("[virtio_net] MAC=", g_mac);
/* DRIVER_OK:设备开始处理队列;随后 kick RXQ 让设备吃掉预填 buffer */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK | VS_DRIVER_OK);
vq_notify(&g_rxq);
g_ready = 1;
datapath_ok = 1;
g_log->info("[virtio_net] DRIVER_OK set; virtqueues live");
}
}
/* ---- IRQ 注册(辅助统计;数据路径为纯轮询,不依赖中断) ----
* 注意: kernel irq_register 按 IDT vector 索引(PIC IRQn → vector 0x20+n),
* 传裸 IRQ 号会落到异常向量槽位永不触发(e1000 当前如此)。
* PIC 未解屏蔽该线时自动退化为纯轮询。 */
if (datapath_ok && api->irq_register && irq_line > 0 && irq_line < 16) {
api->irq_register((u8)(irq_line + 0x20u), (void *)(uintptr_t)virtio_net_irq);
g_log->info("[virtio_net] IRQ handler registered (vector = irq + 0x20)");
} else {
g_log->info("[virtio_net] IRQ not registered (polling mode)");
}
/* ---- 注册 netdev ---- */
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "virtio-net";
for (int i = 0; i < 6; i++) netdev.mac[i] = 0;
netdev.flags = DKM_NET_F_LINK_UP;
for (u32 i = 0; i < 6; i++) netdev.mac[i] = g_mac[i];
netdev.ctx = 0;
netdev.tx = virtio_net_tx; /* stub: 安全占位, 不 NULL */
netdev.rx_poll = virtio_net_rx_poll;
if (datapath_ok) {
netdev.flags = DKM_NET_F_LINK_UP | DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
netdev.tx = virtio_net_tx;
netdev.rx_poll = virtio_net_rx_poll;
} else {
/* 安全占位:flags 仅 LINK_UP,netman 不会选它跑 DHCP(与历史行为一致) */
netdev.flags = DKM_NET_F_LINK_UP;
netdev.tx = virtio_net_tx_stub;
netdev.rx_poll = virtio_net_rx_poll_stub;
}
int net_index = api->net->register_device(&netdev);
log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index);
log_hex("[virtio_net] netdev registered, index=", (u64)(i64)net_index);
} else {
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
}
g_log->info("[virtio_net] discovery only; virtqueues disabled");
/* ---- boot 自测(证明 TX/RX 全链路,失败仅记日志) ---- */
virtio_net_selftest();
log_hex("[virtio_net] stat tx_ok=", g_stat_tx_ok);
log_hex("[virtio_net] stat rx_ok=", g_stat_rx_ok);
g_log->info("[virtio_net] driver ready");
return 0;
}
@@ -278,5 +941,11 @@ int driver_init(const struct dkm_kernel_api *api,
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* reset 设备:停止一切 DMA 与队列活动 */
if (g_io_base) {
vdev_set_status(0);
}
g_ready = 0;
g_io_base = 0;
return 0;
}
+247
View File
@@ -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;
}
+2158
View File
@@ -0,0 +1,2158 @@
/* DKM xHCI Driver — USB xHCI host controller + Mass Storage Class (BOT/SCSI)
*
* Stage 1 optional storage driver, depends on "pci" and "irq", provides "block".
*
* Architecture:
* 1. PCI discovery: class=0x0C/03/30 (Serial Bus / USB / xHCI)
* 2. MMIO mapping: 64-bit BAR0 via mm_map_mmio or HHDM fallback
* 3. Controller init: BIOS handoff, HC reset, parse capabilities
* 4. Ring setup: Command Ring, Event Ring, ERST, DCBAA, Scratchpad
* 5. Port scan: detect connected devices, port reset
* 6. USB enumeration: Enable Slot, Address Device, GET_DESCRIPTOR
* 7. MSC protocol: BOT (Bulk-Only Transport) + SCSI commands
* 8. Block provider: register usb0 via kernel_api.block
*
* Synchronous polling model (no IRQ/MSI in first version).
* All hardware waits use TSC deadline — no infinite loops.
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
DKM_STAT_DECL(xhci_pci_found);
DKM_STAT_DECL(xhci_port_reset_ok);
DKM_STAT_DECL(xhci_msc_found);
DKM_STAT_DECL(xhci_block_reads);
DKM_STAT_DECL(xhci_block_writes);
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "block" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "xhci",
.version = "0.1.0",
.vendor = "Deshab",
.driver_class = 6, /* DKM_CLASS_STORAGE */
.stage = 1,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
/* ================================================================
* Constants — PCI, xHCI registers, TRB types, USB
* ================================================================ */
#define XHCI_CLASS_SERIAL 0x0C
#define XHCI_SUBCLASS_USB 0x03
#define XHCI_PROGIF_XHCI 0x30
/* Capability Registers (MMIO + 0x00) */
#define XHCI_CAP_HCSPARAMS1 0x00
#define XHCI_CAP_HCSPARAMS2 0x04
#define XHCI_CAP_HCSPARAMS3 0x08
#define XHCI_CAP_HCCPARAMS1 0x0C
#define XHCI_CAP_DBOFF 0x14
#define XHCI_CAP_RTSOFF 0x18
/* Operational Registers (MMIO + cap_length) */
#define XHCI_OP_USBCMD 0x00
#define XHCI_OP_USBSTS 0x04
#define XHCI_OP_PAGESIZE 0x08
#define XHCI_OP_DNCTRL 0x14
#define XHCI_OP_CRCR_LO 0x18
#define XHCI_OP_CRCR_HI 0x1C
#define XHCI_OP_DCBAAP_LO 0x30
#define XHCI_OP_DCBAAP_HI 0x34
#define XHCI_OP_CONFIG 0x38
/* Port Registers (MMIO + 0x400 + port * 0x10) */
#define XHCI_PORT_BASE_OFF 0x400
#define XHCI_PORT_STRIDE 0x10
#define XHCI_PORT_SC 0x00
/* Runtime Registers (MMIO + rtsoff) */
#define XHCI_RT_MFINDEX 0x00
#define XHCI_RT_IR0_IMAN 0x20
#define XHCI_RT_IR0_IMOD 0x24
#define XHCI_RT_IR0_ERSTSZ 0x28
#define XHCI_RT_IR0_ERSTBA_LO 0x30
#define XHCI_RT_IR0_ERSTBA_HI 0x34
#define XHCI_RT_IR0_ERDP_LO 0x38
#define XHCI_RT_IR0_ERDP_HI 0x3C
/* ERDP bits: bit3 = Event Handler Busy (软件写 1 清挂起) */
#define ERDP_EHB (1u << 3)
/* USBCMD bits */
#define USBCMD_RS (1u << 0)
#define USBCMD_RST (1u << 1)
#define USBCMD_INTE (1u << 2)
/* USBSTS bits */
#define USBSTS_HCH (1u << 0)
#define USBSTS_EINT (1u << 3)
#define USBSTS_CNR (1u << 11)
/* PORTSC bits */
#define PORTSC_CCS (1u << 0)
#define PORTSC_PED (1u << 1)
#define PORTSC_OCA (1u << 3)
#define PORTSC_PR (1u << 4)
#define PORTSC_PLS_SHIFT 5
#define PORTSC_PLS_MASK 0xF
/* Port Speed 字段在 PORTSC bits[13:10](xHCI 1.2 §5.4.8)。
* BUG-FIX: 原实现 shift=17/mask=0x7 是错的——QEMU 用 10:13,
* 误读导致 SuperSpeed(4) 被当成 FULL(1),EP0 MPS 错误。 */
#define PORTSC_SPEED_SHIFT 10
#define PORTSC_SPEED_MASK 0xF
/* TRB types */
#define TRB_TYPE_NORMAL 0x01
#define TRB_TYPE_SETUP_STAGE 0x02
#define TRB_TYPE_DATA_STAGE 0x03
#define TRB_TYPE_STATUS_STAGE 0x04
#define TRB_TYPE_LINK 0x06
#define TRB_TYPE_ENABLE_SLOT 0x09
#define TRB_TYPE_DISABLE_SLOT 0x0A
#define TRB_TYPE_ADDRESS_DEV 0x0B
#define TRB_TYPE_CONFIGURE_EP 0x0C
#define TRB_TYPE_NOOP 0x17
/* Event TRB types — xHCI 1.2 spec Table 6-28 (TRB Type field [15:10]).
* 规范值:Transfer=0x20, Command Completion=0x21, Port Status Change=0x22。
* BUG-FIX: 原定义 0x01/0x02/0x21 与规范冲突——CMD_COMPLETE(0x21) 被
* 误判为 PORT_CHANGE 事件消费,导致 Enable Slot 等命令事件环被提前
* 清空,后续命令全部超时(QEMU hcd-xhci.c xhci_write_event 佐证)。 */
#define EVT_TYPE_TRANSFER 0x20 /* Transfer Event */
#define EVT_TYPE_CMD_COMPLETE 0x21 /* Command Completion Event */
#define EVT_TYPE_PORT_CHANGE 0x22 /* Port Status Change Event */
/* TRB control field helpers */
#define TRB_TYPE_SHIFT 10
#define TRB_TYPE_MASK 0x3F
#define TRB_IOC (1u << 5) /* Interrupt On Completion */
#define TRB_CH (1u << 6) /* Chain bit */
#define TRB_ENT (1u << 7) /* Evaluate Next TRB (for Link) */
#define TRB_ISP (1u << 8) /* Interrupt on Short Packet */
#define TRB_CYCLE (1u << 0)
#define TRB_TC (1u << 1) /* Toggle Cycle (for Link) */
/* Setup Stage TRB direction */
#define SETUP_DIR_OUT 0
#define SETUP_DIR_IN 1
/* USB speeds */
#define USB_SPEED_FULL 1
#define USB_SPEED_LOW 2
#define USB_SPEED_HIGH 3
#define USB_SPEED_SUPER 4
/* Endpoint types for EP Context */
#define EP_TYPE_CTRL_OUT 0
#define EP_TYPE_CTRL_IN 4
#define EP_TYPE_BULK_OUT 2
#define EP_TYPE_BULK_IN 6
/* Slot states */
#define SLOT_STATE_DISABLED 0
#define SLOT_STATE_ENABLED 1
#define SLOT_STATE_ADDRESSED 2
#define SLOT_STATE_CONFIGURED 3
/* Ring sizes */
#define CMD_RING_SIZE 256
#define EVT_RING_SIZE 256
#define XFER_RING_SIZE 256
/* xHCI MMIO mapping size (64 KB covers most controllers) */
#define XHCI_MAP_SIZE 0x10000
/* BOT (Bulk-Only Transport) signatures */
#define BOT_CBW_SIG 0x43425355u /* "USBC" */
#define BOT_CSW_SIG 0x53425355u /* "USBS" */
/* SCSI opcodes */
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
#define SCSI_READ_CAPACITY10 0x25
#define SCSI_READ10 0x28
#define SCSI_WRITE10 0x2A
/* ================================================================
* Data structures
* ================================================================ */
typedef struct {
u64 parameter; /* DW0-DW1 */
u32 status; /* DW2 */
u32 control; /* DW3 */
} xhci_trb;
/* TRB Ring */
typedef struct {
struct dkm_dma_buffer dma;
u32 size; /* TRB count */
u32 enqueue; /* producer index */
u32 ccs; /* producer Cycle State */
} xhci_ring;
/* ERST entry */
typedef struct {
u64 seg_addr;
u32 seg_size;
u32 rsvd;
} xhci_erst_entry;
/* MSC device state */
typedef struct {
u8 slot_id;
u8 speed;
u8 bulk_out_ep; /* EP number for Bulk OUT */
u8 bulk_in_ep; /* EP number for Bulk IN */
u16 bulk_out_mps; /* Max Packet Size */
u16 bulk_in_mps; /* Max Packet Size */
u32 cbw_tag; /* BOT tag counter */
u64 sector_count;
u32 sector_size;
int ready;
} xhci_msc_dev;
/* ================================================================
* Global state
* ================================================================ */
static const struct dkm_kernel_api *g_api;
static const struct dkm_log_api *g_log;
static volatile u8 *g_mmio; /* MMIO base virtual address */
static u32 g_cap_length; /* Capability register space length */
static u32 g_doorbell_off; /* Doorbell register offset */
static u32 g_runtime_off; /* Runtime register space offset */
static u32 g_max_slots;
static u32 g_max_ports;
static u32 g_max_intrs;
static u32 g_ctx_size; /* 32 or 64 bytes */
static u32 g_page_size; /* controller page size in bytes */
static u32 g_spb_count; /* scratchpad buffer count */
static u32 g_port_base; /* absolute MMIO offset of port register set (cap_length + 0x400) */
static u32 g_ppc; /* Per-Port Power Control (HCCPARAMS1 bit 3) */
static xhci_ring g_cmd_ring;
static xhci_ring g_evt_ring;
static u32 g_evt_ccs; /* consumer cycle state for event ring */
static struct dkm_dma_buffer g_erst; /* ERST DMA buffer */
static struct dkm_dma_buffer g_dcbaa; /* DCBAA DMA buffer */
static struct dkm_dma_buffer g_sp_bufs; /* Scratchpad buffer array */
/* Scratchpad individual pages — max 32 scratchpads for safety */
static struct dkm_dma_buffer g_sp_pages[32];
/* Per-device DMA buffers */
static struct dkm_dma_buffer g_input_ctx; /* Input Context */
static struct dkm_dma_buffer g_dev_ctx; /* Device Context (output) */
static xhci_ring g_ep0_ring; /* EP0 control transfer ring */
static xhci_ring g_bulk_out_ring;
static xhci_ring g_bulk_in_ring;
static struct dkm_dma_buffer g_xfer_buf; /* Data transfer bounce buffer */
static struct dkm_dma_buffer g_csw_buf; /* CSW receive buffer */
static xhci_msc_dev g_msc;
/* PCI device location */
static u8 g_pci_bus, g_pci_dev, g_pci_func;
/* ================================================================
* Utility functions
* ================================================================ */
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--)
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* ================================================================
* Framebuffer 阶段诊断(实机无串口时屏幕直接显示 xhci 失败点)
* 失败时整屏填背景色 + 中央画大号两位十六进制状态码,然后 halt。
* 码表(调用点注释中注明):
* 红底 0x01..0x07 基础设施失败(PCI/MMIO/caps/reset/ring/start)
* 红底 0x11..0x14 PCI 自检细分(见 xhci_pci_diag_code)
* 黄底 0x08 所有端口无设备可枚举
* 黄底 0x0A..0x0F 枚举失败(slot/address/descriptor/configure/scsi)
* 依赖 dkm_kernel_api.fb_* 字段(32bpp,与 console_fb 一致)。
* ================================================================ */
static const u8 g_fb_font_hex[16][8] = {
{0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 0 */
{0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
{0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
{0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
{0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
{0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
{0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
{0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00}, /* 7 */
{0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
{0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
{0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
{0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
{0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
{0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
};
static void fb_draw_char(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, int ch) {
const u8 *g = g_fb_font_hex[ch & 0xF];
for (int row = 0; row < 8; row++) {
u8 bits = g[row];
for (int col = 0; col < 8; col++) {
if (!(bits & (0x80u >> col))) continue;
for (int sy = 0; sy < scale; sy++) {
u32 *line = (u32 *)((u8 *)fb + (u64)(y0 + row * scale + sy) * pitch);
for (int sx = 0; sx < scale; sx++)
line[x0 + col * scale + sx] = color;
}
}
}
}
static void fb_diag_show(const struct dkm_kernel_api *api, u32 bg, u32 code) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return; /* 无 framebuffer 时退化为仅串口日志 */
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
int scale = 8;
int cw = 8 * scale;
int cx = (int)(w / 2) - cw; /* 两位数字整体居中 */
int cy = (int)(h / 2) - (8 * scale) / 2;
u32 fg = 0xFFFFFFFFu;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, scale, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, scale, fg, (int)(code & 0xF));
/* 停住屏幕,等待实机拍照/观察 */
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 任意位数 hex(n=1..8),低位在前填充 */
static void fb_draw_hexn(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, u32 v, int n) {
for (int i = 0; i < n; i++) {
int nib = (int)((v >> (4 * (n - 1 - i))) & 0xF);
fb_draw_char(fb, pitch, x0 + i * 8 * scale, y0, scale, color, nib);
}
}
/* PCI 自检诊断清单项(bus/dev/device_id/class/progif) */
struct usb_ctl_info {
u32 bus;
u32 dev;
u32 devid; /* PCI device ID */
u32 cls; /* (class<<8)|subclass */
u32 progif;
};
/* 扩展诊断:大字标题码 + 下方逐行列出扫描到的 PCI 设备。
* 每行:bus(1) dev(1) deviceid(4) = 左 6 hex,class(2) progif(2) = 右 4 hex。
* scale=2 支持 16 行(640x480 完整容纳)。device ID 用于区分芯片型号:
* Intel 7系: xHCI=1e31, EHCI1=1e2d, EHCI2=1e26
* Intel 6系: EHCI1=1c2d, EHCI2=1c26 (H61 仅 1 个 EHCI)
* 第三方 USB3: ASMedia=1b21, Renesas=1033 */
/* 诊断用,保留 */
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) __attribute__((unused));
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return;
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
u32 fg = 0xFFFFFFFFu;
/* 标题大字(scale 6,两位居中,固定顶部) */
int s = 6, cw = 8 * s;
int cx = (int)(w / 2) - cw;
int cy = 8;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, s, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, s, fg, (int)(code & 0xF));
/* 信息行(scale 2,最多 16 行):
* 左:bus(1)+dev(1)+devid(4)=6 hex;右:class(2)+progif(2)=4 hex */
int s2 = 2, cw2 = 8 * s2;
int row_y = cy + 8 * s + 8;
int total = (6 + 1 + 4) * cw2; /* 176px */
int xl = (int)(w / 2) - total / 2;
int xr = xl + (6 + 1) * cw2;
u32 max_rows = usb_count < 16 ? usb_count : 16;
for (u32 i = 0; i < max_rows; i++) {
u32 bd = ((usb[i].bus & 0xFF) << 4) | (usb[i].dev & 0x1F);
fb_draw_hexn(fb, pitch, xl, row_y, s2, fg, bd, 2);
fb_draw_hexn(fb, pitch, xl + 2 * cw2, row_y, s2, fg, usb[i].devid & 0xFFFF, 4);
fb_draw_hexn(fb, pitch, xr, row_y, s2, fg, usb[i].cls & 0xFFFF, 2);
fb_draw_hexn(fb, pitch, xr + 2 * cw2, row_y, s2, fg, usb[i].progif & 0xFFFF, 2);
row_y += 8 * s2 + 4; /* 20px/行 */
}
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 红底 = 基础设施失败,halt(0xFFFF0000 在 BGRA 下是纯红;
* 原 0xFF0000FF 实际渲染为蓝——0x01 那次实机测试显示“蓝底”即此原因) */
#define FB_FATAL(api, code) fb_diag_show((api), 0xFFFF0000u, (code))
/* 红底 PCI 自检:标题码 + USB 控制器清单 */
#define FB_FATAL_show_usb(api, code) \
fb_diag_show_usb((api), 0xFFFF0000u, (code), g_diag_usb, g_diag_usb_count)
/* 黄底 = 枚举失败,halt */
#define FB_ENUM_FAIL(code) fb_diag_show(g_api, 0xFFFFC800u, (code))
static void xhci_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0;
}
static void xhci_memcpy(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
/* MMIO read/write */
static u32 mmio_read32(u32 off) {
return *(volatile u32 *)(void *)(g_mmio + off);
}
static void mmio_write32(u32 off, u32 value) {
*(volatile u32 *)(void *)(g_mmio + off) = value;
}
static u64 mmio_read64(u32 off) {
u32 lo = mmio_read32(off);
u32 hi = mmio_read32(off + 4);
return ((u64)hi << 32) | lo;
}
static void mmio_write64(u32 off, u64 value) {
mmio_write32(off, (u32)(value & 0xffffffffu));
mmio_write32(off + 4, (u32)(value >> 32));
}
/* Memory barrier (compiler only — x86 TSO guarantees HW ordering) */
static __inline__ void xhci_mb(void) {
__asm__ volatile("" ::: "memory");
}
/* ================================================================
* Phase 1: PCI discovery + MMIO mapping
* ================================================================ */
/* PCI 自检:区分“传统 0xCF8/0xCFC 配置机制失效” vs “确实无 xHCI 控制器”。
* 实机现象:xhci_find 全扫描失败(诊断码 0x01)——USB3 主板必然有 xHCI,
* 扫不到只可能是 (a) 配置机制读不到任何设备 (b) 设备在异常位置 (c) 控制器
* 是 EHCI 等非 xHCI。返回细分诊断码:
* 0x11 bus0/dev0/func0 vendor=0xFFFF → 0xCF8/0xCFC 机制失效
* 0x12 机制可用但全扫描无任何 USB 控制器 (class 0x0C/03)
* 0x13 有 USB 控制器但无 prog_if=0x30(全部 EHCI/UHCI/OHCI)
* 0x14 存在 prog_if=0x30 的 xHCI 但 xhci_find 漏匹配(防御性,理论不可能)
* 同时把扫描到的设备填入 g_diag_usb 供屏幕显示——记录策略:
* 第一遍收集全部 USB 控制器(class 0C/03,含 bus1+ 第三方芯片),
* 第二遍收集其他设备(bus0 优先),总上限 16 行。
* 关键 device ID 判读:Intel 7系 xHCI=1e31 / EHCI1=1e2d / EHCI2=1e26;
* Intel 6系 EHCI1=1c2d / EHCI2=1c26(H61 仅 1 个);ASMedia=1b21;
* Renesas=1033。 */
static struct usb_ctl_info g_diag_usb[16];
static u32 g_diag_usb_count;
#define DIAG_MAX 16
static void diag_add(u32 bus, u32 dev, u32 devid, u32 cls, u32 progif) {
if (g_diag_usb_count >= DIAG_MAX) return;
g_diag_usb[g_diag_usb_count].bus = bus;
g_diag_usb[g_diag_usb_count].dev = dev;
g_diag_usb[g_diag_usb_count].devid = devid;
g_diag_usb[g_diag_usb_count].cls = cls;
g_diag_usb[g_diag_usb_count].progif = progif;
g_diag_usb_count++;
}
/* 诊断用,保留(无 xHCI 场景默认不调用,可通过临时改 driver_init 调试) */
static u32 xhci_pci_diag_code(void) __attribute__((unused));
static u32 xhci_pci_diag_code(void) {
u32 vd0 = dkm_pci_read(0, 0, 0, PCI_VENDOR_ID);
g_diag_usb_count = 0;
if ((vd0 & 0xffff) == 0xffff) return 0x11;
u32 usb_total = 0, xhci_total = 0;
/* 第一遍:USB 控制器(全 bus,含 bus1+ 第三方芯片) */
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) {
usb_total++;
if (prog_if == 0x30) xhci_total++;
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
/* 第二遍:其他设备补位(bus0 优先) */
if (g_diag_usb_count < DIAG_MAX) {
for (u16 bus = 0; bus < 256 && g_diag_usb_count < DIAG_MAX; bus++) {
for (u8 dev = 0; dev < 32 && g_diag_usb_count < DIAG_MAX; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count && g_diag_usb_count < DIAG_MAX; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) continue; /* 已收集 */
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
if (usb_total == 0) return 0x12;
if (xhci_total > 0) return 0x14;
return 0x13;
}
static int xhci_find_nth(int nth, u8 *out_bus, u8 *out_dev, u8 *out_func) {
int seen = 0;
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == XHCI_CLASS_SERIAL &&
subclass == XHCI_SUBCLASS_USB &&
prog_if == XHCI_PROGIF_XHCI) {
if (seen == nth) {
*out_bus = (u8)bus;
*out_dev = dev;
*out_func = func;
return 0;
}
seen++;
}
}
}
}
return -1;
}
static volatile u8 *xhci_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) {
u64 bar_virt = api->hhdm_offset + bar_phys;
/* Prefer mm_map_mmio independent window */
if (api->mm_map_mmio) {
void *mapped = api->mm_map_mmio(bar_phys, XHCI_MAP_SIZE);
if (mapped) {
g_log->info("[xhci] BAR mapped via mm_map_mmio");
return (volatile u8 *)mapped;
}
g_log->warn("[xhci] mm_map_mmio failed; trying HHDM fallback");
}
/* Check if HHDM covers this BAR */
if (api->mmio && api->mmio->is_mapped) {
int level = api->mmio->is_mapped(bar_virt);
if (level > 0) {
g_log->info("[xhci] HHDM covers BAR; direct access");
return (volatile u8 *)(uintptr_t)bar_virt;
}
/* Map via page tables */
if (api->mmio->map_mmio) {
int rc = api->mmio->map_mmio(bar_phys, XHCI_MAP_SIZE);
if (rc == 0 && api->mmio->is_mapped(bar_virt) > 0) {
g_log->info("[xhci] BAR mapped via page tables");
return (volatile u8 *)(uintptr_t)bar_virt;
}
}
}
/* Low BAR fallback */
if (bar_phys < 0x100000000ULL) {
g_log->warn("[xhci] assuming HHDM covers low BAR");
return (volatile u8 *)(uintptr_t)bar_virt;
}
g_log->error("[xhci] BAR MMIO unavailable");
return (volatile u8 *)0;
}
/* ================================================================
* Phase 1: BIOS Handoff via Extended Capabilities
* ================================================================ */
static void xhci_bios_handoff(void) {
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
u32 ext_ptr = (hcc1 >> 16) & 0xFFFF;
if (!ext_ptr) return;
/* Walk extended capabilities (offsets in 32-bit words from MMIO base) */
u32 off = ext_ptr;
for (int i = 0; i < 16 && off != 0; i++) {
u32 dw0 = mmio_read32(off * 4);
u8 id = (u8)(dw0 & 0xFF);
u8 next = (u8)((dw0 >> 8) & 0xFF);
if (id == 1) {
/* USB Legacy Support Capability */
u32 legsup = mmio_read32(off * 4 + 4);
log_hex("[xhci] USBLEGSUP=", legsup);
if (legsup & 0x0000FFFFu) {
/* BIOS owns controller — request ownership */
mmio_write32(off * 4 + 4, legsup | (1u << 16));
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
u32 cur = mmio_read32(off * 4 + 4);
if (!(cur & 0x0000FFFFu)) {
g_log->info("[xhci] BIOS handoff complete");
/* Clear SMI triggers */
mmio_write32(off * 4 + 8, 0);
break;
}
__asm__ volatile("pause");
}
}
}
off = next ? (off + next) : 0;
}
}
/* ================================================================
* Phase 1: Controller reset and init
* ================================================================ */
static int xhci_controller_reset(void) {
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
u32 usbsts = mmio_read32(g_cap_length + XHCI_OP_USBSTS);
/* If controller is running, halt it first */
if (!(usbsts & USBSTS_HCH)) {
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_HCH) break;
__asm__ volatile("pause");
}
}
/* Wait for CNR to clear */
{
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
if (!(mmio_read32(g_cap_length + XHCI_OP_USBSTS) & USBSTS_CNR)) break;
__asm__ volatile("pause");
}
}
/* Assert HC reset */
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd | USBCMD_RST);
/* Wait for reset to complete (RST bit clears) */
{
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
if (!(usbcmd & USBCMD_RST)) {
g_log->info("[xhci] controller reset complete");
return 0;
}
__asm__ volatile("pause");
}
}
g_log->error("[xhci] controller reset timeout");
return -1;
}
static int xhci_parse_caps(void) {
/* Read CAPLENGTH (low byte of MMIO+0) */
g_cap_length = mmio_read32(0) & 0xFF;
u32 hcs1 = mmio_read32(XHCI_CAP_HCSPARAMS1);
u32 hcs2 = mmio_read32(XHCI_CAP_HCSPARAMS2);
u32 hcc1 = mmio_read32(XHCI_CAP_HCCPARAMS1);
g_max_slots = hcs1 & 0xFF;
g_max_intrs = (hcs1 >> 8) & 0x7FF;
g_max_ports = (hcs1 >> 24) & 0xFF;
/* Context size: CSZ bit4 of HCCPARAMS1 */
g_ctx_size = (hcc1 & (1u << 4)) ? 64 : 32;
/* Per-Port Power Control: PPC bit3 of HCCPARAMS1 */
g_ppc = (hcc1 >> 3) & 1;
/* Scratchpad buffer count from HCSPARAMS2 */
g_spb_count = ((hcs2 >> 21) & 0x1F) | ((hcs2 >> 27) & 0x1F);
/*
* Port Register Set offset: absolute from MMIO base.
* xHCI spec: "Port Register Sets are located at offset 0x400
* from the beginning of the Operational Register Space."
* Operational Register Space starts at offset CAPLENGTH.
* Therefore absolute port offset = CAPLENGTH + 0x400.
* Verified against QEMU source (XHCI_REGS_OFFSET_PORT =
* XHCI_REGS_OFFSET_OPER + XHCI_REGS_LENGTH_OPER) and Linux
* kernel (op_regs + PORTSC_OFFSET where op_regs = base + cap_length).
*/
g_port_base = g_cap_length + XHCI_PORT_BASE_OFF;
/* Doorbell and Runtime offsets — full 32-bit, clear reserved low bits */
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
/* Page size: PAGESIZE 寄存器 bit[n] 置位表示支持 2^(n+12) 字节页。
* 取置位的最低 bit(xHCI 规范要求软件使用最小支持页)。
* 原实现 (ps_reg & 0xF) 语义错误,实机多 bit 置位时会得到错误页大小。 */
u32 ps_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE);
u32 ps_bits = ps_reg & 0xFFFFu;
u32 ps_shift = 0;
if (ps_bits) {
while (!(ps_bits & 1u)) { ps_bits >>= 1; ps_shift++; }
g_page_size = 4096u << ps_shift;
} else {
g_page_size = 4096;
}
log_hex("[xhci] CAPLEN=", g_cap_length);
log_hex("[xhci] MaxSlots=", g_max_slots);
log_hex("[xhci] MaxPorts=", g_max_ports);
log_hex("[xhci] MaxIntrs=", g_max_intrs);
log_hex("[xhci] ContextSize=", g_ctx_size);
log_hex("[xhci] PPC=", g_ppc);
log_hex("[xhci] PortBaseOff=", g_port_base);
log_hex("[xhci] ScratchpadBufs=", g_spb_count);
log_hex("[xhci] DBOFF=", g_doorbell_off);
log_hex("[xhci] RTSOFF=", g_runtime_off);
log_hex("[xhci] PageSize=", g_page_size);
if (g_max_slots == 0 || g_max_ports == 0) {
g_log->error("[xhci] invalid capability parameters");
return -1;
}
if (g_max_intrs == 0) {
g_log->warn("[xhci] MaxIntrs=0 (no interrupt register sets); polling mode only");
}
return 0;
}
/* ================================================================
* Phase 2: TRB Ring management
* ================================================================ */
static int xhci_ring_init(xhci_ring *ring, u32 size) {
if (!g_api->dma) return -1;
/* Allocate one page (must be 4K aligned for DMA) */
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &ring->dma) != 0)
return -2;
xhci_zero(ring->dma.virt, ring->dma.size);
ring->size = size;
ring->enqueue = 0;
ring->ccs = 1; /* Producer Cycle State starts at 1 */
/* Append Link TRB at end of ring */
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
u32 link_idx = size - 1;
trbs[link_idx].parameter = ring->dma.phys;
trbs[link_idx].status = 0;
trbs[link_idx].control = (TRB_TYPE_LINK << TRB_TYPE_SHIFT) | TRB_TC | TRB_CYCLE;
return 0;
}
/* Enqueue a TRB onto a ring and return the enqueue index before advance */
static u32 xhci_ring_enqueue(xhci_ring *ring, u64 param, u32 status, u32 control) {
xhci_trb *trbs = (xhci_trb *)ring->dma.virt;
u32 idx = ring->enqueue;
/* Set cycle bit in control */
control &= ~TRB_CYCLE;
if (ring->ccs) control |= TRB_CYCLE;
trbs[idx].parameter = param;
trbs[idx].status = status;
trbs[idx].control = control;
/* Advance enqueue */
ring->enqueue = (idx + 1) % (ring->size - 1); /* -1 to skip Link TRB */
/* If we wrapped, toggle CCS */
if (idx + 1 >= ring->size - 1)
ring->ccs ^= 1;
return idx;
}
/* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell。
* Doorbell Registers 每个 4 字节(xHCI 1.2 §5.6):offset = slot_id * 4。
* BUG-FIX: 原实现 slot_id * 0x20 偏移错误——QEMU 门铃区 reg>>=2 后
* reg 即 slot_id,0x20 偏移会敲到 slot 8,该 slot 无 EP → 控制器不
* 处理 TRB → 无 Transfer Event → 轮询超时。 */
static void xhci_ring_doorbell(u32 slot_id, u32 target) {
mmio_write32(g_doorbell_off + slot_id * 4, target);
}
/* ================================================================
* Phase 2: Event Ring polling
* ================================================================ */
/* Poll event ring for a specific TRB type with TSC timeout.
* Returns 0 on success, fills out_param/out_status/out_control.
* Returns -1 on timeout, -2 on error completion code. */
static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
u64 *out_param, u32 *out_status, u32 *out_control) {
xhci_trb *trbs = (xhci_trb *)g_evt_ring.dma.virt;
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms;
for (;;) {
xhci_trb *evt = &trbs[g_evt_ring.enqueue];
u32 ctrl = evt->control;
u8 cycle = (u8)(ctrl & TRB_CYCLE);
/* Check if this TRB is owned by us (consumer) */
if (cycle == (u8)g_evt_ccs) {
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
/* Process this event */
if (type == expected_type) {
if (out_param) *out_param = evt->parameter;
if (out_status) *out_status = evt->status;
if (out_control)*out_control= evt->control;
/* Advance dequeue */
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
/* Update ERDP: 地址 16 字节对齐,bit0 保持 0,bit3=EHB 清挂起 */
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Check completion code */
u32 cc = (evt->status >> 24) & 0xFF;
if (cc != 1) { /* 1 = Success */
if (cc != 0) {
log_hex("[xhci] event completion code=", cc);
return -2;
}
}
return 0;
}
/* Unexpected event type — handle or skip */
if (type == EVT_TYPE_PORT_CHANGE) {
/* Port Status Change Event: clear PORTSC change bits */
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
if (port_id > 0 && port_id <= g_max_ports) {
u32 port_off = g_port_base + (port_id - 1) * XHCI_PORT_STRIDE + XHCI_PORT_SC;
/* Read PORTSC and write back to clear change bits (W1C) */
u32 portsc = mmio_read32(port_off);
mmio_write32(port_off, portsc);
log_hex("[xhci] port change event, port=", port_id);
}
} else {
log_hex("[xhci] unexpected event type=", type);
}
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp2 = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | ERDP_EHB);
continue;
}
if (dkm_rdtsc() > deadline) {
g_log->warn("[xhci] event poll timeout");
return -1;
}
__asm__ volatile("pause");
}
}
/* ================================================================
* Phase 2: Command submission
* ================================================================ */
static int xhci_cmd_submit(u64 param, u32 status, u32 control,
u64 timeout_ms,
u64 *out_param, u32 *out_status, u32 *out_control) {
/* Clear Event Ring interrupt pending */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
/* Enqueue command TRB */
xhci_ring_enqueue(&g_cmd_ring, param, status, control);
xhci_mb();
/* Debug: verify CRCR and command ring state */
log_hex("[xhci] CRCR=", mmio_read64(g_cap_length + XHCI_OP_CRCR_LO));
log_hex("[xhci] cmd ring enqueue=", g_cmd_ring.enqueue);
log_hex("[xhci] cmd ring ccs=", g_cmd_ring.ccs);
xhci_ring_doorbell(0, 0); /* Command ring doorbell */
/* Debug: check USBSTS after doorbell */
log_hex("[xhci] USBSTS after db=", mmio_read32(g_cap_length + XHCI_OP_USBSTS));
/* Poll for Command Completion Event */
/* Debug: dump event ring head entry */
{
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
log_hex("[xhci] evt ring head=", g_evt_ring.enqueue);
log_hex("[xhci] evt[0] ctrl=", evts[g_evt_ring.enqueue].control);
}
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
out_param, out_status, out_control);
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return rc;
}
/* ================================================================
* Phase 2: Setup controller rings + DCBAA + Scratchpad
* ================================================================ */
static int xhci_setup_rings(void) {
if (!g_api->dma) {
g_log->warn("[xhci] DMA API unavailable");
return -1;
}
/* Command Ring */
if (xhci_ring_init(&g_cmd_ring, CMD_RING_SIZE) != 0) {
g_log->error("[xhci] cmd ring alloc failed");
return -2;
}
log_hex("[xhci] cmd ring phys=", g_cmd_ring.dma.phys);
/* Event Ring */
if (xhci_ring_init(&g_evt_ring, EVT_RING_SIZE) != 0) {
g_log->error("[xhci] evt ring alloc failed");
return -3;
}
g_evt_ccs = 1; /* Consumer Cycle State starts at 1 */
log_hex("[xhci] evt ring phys=", g_evt_ring.dma.phys);
/* ERST (Event Ring Segment Table) */
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_erst) != 0) {
g_log->error("[xhci] ERST alloc failed");
return -4;
}
xhci_erst_entry *erst = (xhci_erst_entry *)g_erst.virt;
xhci_zero(erst, sizeof(xhci_erst_entry));
erst->seg_addr = g_evt_ring.dma.phys;
erst->seg_size = EVT_RING_SIZE;
/* DCBAA (Device Context Base Address Array) */
u32 dcbaa_size = (g_max_slots + 1) * 8;
u32 dcbaa_pages = (dcbaa_size + 4095) / 4096;
if (dcbaa_pages == 0) dcbaa_pages = 1;
if (g_api->dma->alloc_pages(dcbaa_pages, 4096, 0x100000000ULL, &g_dcbaa) != 0) {
g_log->error("[xhci] DCBAA alloc failed");
return -5;
}
xhci_zero(g_dcbaa.virt, g_dcbaa.size);
log_hex("[xhci] DCBAA phys=", g_dcbaa.phys);
/* Scratchpad Buffer Array (if needed) */
if (g_spb_count > 0) {
u32 sp_arr_size = g_spb_count * 8;
u32 sp_arr_pages = (sp_arr_size + 4095) / 4096;
if (sp_arr_pages == 0) sp_arr_pages = 1;
if (g_api->dma->alloc_pages(sp_arr_pages, 4096, 0x100000000ULL, &g_sp_bufs) != 0) {
g_log->error("[xhci] scratchpad array alloc failed");
return -6;
}
xhci_zero(g_sp_bufs.virt, g_sp_bufs.size);
/* Allocate individual scratchpad buffer pages */
u32 sp_count = g_spb_count;
if (sp_count > 32) sp_count = 32; /* safety cap */
for (u32 i = 0; i < sp_count; i++) {
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_sp_pages[i]) != 0) {
g_log->error("[xhci] scratchpad page alloc failed");
return -7;
}
xhci_zero(g_sp_pages[i].virt, g_sp_pages[i].size);
/* Fill scratchpad buffer array with physical addresses */
u64 *sp_arr = (u64 *)g_sp_bufs.virt;
sp_arr[i] = g_sp_pages[i].phys;
}
/* DCBAA slot 0 points to scratchpad buffer array */
u64 *dcbaa_entries = (u64 *)g_dcbaa.virt;
dcbaa_entries[0] = g_sp_bufs.phys;
log_hex("[xhci] SPB array phys=", g_sp_bufs.phys);
}
return 0;
}
static int xhci_controller_start(void) {
u32 cap_off = g_cap_length;
/* Configure Command Ring */
u64 crcr = g_cmd_ring.dma.phys | (u64)g_cmd_ring.ccs;
mmio_write64(cap_off + XHCI_OP_CRCR_LO, crcr);
/* Configure DCBAAP */
mmio_write64(cap_off + XHCI_OP_DCBAAP_LO, g_dcbaa.phys);
/* Configure MaxSlotsEn */
mmio_write32(cap_off + XHCI_OP_CONFIG, g_max_slots);
/* Configure Event Ring */
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
u64 erdp = g_evt_ring.dma.phys;
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Enable interrupt (IMAN.IE=1) */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | (1u << 1));
/* Enable controller interrupts + start controller */
u32 usbcmd = mmio_read32(cap_off + XHCI_OP_USBCMD);
usbcmd |= USBCMD_INTE | USBCMD_RS;
mmio_write32(cap_off + XHCI_OP_USBCMD, usbcmd);
/* Wait for HCH to clear (controller running) */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
while (dkm_rdtsc() < deadline) {
u32 usbsts = mmio_read32(cap_off + XHCI_OP_USBSTS);
if (!(usbsts & USBSTS_HCH)) {
g_log->info("[xhci] controller started");
return 0;
}
__asm__ volatile("pause");
}
g_log->error("[xhci] controller start timeout");
return -1;
}
/* ================================================================
* Phase 3: Port detection and reset
* ================================================================ */
static int xhci_reset_port(u32 port_idx) {
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
/* Check if device is connected */
if (!(portsc & PORTSC_CCS)) return -1;
/* Already enabled? */
if (portsc & PORTSC_PED) return 0;
/* 只置 PR,并清掉 RW1C 位。把读到的 PORTSC 原样或上 PR 会把
* PED=0 和已经为 1 的变化位写回去,实机上复位经常起不来。 */
u32 cmd = portsc & ~((1u << 1) | PORTSC_PR | 0x00FE0000u);
cmd |= PORTSC_PR;
if (portsc & (1u << 9)) cmd |= (1u << 9);
mmio_write32(port_off, cmd);
/* Wait for PED (Port Enabled) with 500ms timeout */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
while (dkm_rdtsc() < deadline) {
portsc = mmio_read32(port_off);
if (portsc & PORTSC_PED) {
/* Clear port change bits (W1C: write 1 to clear) */
mmio_write32(port_off, portsc);
DKM_STAT_INC(xhci_port_reset_ok);
/* Drain any Port Status Change Events from the event ring.
* Port reset generates a Port Status Change Event that must
* be consumed before submitting new commands, otherwise the
* event ring gets congested and command completion events
* may not be delivered. */
dkm_delay_ms(10); /* Allow controller to post the event */
xhci_trb *evts = (xhci_trb *)g_evt_ring.dma.virt;
for (int drain = 0; drain < 16; drain++) {
xhci_trb *evt = &evts[g_evt_ring.enqueue];
u32 ctrl = evt->control;
u8 cycle = (u8)(ctrl & TRB_CYCLE);
if (cycle != (u8)g_evt_ccs) break; /* No more events */
u8 type = (u8)((ctrl >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK);
if (type == EVT_TYPE_PORT_CHANGE) {
u32 port_id = (u32)((evt->parameter >> 24) & 0xFF);
log_hex("[xhci] drained port change event, port=", port_id);
}
/* Advance dequeue */
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
}
return 0;
}
/* Check if device disconnected during reset */
if (!(portsc & PORTSC_CCS)) return -2;
__asm__ volatile("pause");
}
g_log->warn("[xhci] port reset timeout");
return -3;
}
static u8 xhci_get_port_speed(u32 port_idx) {
u32 port_off = g_port_base + port_idx * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
return (u8)((portsc >> PORTSC_SPEED_SHIFT) & PORTSC_SPEED_MASK);
}
/* ================================================================
* Phase 3: Enable Slot
* ================================================================ */
static int xhci_enable_slot(u8 *out_slot_id) {
/* Submit Enable Slot Command TRB */
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Enable Slot failed");
return rc;
}
/* Slot ID 位于 Command Completion Event 的 TRB control[31:24]。
* (xHCI 1.2 §6.4.2.2:CMD_COMPLETE 事件 TRB parameter = 命令 TRB 地址,
* slot id 在 control 高字节 —— QEMU hcd-xhci.c xhci_write_event 佐证) */
*out_slot_id = (u8)((out_control >> 24) & 0xFF);
log_hex("[xhci] slot_id=", *out_slot_id);
return 0;
}
/* ================================================================
* Phase 3: Address Device
* ================================================================ */
static int xhci_address_device(u8 slot_id, int bsr) {
/* Input Context physical address */
u64 input_ctx_phys = g_input_ctx.phys;
u32 control = (TRB_TYPE_ADDRESS_DEV << TRB_TYPE_SHIFT);
if (bsr) control |= (1u << 9); /* BSR (Block Set Address) */
/* Slot ID goes in control bits [31:24] */
control |= ((u32)slot_id << 24);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Address Device failed");
return rc;
}
return 0;
}
/* ================================================================
* Phase 3: Configure Endpoint
* ================================================================ */
static int xhci_configure_endpoint(u8 slot_id) {
u64 input_ctx_phys = g_input_ctx.phys;
u32 control = (TRB_TYPE_CONFIGURE_EP << TRB_TYPE_SHIFT) | ((u32)slot_id << 24);
u64 out_param = 0;
u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) {
g_log->error("[xhci] Configure Endpoint failed");
return rc;
}
return 0;
}
/* ================================================================
* Phase 3: Control Transfer (EP0)
* ================================================================ */
static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
u8 direction) {
/* Setup Stage TRB */
u64 setup_val = ((u64)setup_pkt[4] << 32) | ((u64)setup_pkt[5] << 40) |
((u64)setup_pkt[6] << 48) | ((u64)setup_pkt[7] << 56);
setup_val |= (u64)setup_pkt[0] | ((u64)setup_pkt[1] << 8) |
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
u32 setup_status = 8; /* Setup packet length */
/* Setup Stage TRB(不设 IOC:控制传输只需在最后一个 TRB 上报完成,
* 中间 TRB 设 IOC 会让 QEMU/实机产生多余 Transfer Event,残留事件
* 污染后续 CMD_COMPLETE 轮询——日志表现为 "unexpected event type") */
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT);
/* Setup Stage TRB 必须设置 IDT (Immediate Data, bit6):
* setup 包 8 字节直接内嵌在 TRB parameter 字段。
* BUG-FIX: 原实现缺 IDT,QEMU xhci_fire_ctl_transfer 校验
* `!(trb_setup->control & TRB_TR_IDT)` 直接返回 -1 → 无 Transfer
* Event → 轮询超时(GET_DESCRIPTOR 失败)。 */
setup_control |= (1u << 6); /* TRB_TR_IDT */
/* Set direction in TRB: bit 16 of Setup Stage TRB */
if (direction) setup_control |= (1u << 16);
xhci_ring_enqueue(&g_ep0_ring, setup_val, setup_status, setup_control);
/* Data Stage TRB (if data_len > 0) */
if (data_len > 0 && data) {
/* Copy data to transfer buffer for OUT direction */
if (direction == 0) {
xhci_memcpy(g_xfer_buf.virt, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
u32 data_status = data_len;
/* Data Stage 不设 IOC(避免多余事件残留) */
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT);
if (direction) data_control |= (1u << 16); /* DIR=IN */
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
}
/* Status Stage TRB */
u32 status_dir = direction ? 0 : 1; /* opposite of data direction */
u32 status_control = (TRB_TYPE_STATUS_STAGE << TRB_TYPE_SHIFT) | TRB_IOC;
if (status_dir) status_control |= (1u << 16);
xhci_ring_enqueue(&g_ep0_ring, 0, 0, status_control);
xhci_mb();
xhci_ring_doorbell(g_msc.slot_id, 1); /* EP1 = EP0+1 */
/* Poll for Transfer Event */
u64 timeout_ms = 5000;
u64 out_param = 0;
u32 out_status = 0;
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, timeout_ms,
&out_param, &out_status, 0);
if (rc != 0) {
g_log->warn("[xhci] control transfer failed");
return rc;
}
/* For IN direction, copy data from DMA buffer */
if (direction && data_len > 0 && data) {
xhci_memcpy(data, g_xfer_buf.virt, data_len);
}
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return 0;
}
/* ================================================================
* Phase 3: USB Descriptor reads
* ================================================================ */
static int xhci_get_device_descriptor(u8 *out_desc) {
u8 setup[8];
setup[0] = 0x80; /* bmRequestType: Device-to-Host, Standard, Device */
setup[1] = 0x06; /* bRequest: GET_DESCRIPTOR */
setup[2] = 0x00; /* wValue low: Descriptor Index */
setup[3] = 0x01; /* wValue high: Device Descriptor */
setup[4] = 0x00; /* wIndex low */
setup[5] = 0x00; /* wIndex high */
setup[6] = 0x12; /* wLength low: 18 bytes */
setup[7] = 0x00; /* wLength high */
xhci_zero(out_desc, 18);
return xhci_control_transfer(setup, out_desc, 18, 1);
}
static int xhci_get_config_descriptor(u8 *out_desc, u32 max_len) {
u8 setup[8];
setup[0] = 0x80; /* Device-to-Host, Standard, Device */
setup[1] = 0x06; /* GET_DESCRIPTOR */
setup[2] = 0x00; /* Index */
setup[3] = 0x02; /* Configuration Descriptor */
setup[4] = 0x00;
setup[5] = 0x00;
setup[6] = (u8)(max_len & 0xFF);
setup[7] = (u8)((max_len >> 8) & 0xFF);
xhci_zero(out_desc, max_len);
return xhci_control_transfer(setup, out_desc, max_len, 1);
}
/* ================================================================
* Phase 4: Bulk Transfer
* ================================================================ */
static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
u8 direction, /* 0=OUT, 1=IN */
const void *data, u32 data_len) {
if (data_len == 0) return -1;
/* For OUT, copy data to DMA buffer */
if (direction == 0) {
xhci_memcpy(g_xfer_buf.virt, data, data_len);
__asm__ volatile("wbinvd" ::: "memory");
}
/* Enqueue Normal TRB(s) — single TRB for now (up to 64KB) */
u32 control = (TRB_TYPE_NORMAL << TRB_TYPE_SHIFT) | TRB_IOC;
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
xhci_mb();
/* Doorbell target = Endpoint ID:EPn OUT→2n,EPn IN→2n+1。
* BUG-FIX: 原实现传 EP 号(2/1),QEMU xhci_kick_ep 按 Endpoint ID
* 索引 eps[epid-1] → 敲错端点 → 无 Transfer Event → 轮询超时。 */
u8 db_target = (direction ? (u8)(2u * ep_num + 1) : (u8)(2u * ep_num));
xhci_ring_doorbell(g_msc.slot_id, db_target);
/* Poll for Transfer Event */
u64 out_param = 0;
u32 out_status = 0;
int rc = xhci_poll_event(EVT_TYPE_TRANSFER, 10000,
&out_param, &out_status, 0);
if (rc != 0) {
g_log->warn("[xhci] bulk transfer failed");
log_hex("[xhci] bulk rc=", (u64)(i64)rc);
return rc;
}
/* For IN, copy data from DMA buffer */
if (direction == 1) {
xhci_memcpy((void *)data, g_xfer_buf.virt, data_len);
}
/* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS,
mmio_read32(g_cap_length + XHCI_OP_USBSTS) | USBSTS_EINT);
return 0;
}
/* ================================================================
* Phase 4: BOT (Bulk-Only Transport) + SCSI
* ================================================================ */
static int xhci_bot_send_cbw(u8 lun, u8 flags, u32 data_len,
const u8 *cb, u8 cb_len) {
u8 cbw[31];
xhci_zero(cbw, 31);
/* Signature */
cbw[0] = 0x55; cbw[1] = 0x53; cbw[2] = 0x42; cbw[3] = 0x43;
/* Tag (little-endian) */
u32 tag = g_msc.cbw_tag++;
cbw[4] = (u8)(tag & 0xFF);
cbw[5] = (u8)((tag >> 8) & 0xFF);
cbw[6] = (u8)((tag >> 16) & 0xFF);
cbw[7] = (u8)((tag >> 24) & 0xFF);
/* Data Transfer Length (little-endian) */
cbw[8] = (u8)(data_len & 0xFF);
cbw[9] = (u8)((data_len >> 8) & 0xFF);
cbw[10] = (u8)((data_len >> 16) & 0xFF);
cbw[11] = (u8)((data_len >> 24) & 0xFF);
/* Flags */
cbw[12] = flags;
/* LUN */
cbw[13] = lun;
/* CB Length */
cbw[14] = cb_len;
/* Command Block */
for (u8 i = 0; i < cb_len && i < 16; i++)
cbw[15 + i] = cb[i];
return xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
0, cbw, 31);
}
static int xhci_bot_receive_csw(void) {
int rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, g_csw_buf.virt, 13);
if (rc != 0) return rc;
/* Verify CSW signature */
u8 *csw = (u8 *)g_csw_buf.virt;
if (csw[0] != 0x55 || csw[1] != 0x53 || csw[2] != 0x42 || csw[3] != 0x53) {
g_log->error("[xhci] CSW signature mismatch");
return -2;
}
/* CSW status: byte 12, 0=OK */
u8 status = csw[12];
if (status != 0) {
log_hex("[xhci] CSW status=", status);
return -3;
}
return 0;
}
/* SCSI INQUIRY */
static int xhci_scsi_inquiry(void) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_INQUIRY;
cb[4] = 36; /* allocation length */
int rc = xhci_bot_send_cbw(0, 0x80, 36, cb, 6);
if (rc != 0) return rc;
/* Data phase: IN 36 bytes */
u8 inq_data[36];
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, inq_data, 36);
if (rc != 0) return rc;
/* CSW */
rc = xhci_bot_receive_csw();
if (rc != 0) return rc;
/* Parse INQUIRY data */
u8 dev_type = inq_data[0] & 0x1F;
log_hex("[xhci] INQ PeripheralType=", dev_type);
g_log->info("[xhci] INQ Vendor:");
g_log->info((const char *)(inq_data + 8)); /* Not null-terminated but safe for log */
return (dev_type == 0x00) ? 0 : -1; /* 0 = Direct Access Block */
}
/* SCSI READ CAPACITY(10) */
static int xhci_scsi_read_capacity(u64 *out_sectors, u32 *out_blk_size) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_READ_CAPACITY10;
int rc = xhci_bot_send_cbw(0, 0x80, 8, cb, 10);
if (rc != 0) return rc;
/* Data phase: IN 8 bytes */
u8 cap_data[8];
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, cap_data, 8);
if (rc != 0) return rc;
/* CSW */
rc = xhci_bot_receive_csw();
if (rc != 0) return rc;
/* Parse big-endian */
u32 max_lba = ((u32)cap_data[0] << 24) | ((u32)cap_data[1] << 16) |
((u32)cap_data[2] << 8) | (u32)cap_data[3];
u32 blk_size = ((u32)cap_data[4] << 24) | ((u32)cap_data[5] << 16) |
((u32)cap_data[6] << 8) | (u32)cap_data[7];
*out_sectors = (u64)max_lba + 1; /* READ CAPACITY returns max LBA */
*out_blk_size = blk_size;
log_hex("[xhci] capacity sectors=", *out_sectors);
log_hex("[xhci] block size=", blk_size);
return 0;
}
/* SCSI TEST UNIT READY */
static int xhci_scsi_test_unit_ready(void) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_TEST_UNIT_READY;
int rc = xhci_bot_send_cbw(0, 0x00, 0, cb, 6);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* SCSI READ(10) */
static int xhci_scsi_read10(u64 lba, u32 count, void *buffer) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_READ10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
int rc = xhci_bot_send_cbw(0, 0x80, data_len, cb, 10);
if (rc != 0) return rc;
/* Data phase: IN */
rc = xhci_bulk_transfer(&g_bulk_in_ring, g_msc.bulk_in_ep,
1, buffer, data_len);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* SCSI WRITE(10) */
static int xhci_scsi_write10(u64 lba, u32 count, const void *buffer) {
u8 cb[16];
xhci_zero(cb, 16);
cb[0] = SCSI_WRITE10;
cb[2] = (u8)((lba >> 24) & 0xFF);
cb[3] = (u8)((lba >> 16) & 0xFF);
cb[4] = (u8)((lba >> 8) & 0xFF);
cb[5] = (u8)(lba & 0xFF);
cb[7] = (u8)((count >> 8) & 0xFF);
cb[8] = (u8)(count & 0xFF);
u32 data_len = count * 512;
int rc = xhci_bot_send_cbw(0, 0x00, data_len, cb, 10);
if (rc != 0) return rc;
/* Data phase: OUT */
rc = xhci_bulk_transfer(&g_bulk_out_ring, g_msc.bulk_out_ep,
0, buffer, data_len);
if (rc != 0) return rc;
return xhci_bot_receive_csw();
}
/* ================================================================
* Phase 4+5: Block device interface
* ================================================================ */
static int xhci_block_read(void *ctx, u64 lba, u32 count, void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(xhci_block_reads);
u8 *buf = (u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count; /* max 16 sectors (8KB) per SCSI cmd */
int rc = xhci_scsi_read10(lba, chunk, g_xfer_buf.virt);
if (rc != 0) {
log_hex("[xhci] READ10 failed lba=", lba);
return rc;
}
xhci_memcpy(buf, g_xfer_buf.virt, chunk * 512);
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
static int xhci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
(void)ctx;
if (!g_msc.ready || !buffer || count == 0) return -1;
DKM_STAT_INC(xhci_block_writes);
const u8 *buf = (const u8 *)buffer;
while (count > 0) {
u32 chunk = count > 16 ? 16 : count;
xhci_memcpy(g_xfer_buf.virt, buf, chunk * 512);
__asm__ volatile("wbinvd" ::: "memory"); /* Cache flush for DMA */
int rc = xhci_scsi_write10(lba, chunk, g_xfer_buf.virt);
if (rc != 0) {
log_hex("[xhci] WRITE10 failed lba=", lba);
return rc;
}
buf += chunk * 512;
lba += chunk;
count -= chunk;
}
return 0;
}
/* ================================================================
* Phase 3: Build Input Context for Address Device
* ================================================================ */
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0, u32 port_idx) {
(void)slot_id;
u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt;
xhci_zero(ic, g_input_ctx.size);
/* Input Control Context (xHCI 1.2 §6.4.3):
* DWORD0 = Drop Context Flags(本次全 0,不 drop 任何上下文)
* DWORD1 = Add Context Flags(bit0=Slot, bit1=EP0)
* BUG-FIX: 原实现把 Add flags 写进 DWORD0(Drop 槽),
* QEMU/实机校验 ictl_ctx[0]==0 && ictl_ctx[1]==0x3 直接失败 CC_TRB_ERROR。 */
u32 *icc = (u32 *)ic;
icc[0] = 0x0; /* Drop Context Flags */
icc[1] = (1u << 0) | (1u << 1); /* Add: Slot + EP0 */
/* Slot Context (§6.2.2):
* DWORD0: Route String [0:19] + Speed [20:22] + Context Entries [27:31]
* DWORD1: Max Exit Latency [0:7] + Root Hub Port Number [16:23]
* BUG-FIX: 原实现 speed<<29(占用 Context Entries 位域)、MPS0 写入
* DWORD1(规范 Slot Context 无 MPS0 字段,MPS0 在 EP0 Context)——
* QEMU xhci_lookup_uport 读 DWORD1[16:23] 找端口,原值 port=0 查不到
* 设备 → CC_TRB_ERROR。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 route_string = 0; /* direct-connect, no hub */
slot_ctx[0] = route_string | ((u32)speed << 20) | (1u << 27);
slot_ctx[1] = (port_idx + 1) << 16; /* Root Hub Port Number (1-based) */
/* EP0 Context starts at offset ctx_sz * 2
* Endpoint Context (§6.2.3) 布局:
* DWORD1: CErr[4:5] + EP Type[6:8] + Max Packet Size[16:31]
* DWORD2: Dequeue Pointer Low[4:31](bit0=DCS, bits1:3=reserved)
* DWORD3: Dequeue Pointer High
* DWORD4: Average TRB Length[0:15]
* BUG-FIX: 原实现把 dequeue 写到 DWORD6/7——QEMU xhci_init_epctx
* 从 DWORD2/3 读 dequeue(ctx[2]&~0xf, ctx[3]),读到 0 → ring 无
* dequeue → kick_epctx 直接返回,TRB 永不处理 → 无 Transfer Event。 */
u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2);
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
ep0_ctx[2] = (u32)(ep0_deq & 0xFFFFFFF0u) | (u32)g_ep0_ring.ccs;
ep0_ctx[3] = (u32)(ep0_deq >> 32);
ep0_ctx[4] = 8; /* Average TRB Length */
}
static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
u16 out_mps, u16 in_mps) {
u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt;
/* Input Control Context(Configure Endpoint 语义,xHCI 1.2 §6.4.3):
* DWORD0 = Drop(0),DWORD1 = Add。
* Configure 时 bit0=Slot 必须 Add,bit1=EP0 不可重复 Add
* (QEMU xhci_configure_slot 校验 (ictl[1]&0x3)==0x1)。
* 端点位用 Context Index:EPn OUT→2n,EPn IN→2n+1
* (QEMU 从 i=2 开始遍历 Add flags,按 ictx+32*i 读 EP Context)。 */
u32 *icc = (u32 *)ic;
u32 out_idx = 2u * bulk_out_ep;
u32 in_idx = 2u * bulk_in_ep + 1;
icc[0] = 0x0;
icc[1] = (1u << 0) | (1u << out_idx) | (1u << in_idx);
/* Update Slot Context: Context Entries = max Context Index
* (DWORD0 bits[27:31],保留 Speed/Route 原值)。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 max_idx = out_idx > in_idx ? out_idx : in_idx;
slot_ctx[0] &= ~(0x1Fu << 27);
slot_ctx[0] |= (max_idx << 27);
/* Bulk OUT Endpoint Context。
* 偏移 = ctx_sz * (out_idx + 1):input context 布局是
* [ICC][Slot][EP0][EP1]... 每个占 1 个 ctx_sz,Context Index i 的
* EP context 在 (i+1) 个槽处(QEMU xhci_configure_slot 用
* ictx+32+(32*i) 读取)。BUG-FIX: 原实现 ctx_sz*out_idx 少了 ICC
* 槽 → 全部错位 32 字节 → QEMU 读到零 EP context。 */
u32 *out_ctx = (u32 *)(ic + ctx_sz * (out_idx + 1));
out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16);
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
out_ctx[2] = (u32)(out_deq & 0xFFFFFFF0u) | (u32)g_bulk_out_ring.ccs;
out_ctx[3] = (u32)(out_deq >> 32);
out_ctx[4] = 512; /* Average TRB Length */
/* Bulk IN Endpoint Context (at offset ctx_sz * (in_idx + 1)) */
u32 *in_ctx = (u32 *)(ic + ctx_sz * (in_idx + 1));
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
in_ctx[2] = (u32)(in_deq & 0xFFFFFFF0u) | (u32)g_bulk_in_ring.ccs;
in_ctx[3] = (u32)(in_deq >> 32);
in_ctx[4] = 512; /* Average TRB Length */
}
/* ================================================================
* Phase 3: Full device enumeration
* ================================================================ */
static int xhci_enum_msc_device(u32 port_idx) {
u8 speed = xhci_get_port_speed(port_idx);
log_hex("[xhci] port speed=", speed);
/* Default MaxPacketSize for EP0 based on speed
* (xHCI 1.2 §4.3: Full=8? No — FS/HS EP0 通常 64,LS=8,SS=512) */
u16 mps0 = 64; /* Full/High-Speed default */
if (speed == USB_SPEED_LOW) mps0 = 8;
if (speed == USB_SPEED_SUPER) mps0 = 512;
/* Step 1: Enable Slot */
u8 slot_id = 0;
if (xhci_enable_slot(&slot_id) != 0) {
FB_ENUM_FAIL(0x0A); /* 黄: Enable Slot 失败 */
return -1;
}
g_msc.slot_id = slot_id;
g_msc.speed = speed;
/* Step 1.5: Allocate per-device DMA buffers(在 DCBAA 指向输出上下文之前) */
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_input_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_dev_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2;
if (g_api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_csw_buf) != 0) return -2;
if (xhci_ring_init(&g_ep0_ring, XFER_RING_SIZE) != 0) return -2;
if (xhci_ring_init(&g_bulk_out_ring, XFER_RING_SIZE) != 0) return -2;
if (xhci_ring_init(&g_bulk_in_ring, XFER_RING_SIZE) != 0) return -2;
/* DCBAA[slot_id] 必须指向输出上下文(Device Context)。
* Address Device/Configure Endpoint 命令都会读取该地址;
* 缺失时 QEMU/实机将 octx=0 → DMA 写失败/CC_TRB_ERROR。 */
xhci_zero(g_dev_ctx.virt, g_dev_ctx.size);
u64 *dcbaa = (u64 *)g_dcbaa.virt;
dcbaa[slot_id] = g_dev_ctx.phys;
log_hex("[xhci] DCBAA[slot]=", dcbaa[slot_id]);
/* Step 3: Build Input Context and Address Device (BSR=1 first) */
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 1) != 0) {
/* BSR=1 failed, try without BSR */
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) {
FB_ENUM_FAIL(0x0B); /* 黄: Address Device 失败(BSR=1/0 均失败) */
return -3;
}
}
/* Step 4: Get Device Descriptor */
u8 dev_desc[18];
if (xhci_get_device_descriptor(dev_desc) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Device) 失败 */
return -4;
}
u8 dev_class = dev_desc[4];
u16 vid = (u16)dev_desc[8] | ((u16)dev_desc[9] << 8);
u16 pid = (u16)dev_desc[10] | ((u16)dev_desc[11] << 8);
u8 real_mps0 = dev_desc[7];
log_hex("[xhci] dev class=", dev_class);
log_hex("[xhci] VID=", vid);
log_hex("[xhci] PID=", pid);
log_hex("[xhci] bMaxPacketSize0=", real_mps0);
/* Step 5: Re-address with real MPS if different */
if (real_mps0 != mps0 && real_mps0 >= 8) {
mps0 = real_mps0;
xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) {
g_log->warn("[xhci] re-address failed");
/* Continue anyway — device may still work */
}
}
/* Step 6: Get Configuration Descriptor */
u8 cfg_desc[255];
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Config) 失败 */
return -5;
}
/* Parse configuration descriptor to find MSC interface and endpoints */
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
if (total_len < 9) {
FB_ENUM_FAIL(0x0D); /* 黄: 配置描述符过短 */
return -5;
}
u8 bulk_out_ep = 0, bulk_in_ep = 0;
u16 bulk_out_mps = 512, bulk_in_mps = 512;
int msc_found = 0;
/* Walk through descriptors: config(9) + interface(9) + endpoints(7*N) */
u32 pos = 9; /* skip config descriptor */
while (pos + 2 <= (u32)total_len && pos + cfg_desc[pos] <= (u32)total_len) {
u8 desc_len = cfg_desc[pos];
u8 desc_type = cfg_desc[pos + 1];
if (desc_type == 4 && desc_len >= 9) {
/* Interface Descriptor */
u8 iface_class = cfg_desc[pos + 5];
u8 iface_subclass = cfg_desc[pos + 6];
u8 iface_protocol = cfg_desc[pos + 7];
if (iface_class == 0x08 && iface_subclass == 0x06 && iface_protocol == 0x50) {
msc_found = 1;
g_log->info("[xhci] MSC BOT interface found");
/* Scan following endpoint descriptors.
* SuperSpeed 设备在 endpoint 后紧跟 6 字节 SS Endpoint
* Companion descriptor (type 0x30)——必须跳过继续找下一
* 个 endpoint,不能因 type!=5 提前终止(Bulk OUT 常在
* IN 之后且被 companion 隔开)。 */
u32 ep_pos = pos + desc_len;
while (ep_pos + 2 <= (u32)total_len) {
u8 ep_len = cfg_desc[ep_pos];
if (ep_len == 0 || ep_pos + ep_len > (u32)total_len) break;
if (cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */
u8 ep_addr = cfg_desc[ep_pos + 2];
u8 ep_attr = cfg_desc[ep_pos + 3];
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
if ((ep_attr & 0x03) == 0x02) { /* Bulk */
if (ep_addr & 0x80) {
bulk_in_ep = ep_addr & 0x0F;
bulk_in_mps = ep_mps;
} else {
bulk_out_ep = ep_addr & 0x0F;
bulk_out_mps = ep_mps;
}
}
} else if (cfg_desc[ep_pos + 1] == 4) {
/* 下一个 interface — 停止扫描 */
break;
}
ep_pos += ep_len;
}
break;
}
}
pos += desc_len;
}
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
g_log->warn("[xhci] no MSC BOT interface found");
FB_ENUM_FAIL(0x0D); /* 黄: 未找到 MSC BOT 接口/端点 */
return -6;
}
log_hex("[xhci] Bulk OUT EP=", bulk_out_ep);
log_hex("[xhci] Bulk IN EP=", bulk_in_ep);
log_hex("[xhci] Bulk OUT MPS=", bulk_out_mps);
log_hex("[xhci] Bulk IN MPS=", bulk_in_mps);
g_msc.bulk_out_ep = bulk_out_ep;
g_msc.bulk_in_ep = bulk_in_ep;
g_msc.bulk_out_mps = bulk_out_mps;
g_msc.bulk_in_mps = bulk_in_mps;
/* Step 7: Configure Endpoint */
xhci_zero(g_input_ctx.virt, g_input_ctx.size);
xhci_build_input_ctx_bulk_eps(bulk_out_ep, bulk_in_ep,
bulk_out_mps, bulk_in_mps);
if (xhci_configure_endpoint(slot_id) != 0) {
g_log->warn("[xhci] Configure Endpoint failed");
FB_ENUM_FAIL(0x0E); /* 黄: Configure Endpoint 失败 */
return -7;
}
/* Step 8: SCSI INQUIRY to verify MSC device */
if (xhci_scsi_inquiry() != 0) {
g_log->warn("[xhci] INQUIRY failed or not block device");
/* Continue anyway — some devices return non-zero peripheral type */
}
/* Step 9: TEST UNIT READY (retry up to 5 times) */
for (int i = 0; i < 5; i++) {
if (xhci_scsi_test_unit_ready() == 0) break;
dkm_delay_ms(100);
}
/* Step 10: READ CAPACITY */
u64 sectors = 0;
u32 blk_size = 512;
if (xhci_scsi_read_capacity(&sectors, &blk_size) != 0) {
g_log->warn("[xhci] READ CAPACITY failed");
/* Use defaults */
sectors = 0;
blk_size = 512;
}
g_msc.sector_count = sectors;
g_msc.sector_size = blk_size;
g_msc.cbw_tag = 1;
g_msc.ready = 1;
DKM_STAT_INC(xhci_msc_found);
log_hex("[xhci] MSC device ready, sectors=", sectors);
log_hex("[xhci] MSC device block_size=", blk_size);
return 0;
}
/* ================================================================
* driver_init — main entry point
* ================================================================ */
#define XHCI_TRY_NEXT do { xhci_nth++; if (xhci_nth < 4) goto again_ctrl; return 0; } while (0)
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
dkm_tsc_calibrate();
if (!api || !api->log) return -1;
g_api = api;
g_log = api->log;
dkm_instr_init(api);
g_log->info("[xhci] init begin");
/* ---- Phase 1: PCI discovery ---- */
u8 bus = 0, dev = 0, func = 0;
/* 实机常有不止一只 xHCI。U 盘可能不在第一只上,找不到 MSC 就试下一只。 */
int xhci_nth = 0;
again_ctrl:
if (xhci_find_nth(xhci_nth, &bus, &dev, &func) != 0) {
g_log->info("[xhci] xHCI controller not found");
/* BUG-FIX: 不 halt。xhci 是可选驱动(manifest required=false),
* 无 xHCI 的平台(Intel 6 系/纯 EHCI)必须继续引导到 EHCI/DSK,
* 否则 `for(;;)hlt` 卡死整条启动链。仅记日志,如需诊断可开
* xhci_pci_diag_code() 打印控制器清单(见注释)。 */
return 0;
}
g_pci_bus = bus; g_pci_dev = dev; g_pci_func = func;
DKM_STAT_INC(xhci_pci_found);
g_log->info("[xhci] xHCI controller found");
log_hex("[xhci] bus=", bus);
log_hex("[xhci] dev=", dev);
log_hex("[xhci] func=", func);
u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID);
log_hex("[xhci] vendor=", vd & 0xffff);
log_hex("[xhci] device=", vd >> 16);
/* Intel PCH 默认把 USB2 口留在 EHCI。不把路由切到 xHCI,
* U 盘在 xHCI 上永远没有 CCS,块设备就不会注册。 */
if ((vd & 0xffff) == 0x8086) {
u32 usb2_mask = dkm_pci_read(bus, dev, func, 0xD4);
u32 usb3_mask = dkm_pci_read(bus, dev, func, 0xDC);
if (usb2_mask) dkm_pci_write(bus, dev, func, 0xD0, usb2_mask);
if (usb3_mask) dkm_pci_write(bus, dev, func, 0xD8, usb3_mask);
log_hex("[xhci] intel usb2 route=", usb2_mask);
log_hex("[xhci] intel usb3 route=", usb3_mask);
dkm_delay_ms(200);
}
/* Enable PCI MEM + BUSM, disable INTx (polling mode) */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
dkm_pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[xhci] PCI command=", command);
/* Read BAR0 (64-bit) */
u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0);
u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1);
u64 bar_phys;
if (bar0_lo & 1u) {
g_log->warn("[xhci] BAR0 is IO space; unsupported");
FB_FATAL(api, 0x02); /* 红: BAR0 是 IO 空间 */
XHCI_TRY_NEXT;
}
if ((bar0_lo & 0x6u) == 0x4u) {
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
} else {
bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u);
}
u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[xhci] BAR0 phys=", bar_phys);
log_hex("[xhci] IRQ line=", irq_line);
log_hex("[xhci] HHDM offset=", api->hhdm_offset);
if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[xhci] missing HHDM or BAR0; skip");
FB_FATAL(api, 0x02); /* 红: 缺 HHDM/BAR */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: MMIO mapping ---- */
g_mmio = xhci_map_bar(api, bar_phys);
if (!g_mmio) {
g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
FB_FATAL(api, 0x03); /* 红: BAR MMIO 映射失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: Parse capabilities ---- */
if (xhci_parse_caps() != 0) {
g_log->error("[xhci] capability parse failed");
FB_FATAL(api, 0x04); /* 红: capability 解析失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 1: BIOS Handoff ---- */
xhci_bios_handoff();
/* ---- Phase 1: Controller Reset ---- */
if (xhci_controller_reset() != 0) {
g_log->error("[xhci] controller reset failed");
FB_FATAL(api, 0x05); /* 红: 控制器 reset 失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
if (!api->dma) {
g_log->warn("[xhci] DMA API unavailable");
FB_FATAL(api, 0x06); /* 红: DMA API 缺失 */
XHCI_TRY_NEXT;
}
if (xhci_setup_rings() != 0) {
g_log->error("[xhci] ring setup failed");
FB_FATAL(api, 0x06); /* 红: ring 初始化失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 2: Start Controller ---- */
if (xhci_controller_start() != 0) {
g_log->error("[xhci] controller start failed");
FB_FATAL(api, 0x07); /* 红: 控制器启动失败 */
XHCI_TRY_NEXT;
}
/* ---- Phase 3: Port Scan + Device Enumeration ---- */
/*
* Wait 100ms after controller start for QEMU to complete USB device
* attachment and update port status. Without this delay, PORTSC.CCS
* may read 0 even though a device is physically connected.
*/
/* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。
* 20ms 就读 CCS 时,实机上的 U 盘还没出现,于是不会注册块设备。 */
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
if (g_ppc && !(portsc & (1u << 9))) {
mmio_write32(port_off, portsc | (1u << 9));
}
}
g_log->info("[xhci] waiting 1500ms for port settle...");
dkm_delay_ms(1500);
g_msc.ready = 0;
int saw_ccs = 0;
int saw_reset_fail = 0;
for (int pass = 0; pass < 2 && !g_msc.ready; pass++) {
if (pass == 1) {
g_log->info("[xhci] second port scan");
dkm_delay_ms(1500);
}
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
log_hex("[xhci] port=", port);
log_hex("[xhci] PORTSC=", portsc);
if (!(portsc & PORTSC_CCS)) continue;
saw_ccs = 1;
g_log->info("[xhci] device connected");
if (xhci_reset_port(port) != 0) {
g_log->warn("[xhci] port reset failed");
saw_reset_fail = 1;
continue;
}
g_log->info("[xhci] port reset OK");
if (xhci_enum_msc_device(port) == 0) {
g_log->info("[xhci] MSC enumeration successful");
break;
}
g_log->warn("[xhci] MSC enumeration failed; trying next port");
}
}
if (!g_msc.ready && saw_ccs && xhci_nth >= 3) {
g_log->panic(saw_reset_fail ? "XHC-E01 PORT RESET FAILED"
: "XHC-E02 MSC ENUM FAILED");
}
/* ---- Phase 5: Register Block Device ---- */
if (g_msc.ready) {
if (api->block && api->block->register_device) {
struct dkm_block_device_desc desc;
desc.name = "usb0";
desc.sector_size = g_msc.sector_size;
desc.sector_count = g_msc.sector_count;
desc.ctx = 0;
desc.read = xhci_block_read;
desc.write = xhci_block_write;
int index = api->block->register_device(&desc);
log_hex("[xhci] block provider index=", (u64)(i64)index);
} else {
g_log->warn("[xhci] block API unavailable; provider not registered");
}
} else {
g_log->info("[xhci] no MSC device found; block provider not registered");
if (xhci_nth < 3) {
xhci_nth++;
g_log->info("[xhci] trying next controller");
goto again_ctrl;
}
FB_FATAL(api, 0x08); /* 黄: 所有端口无设备/枚举全部失败 */
}
g_log->info("[xhci] driver ready");
return 0;
}
#undef XHCI_TRY_NEXT
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* Halt controller if it was started */
if (g_mmio) {
u32 usbcmd = mmio_read32(g_cap_length + XHCI_OP_USBCMD);
mmio_write32(g_cap_length + XHCI_OP_USBCMD, usbcmd & ~USBCMD_RS);
}
return 0;
}
@@ -0,0 +1,16 @@
# Build for Windows PE64 without any MSVC toolchain:
# - lld-link (LLVM) links directly in MSVC flavor
# - all Win32 imports use raw-dylib (no import .lib files needed)
# - no CRT: we provide mainCRTStartup ourselves and link /NODEFAULTLIB
[build]
target = "x86_64-pc-windows-msvc"
[target.x86_64-pc-windows-msvc]
linker = "C:/Program Files/LLVM/bin/lld-link.exe"
rustflags = [
"-C", "link-arg=/NODEFAULTLIB",
"-C", "link-arg=/SUBSYSTEM:CONSOLE",
"-C", "link-arg=/ENTRY:mainCRTStartup",
"-C", "link-arg=/STACK:4194304",
"-C", "link-arg=/DEBUG:NONE",
]
+240
View File
@@ -0,0 +1,240 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ab_glyph"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "deaicup-pe"
version = "0.1.0"
dependencies = [
"egui",
]
[[package]]
name = "ecolor"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
[[package]]
name = "egui"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
dependencies = [
"ahash",
"epaint",
"nohash-hasher",
]
[[package]]
name = "emath"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
[[package]]
name = "epaint"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
dependencies = [
"ab_glyph",
"ahash",
"ecolor",
"emath",
"nohash-hasher",
"parking_lot",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "owned_ttf_parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "zerocopy"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "deaicup-pe"
version = "0.1.0"
edition = "2021"
# Deaicup Software — Windows PE64 target for Deshab UTSM PE compat layer.
# egui UI logic is kept; the eframe runtime is replaced by a hand-written
# Win32 backend (user32 window + GDI DIB blit) plus a pure-Rust software
# rasterizer for egui tessellated primitives.
[[bin]]
name = "deaicup"
path = "src/main.rs"
[dependencies]
egui = { version = "=0.27.2", default-features = false, features = ["default_fonts"] }
[profile.release]
opt-level = 2
debug = false
debug-assertions = false
lto = "thin"
codegen-units = 1
panic = "abort"
+438
View File
@@ -0,0 +1,438 @@
//! Deaicup Software — Windows PE64 entry point.
//!
//! The original application runs on eframe/egui. For the Deshab UTSM PE
//! compatibility layer we keep the egui UI logic but replace the eframe
//! runtime with a minimal hand-written Win32 backend:
//! user32 window + message pump -> egui RawInput events
//! egui tessellation -> SoftRaster (pure Rust, raster.rs)
//! 32bpp DIB backbuffer -> GDI BitBlt onto the window DC
//!
//! No CRT: the PE entry is `mainCRTStartup` (see .cargo/config.toml) and the
//! process ends via ExitProcess.
#![no_main]
#![allow(static_mut_refs)]
mod raster;
#[allow(dead_code)]
mod rt;
mod win32;
use egui::{self, Color32, Context, Event, Key, Modifiers, PointerButton, Pos2, Rect};
use raster::SoftRaster;
use win32::*;
/* ============================================================
* UI 逻辑(移植自原 eframe 应用 CODE/Deaicup Software/src)
* ========================================================== */
#[derive(Debug, PartialEq, Clone, Copy, Default)]
enum Page {
#[default]
Home,
Tool,
Game,
LinuxWare,
WindowsWare,
Set,
About,
}
#[derive(Default)]
struct DeaicupSoftware {
current_page: Page,
}
impl DeaicupSoftware {
fn ui(&mut self, ctx: &Context) {
// 原应用顶部是远程图片 https://deaicup.com/.../top.png。
// PE 环境无网络,用标题栏替代,保留 75px 高度布局。
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui| {
ui.set_min_height(75.0);
ui.vertical_centered(|ui| {
ui.add_space(15.0);
ui.label(egui::RichText::new("Deaicup Software").size(30.0).strong());
});
});
// main
egui::SidePanel::left("navigation_panel")
.default_width(200.0)
.resizable(true)
.show(ctx, |ui| {
ui.heading("Menu");
ui.separator();
ui.vertical(|ui| {
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked() {
self.current_page = Page::Home;
}
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked() {
self.current_page = Page::Tool;
}
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked() {
self.current_page = Page::Game;
}
if ui
.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE")
.clicked()
{
self.current_page = Page::LinuxWare;
}
if ui
.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE")
.clicked()
{
self.current_page = Page::WindowsWare;
}
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked() {
self.current_page = Page::Set;
}
if ui
.selectable_label(self.current_page == Page::About, "ABOUT")
.clicked()
{
self.current_page = Page::About;
}
})
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.separator();
match self.current_page {
Page::Home => ui.label("Home"),
Page::Tool => ui.label("Tool"),
Page::Game => ui.label("Game"),
Page::LinuxWare => ui.label("LinuxWare"),
Page::WindowsWare => ui.label("WindowsWare"),
Page::Set => ui.label("Set"),
Page::About => ui.label("About"),
}
});
}
}
/* ============================================================
* 全局输入状态(单线程:wnd_proc 只被 DispatchMessageW 同步调用)
* ========================================================== */
static mut EVENTS: Vec<Event> = Vec::new();
static mut POINTER: Pos2 = Pos2::ZERO;
static mut MOD_ALT: bool = false;
static mut MOD_CTRL: bool = false;
static mut MOD_SHIFT: bool = false;
static mut QUIT: bool = false;
fn current_modifiers() -> Modifiers {
unsafe {
Modifiers {
alt: MOD_ALT,
ctrl: MOD_CTRL,
shift: MOD_SHIFT,
mac_cmd: false,
command: MOD_CTRL,
}
}
}
#[inline]
fn lparam_x(l: LPARAM) -> i32 {
(l as i32 & 0xFFFF) as i16 as i32
}
#[inline]
fn lparam_y(l: LPARAM) -> i32 {
((l as i32 >> 16) & 0xFFFF) as i16 as i32
}
fn push_pointer_button(button: PointerButton, pressed: bool, l: LPARAM) -> LRESULT {
unsafe {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerButton {
pos: POINTER,
button,
pressed,
modifiers: current_modifiers(),
});
}
0
}
fn vk_to_egui_key(vk: usize) -> Option<Key> {
// 字符键不需要映射为 egui::Key —— 文本输入走 WM_CHAR -> Event::Text。
Some(match vk {
VK_LEFT => Key::ArrowLeft,
VK_UP => Key::ArrowUp,
VK_RIGHT => Key::ArrowRight,
VK_DOWN => Key::ArrowDown,
VK_BACK => Key::Backspace,
VK_DELETE => Key::Delete,
VK_RETURN => Key::Enter,
VK_TAB => Key::Tab,
VK_ESCAPE => Key::Escape,
VK_SPACE => Key::Space,
_ => return None,
})
}
fn on_key(vk: usize, pressed: bool) -> LRESULT {
unsafe {
match vk {
VK_SHIFT => MOD_SHIFT = pressed,
VK_CONTROL => MOD_CTRL = pressed,
VK_MENU => MOD_ALT = pressed,
_ => {}
}
if let Some(key) = vk_to_egui_key(vk) {
EVENTS.push(Event::Key {
key,
physical_key: None,
pressed,
repeat: false,
modifiers: current_modifiers(),
});
}
}
0
}
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w: WPARAM, l: LPARAM) -> LRESULT {
unsafe {
match msg {
WM_CLOSE | WM_DESTROY => {
QUIT = true;
PostQuitMessage(0);
0
}
WM_MOUSEMOVE => {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerMoved(POINTER));
0
}
WM_LBUTTONDOWN => push_pointer_button(PointerButton::Primary, true, l),
WM_LBUTTONUP => push_pointer_button(PointerButton::Primary, false, l),
WM_RBUTTONDOWN => push_pointer_button(PointerButton::Secondary, true, l),
WM_RBUTTONUP => push_pointer_button(PointerButton::Secondary, false, l),
WM_MBUTTONDOWN => push_pointer_button(PointerButton::Middle, true, l),
WM_MBUTTONUP => push_pointer_button(PointerButton::Middle, false, l),
WM_KEYDOWN => on_key(w, true),
WM_KEYUP => on_key(w, false),
WM_CHAR => {
if let Some(c) = char::from_u32(w as u32) {
if c >= ' ' && c != '\x7f' {
EVENTS.push(Event::Text(c.to_string()));
}
}
0
}
WM_ERASEBKGND => 1, // 背景由 egui 全量绘制
_ => DefWindowProcW(hwnd, msg, w, l),
}
}
}
/* ============================================================
* 计时
* ========================================================== */
fn now_secs() -> f64 {
unsafe {
static mut FREQ: i64 = 0;
if FREQ == 0 {
QueryPerformanceFrequency(&mut FREQ);
if FREQ == 0 {
FREQ = 1000;
}
}
let mut c: i64 = 0;
QueryPerformanceCounter(&mut c);
c as f64 / FREQ as f64
}
}
/* ============================================================
* PE 入口
* ========================================================== */
/// ExitProcess wrapper: restore the kernel GS base before leaving.
fn exit_process(code: u32) -> ! {
unsafe {
rt::restore_tls();
ExitProcess(code);
}
}
#[no_mangle]
pub extern "C" fn mainCRTStartup() {
unsafe {
// Fake TEB/TLS must precede any std facility that might touch
// thread locals (parking_lot, std::io, etc.).
rt::install_fake_tls();
}
log("[deaicup] mainCRTStartup\n");
unsafe {
let hinst = GetModuleHandleW(core::ptr::null());
let class_name = wide("DeaicupWindow");
let wc = WNDCLASSEXW {
cbSize: core::mem::size_of::<WNDCLASSEXW>() as u32,
style: 0,
lpfnWndProc: wnd_proc,
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinst,
hIcon: 0,
hCursor: LoadCursorW(0, IDC_ARROW),
hbrBackground: 0,
lpszMenuName: core::ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: 0,
};
RegisterClassExW(&wc);
// 屏幕尺寸(shim 报 framebuffer 尺寸),窗口 800x600,超出则收缩
let sw = GetSystemMetrics(SM_CXSCREEN);
let sh = GetSystemMetrics(SM_CYSCREEN);
let mut ww = 800;
let mut wh = 600;
if sw > 0 && ww > sw {
ww = sw;
}
if sh > 0 && wh > sh {
wh = sh;
}
let title = wide("Deaicup Software");
let hwnd = CreateWindowExW(
0,
class_name.as_ptr(),
title.as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
ww,
wh,
0,
0,
hinst,
core::ptr::null_mut(),
);
if hwnd == 0 {
log("[deaicup] CreateWindowExW failed\n");
exit_process(1);
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 32bpp top-down DIB 后台缓冲
let hdc = GetDC(hwnd);
let memdc = CreateCompatibleDC(hdc);
let mut bmi = BITMAPINFO::default();
bmi.bmiHeader.biSize = core::mem::size_of::<BITMAPINFOHEADER>() as u32;
bmi.bmiHeader.biWidth = ww;
bmi.bmiHeader.biHeight = -wh; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
let mut bits: *mut core::ffi::c_void = core::ptr::null_mut();
let hbmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &mut bits, 0, 0);
if hbmp == 0 || bits.is_null() {
log("[deaicup] CreateDIBSection failed\n");
exit_process(1);
}
SelectObject(memdc, hbmp);
log("[dc] SelectObject ok\n");
/* 二分探针:逐项验证 std 设施,定位 Context::default 崩溃点 */
{
let mut v: Vec<u8> = Vec::with_capacity(16);
for i in 0..64u8 { v.push(i); }
log("[dc] vec ok\n");
let s = String::from("hello");
let _ = s.len();
log("[dc] string ok\n");
let mut h: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
h.insert(1, 2); h.insert(3, 4);
let _ = h.get(&1);
log("[dc] hashmap ok\n");
let a = std::sync::Arc::new(42u32);
let b = a.clone();
let _ = *b;
log("[dc] arc ok\n");
let m = std::sync::Mutex::new(7u32);
*m.lock().unwrap() = 8;
log("[dc] mutex ok\n");
}
let ctx = Context::default();
log("[dc] Context::default ok\n");
ctx.set_pixels_per_point(1.0);
let mut app = DeaicupSoftware::default();
let mut raster = SoftRaster::new();
let mut msg = MSG::default();
let mut frame: u64 = 0;
log("[dc] app+raster ok\n");
while !QUIT {
// 消息泵:把 Win32 消息转成 egui 事件(由 wnd_proc 推入 EVENTS)
while PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != 0 {
if msg.message == WM_QUIT {
QUIT = true;
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if QUIT {
break;
}
if frame < 3 { log("[dc] msgpump ok\n"); }
let events = core::mem::take(&mut *core::ptr::addr_of_mut!(EVENTS));
let had_events = !events.is_empty();
let input = egui::RawInput {
screen_rect: Some(Rect::from_min_size(
Pos2::ZERO,
egui::vec2(ww as f32, wh as f32),
)),
time: Some(now_secs()),
modifiers: current_modifiers(),
events,
..Default::default()
};
if frame < 3 { log("[dc] rawinput ok\n"); }
let full = ctx.run(input, |ctx| app.ui(&ctx));
if frame < 3 { log("[dc] ctx.run ok\n"); }
let repaint_after = full
.viewport_output
.values()
.next()
.map(|v| v.repaint_delay)
.unwrap_or(std::time::Duration::from_millis(16));
raster.apply_delta(&full.textures_delta);
if frame < 3 { log("[dc] apply_delta ok\n"); }
let prims = ctx.tessellate(full.shapes, full.pixels_per_point);
if frame < 3 { log("[dc] tessellate ok\n"); }
let dst = core::slice::from_raw_parts_mut(bits as *mut u32, (ww * wh) as usize);
raster::clear(dst, Color32::from_rgb(0x30, 0x34, 0x3d));
raster.paint(dst, ww, wh, &prims);
if frame < 3 { log("[dc] paint ok\n"); }
BitBlt(hdc, 0, 0, ww, wh, memdc, 0, 0, SRCCOPY);
if frame < 3 { log("[dc] bitblt ok\n"); }
frame += 1;
if frame > 2 && !had_events && repaint_after > std::time::Duration::from_millis(5) {
let ms = repaint_after.as_millis().min(50) as u32;
Sleep(if ms == 0 { 5 } else { ms });
}
}
log("[deaicup] quit, ExitProcess(0)\n");
exit_process(0);
}
}
+241
View File
@@ -0,0 +1,241 @@
//! Pure-Rust software rasterizer for egui tessellated primitives.
//! Renders `ClippedPrimitive` meshes (font atlas + color triangles) into a
//! 32bpp 0xAARRGGBB backbuffer that is later blitted via GDI BitBlt.
use egui::epaint::Primitive;
use egui::{ClippedPrimitive, Color32, Rect, TextureFilter, TextureId, TexturesDelta};
struct Texture {
w: usize,
h: usize,
/// RGBA8, row-major.
rgba: Vec<u8>,
linear: bool,
}
pub struct SoftRaster {
/// Small texture store (font atlas + user images). Vec-based to avoid
/// std HashMap RandomState (which would import an OS RNG API).
textures: Vec<(TextureId, Texture)>,
}
impl SoftRaster {
pub fn new() -> Self {
SoftRaster {
textures: Vec::new(),
}
}
fn get(&self, id: TextureId) -> Option<&Texture> {
self.textures.iter().find(|(tid, _)| *tid == id).map(|(_, t)| t)
}
/// Apply egui's texture updates (font atlas etc).
pub fn apply_delta(&mut self, delta: &TexturesDelta) {
for (id, img_delta) in &delta.set {
let size = img_delta.image.size();
let mut rgba = Vec::with_capacity(size[0] * size[1] * 4);
match &img_delta.image {
egui::ImageData::Color(color) => {
for p in &color.pixels {
rgba.extend_from_slice(&p.to_array());
}
}
egui::ImageData::Font(font) => {
// Coverage -> straight-alpha white texel (255,255,255,a).
for c in font.srgba_pixels(None) {
let a = c.a();
rgba.extend_from_slice(&[255, 255, 255, a]);
}
}
}
let linear = img_delta.options.magnification == TextureFilter::Linear;
let tex = Texture {
w: size[0],
h: size[1],
rgba,
linear,
};
if let Some(slot) = self.textures.iter_mut().find(|(tid, _)| tid == id) {
slot.1 = tex;
} else {
self.textures.push((*id, tex));
}
}
for id in &delta.free {
self.textures.retain(|(tid, _)| tid != id);
}
}
/// Paint all clipped primitives into `dst` (0xAARRGGBB pixels, dw*dh).
pub fn paint(&self, dst: &mut [u32], dw: i32, dh: i32, prims: &[ClippedPrimitive]) {
for cp in prims {
if let Primitive::Mesh(mesh) = &cp.primitive {
let Some(tex) = self.get(mesh.texture_id) else {
continue;
};
let clip = clip_rect_px(&cp.clip_rect, dw, dh);
if clip.2 <= clip.0 || clip.3 <= clip.1 {
continue;
}
for tri in mesh.indices.chunks_exact(3) {
let v0 = &mesh.vertices[tri[0] as usize];
let v1 = &mesh.vertices[tri[1] as usize];
let v2 = &mesh.vertices[tri[2] as usize];
raster_tri(dst, dw, dh, clip, v0, v1, v2, tex);
}
}
}
}
}
/// (x0, y0, x1, y1) integer clip bounds from an egui rect (points == pixels here).
fn clip_rect_px(r: &Rect, dw: i32, dh: i32) -> (i32, i32, i32, i32) {
let x0 = (r.min.x.floor() as i32).clamp(0, dw);
let y0 = (r.min.y.floor() as i32).clamp(0, dh);
let x1 = (r.max.x.ceil() as i32).clamp(0, dw);
let y1 = (r.max.y.ceil() as i32).clamp(0, dh);
(x0, y0, x1, y1)
}
#[inline]
fn edge(ax: f32, ay: f32, bx: f32, by: f32, px: f32, py: f32) -> f32 {
(px - ax) * (by - ay) - (py - ay) * (bx - ax)
}
fn raster_tri(
dst: &mut [u32],
dw: i32,
_dh: i32,
clip: (i32, i32, i32, i32),
v0: &egui::epaint::Vertex,
v1: &egui::epaint::Vertex,
v2: &egui::epaint::Vertex,
tex: &Texture,
) {
let x0 = v0.pos.x;
let y0 = v0.pos.y;
let x1 = v1.pos.x;
let y1 = v1.pos.y;
let x2 = v2.pos.x;
let y2 = v2.pos.y;
let area = edge(x0, y0, x1, y1, x2, y2);
if area.abs() < 1e-6 {
return;
}
let inv_area = 1.0 / area;
let min_x = ((x0.min(x1).min(x2)).floor() as i32).max(clip.0);
let min_y = ((y0.min(y1).min(y2)).floor() as i32).max(clip.1);
let max_x = ((x0.max(x1).max(x2)).ceil() as i32).min(clip.2);
let max_y = ((y0.max(y1).max(y2)).ceil() as i32).min(clip.3);
if min_x >= max_x || min_y >= max_y {
return;
}
for py in min_y..max_y {
let fy = py as f32 + 0.5;
let mut px = min_x;
// Row start barycentrics
let mut w0 = edge(x1, y1, x2, y2, px as f32 + 0.5, fy) * inv_area;
let mut w1 = edge(x2, y2, x0, y0, px as f32 + 0.5, fy) * inv_area;
let mut w2 = 1.0 - w0 - w1;
// Per-pixel increments (dx = +1).
// d/dpx edge(a, b, P) = (b.y - a.y):
// w0 = edge(p1, p2, P) -> (y2 - y1)
// w1 = edge(p2, p0, P) -> (y0 - y2)
// 符号写反会让 u/v/顶点色沿 +x 反向插值:文字逐行起始像素正确、
// 向右逐渐反向采样图集(字形水平镜像/竖条碎裂),纯色面板因
// 采样恒白区域不可见而掩盖了该 bug。
let dw0 = (y2 - y1) * inv_area;
let dw1 = (y0 - y2) * inv_area;
while px < max_x {
if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 {
let u = w0 * v0.uv.x + w1 * v1.uv.x + w2 * v2.uv.x;
let v = w0 * v0.uv.y + w1 * v1.uv.y + w2 * v2.uv.y;
let t = sample(tex, u, v);
let c0 = v0.color;
let c1 = v1.color;
let c2 = v2.color;
let cr = (w0 * c0.r() as f32 + w1 * c1.r() as f32 + w2 * c2.r() as f32) as u32;
let cg = (w0 * c0.g() as f32 + w1 * c1.g() as f32 + w2 * c2.g() as f32) as u32;
let cb = (w0 * c0.b() as f32 + w1 * c1.b() as f32 + w2 * c2.b() as f32) as u32;
let ca = (w0 * c0.a() as f32 + w1 * c1.a() as f32 + w2 * c2.a() as f32) as u32;
// src color = vertex * texture (straight), alpha = va * ta
let sa = (ca * t[3] as u32 + 127) / 255;
if sa != 0 {
let sr = (cr * t[0] as u32 + 127) / 255;
let sg = (cg * t[1] as u32 + 127) / 255;
let sb = (cb * t[2] as u32 + 127) / 255;
let idx = (py * dw + px) as usize;
let d = dst[idx];
let dr = (d >> 16) & 0xFF;
let dg = (d >> 8) & 0xFF;
let db = d & 0xFF;
let inv = 255 - sa;
let or = (sr * sa + dr * inv + 127) / 255;
let og = (sg * sa + dg * inv + 127) / 255;
let ob = (sb * sa + db * inv + 127) / 255;
dst[idx] = 0xFF00_0000 | (or << 16) | (og << 8) | ob;
}
}
px += 1;
w0 += dw0;
w1 += dw1;
w2 = 1.0 - w0 - w1;
}
}
}
#[inline]
fn sample(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
if tex.linear {
sample_linear(tex, u, v)
} else {
sample_nearest(tex, u, v)
}
}
#[inline]
fn sample_nearest(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let x = (u * tex.w as f32 - 0.5).round() as i32;
let y = (v * tex.h as f32 - 0.5).round() as i32;
texel(tex, x, y)
}
fn sample_linear(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let fx = u * tex.w as f32 - 0.5;
let fy = v * tex.h as f32 - 0.5;
let x0 = fx.floor() as i32;
let y0 = fy.floor() as i32;
let tx = ((fx - x0 as f32) * 256.0) as i32;
let ty = ((fy - y0 as f32) * 256.0) as i32;
let p00 = texel(tex, x0, y0);
let p10 = texel(tex, x0 + 1, y0);
let p01 = texel(tex, x0, y0 + 1);
let p11 = texel(tex, x0 + 1, y0 + 1);
let mut out = [0u8; 4];
for i in 0..4 {
let top = (p00[i] as i32 * (256 - tx) + p10[i] as i32 * tx) >> 8;
let bot = (p01[i] as i32 * (256 - tx) + p11[i] as i32 * tx) >> 8;
out[i] = ((top * (256 - ty) + bot * ty) >> 8) as u8;
}
out
}
#[inline]
fn texel(tex: &Texture, x: i32, y: i32) -> [u8; 4] {
let x = x.clamp(0, tex.w as i32 - 1) as usize;
let y = y.clamp(0, tex.h as i32 - 1) as usize;
let i = (y * tex.w + x) * 4;
[tex.rgba[i], tex.rgba[i + 1], tex.rgba[i + 2], tex.rgba[i + 3]]
}
/// Clear the whole backbuffer to an opaque color.
pub fn clear(dst: &mut [u32], c: Color32) {
let px = 0xFF00_0000 | ((c.r() as u32) << 16) | ((c.g() as u32) << 8) | c.b() as u32;
dst.fill(px);
}
+600
View File
@@ -0,0 +1,600 @@
//! Minimal CRT / libm runtime for the no-CRT PE64 build.
//!
//! The UTSM PE shim only resolves DLL imports (kernel32/user32/gdi32).
//! Symbols below are *link-time* references emitted by LLVM/std, so they must
//! live inside the PE image itself:
//! - string intrinsics: memcpy / memmove / memset / memcmp
//! - MSVC runtime: __chkstk, _fltused, _tls_index, _tls_used,
//! __CxxFrameHandler3 (never called, panic = abort)
//! - f32 libm: floorf/ceilf/roundf, sinf/cosf, atan2f, powf,
//! exp2f, acosf, cbrtf, _hypotf
//!
//! Math routines are ported from musl-libm (MIT) where accuracy matters
//! (sin/cos/atan) and use simple polynomial/series elsewhere. Visual use by
//! egui only needs ~1e-5 relative accuracy.
//!
//! TLS: Rust std on windows-msvc checks `_tls_used` and (if nonzero) accesses
//! thread locals through gs:[0x58] -> slots[_tls_index]. We are Ring0 with no
//! TEB, so mainCRTStartup installs a fake GS base pointing at a zeroed TLS
//! block (install_fake_tls) and restores it before ExitProcess.
#![allow(non_snake_case)]
use core::arch::asm;
/* ============================================================
* string intrinsics
* ========================================================== */
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep movsb",
inout("rcx") n => _,
inout("rsi") src => _,
inout("rdi") dst => _,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memmove(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let d = dst as usize;
let s = src as usize;
if d < s || d >= s.wrapping_add(n) {
memcpy(dst, src, n)
} else {
// Backward copy without touching DF (ABI requires DF=0).
let mut i = n;
while i > 0 {
i -= 1;
core::ptr::write_volatile(dst.add(i), core::ptr::read_volatile(src.add(i)));
}
dst
}
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memset(dst: *mut u8, val: i32, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep stosb",
inout("rcx") n => _,
inout("rdi") dst => _,
in("al") val as u8,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
// Volatile reads keep LLVM from idiom-recognizing this back into memcmp.
for i in 0..n {
let x = core::ptr::read_volatile(a.add(i));
let y = core::ptr::read_volatile(b.add(i));
if x != y {
return x as i32 - y as i32;
}
}
0
}
/* ============================================================
* MSVC runtime bits
* ========================================================== */
/// Stack probe. Our stack is one large committed region (see /STACK flag),
/// so probing pages is unnecessary. An empty body compiles to `ret`,
/// which trivially satisfies the preserve-everything contract.
#[no_mangle]
pub extern "C" fn __chkstk() {}
/// Referenced by std's unwind tables (.pdata personality). With
/// panic = "abort" it is never invoked; loop loudly if it ever is.
#[no_mangle]
pub extern "C" fn __CxxFrameHandler3() -> ! {
loop {
core::hint::spin_loop();
}
}
/// CRT float-usage marker (data symbol only).
#[no_mangle]
pub static _fltused: i32 = 0x9875;
/// TLS slot index of this module. We use slot 0 of the fake TLS array.
#[no_mangle]
pub static _tls_index: u32 = 0;
/// IMAGE_TLS_DIRECTORY64 stand-in (all zeros: no raw template, no callbacks).
/// std only checks that its address is nonzero to decide TLS is supported.
#[no_mangle]
pub static _tls_used: [u8; 0x30] = [0; 0x30];
/* ---- fake TEB / TLS so std thread_local access can't fault ---- */
const IA32_GS_BASE: u32 = 0xC000_0101;
static mut OLD_GS_BASE: u64 = 0;
static mut TLS_READY: bool = false;
fn rdmsr(msr: u32) -> u64 {
let lo: u32;
let hi: u32;
unsafe {
asm!("rdmsr", in("ecx") msr, out("eax") lo, out("edx") hi,
options(nomem, nostack, preserves_flags));
}
((hi as u64) << 32) | lo as u64
}
fn wrmsr(msr: u32, val: u64) {
unsafe {
asm!("wrmsr", in("ecx") msr, in("eax") val as u32,
in("edx") (val >> 32) as u32, options(nomem, nostack, preserves_flags));
}
}
/// Point GS at a minimal fake TEB whose ThreadLocalStoragePointer (0x58)
/// references a zeroed slots array; slot 0 covers `_tls_index == 0`.
/// All const-init thread locals in std are zero-valid (None/0).
///
/// # Safety
/// Ring0 only; call once at PE entry before touching std facilities.
pub unsafe fn install_fake_tls() {
static mut TEB: [u8; 0x100] = [0; 0x100];
static mut SLOTS: [usize; 64] = [0; 64];
static mut TLS_BLOCK: [u8; 8192] = [0; 8192];
if TLS_READY {
return;
}
let teb = core::ptr::addr_of_mut!(TEB);
let slots = core::ptr::addr_of_mut!(SLOTS);
let block = core::ptr::addr_of_mut!(TLS_BLOCK);
(*slots)[0] = block as usize;
*((*teb).as_mut_ptr().add(0x58) as *mut usize) = slots as usize;
OLD_GS_BASE = rdmsr(IA32_GS_BASE);
wrmsr(IA32_GS_BASE, teb as u64);
TLS_READY = true;
}
/// Restore the kernel's GS base (call before ExitProcess).
///
/// # Safety
/// Ring0 only; pair with install_fake_tls.
pub unsafe fn restore_tls() {
if TLS_READY {
wrmsr(IA32_GS_BASE, OLD_GS_BASE);
TLS_READY = false;
}
}
/* ============================================================
* f32 libm
* ========================================================== */
#[inline(always)]
fn asu32(x: f32) -> u32 {
x.to_bits()
}
#[inline(always)]
fn asf32(x: u32) -> f32 {
f32::from_bits(x)
}
#[no_mangle]
pub extern "C" fn floorf(x: f32) -> f32 {
// |x| >= 2^23 is already integral; NaN passes the compare false and
// degrades to 0.0, which egui never feeds us anyway.
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32; // truncate toward zero
let f = i as f32;
if f > x {
f - 1.0
} else {
f
}
}
#[no_mangle]
pub extern "C" fn ceilf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32;
let f = i as f32;
if f < x {
f + 1.0
} else {
f
}
}
/// C semantics: round half away from zero.
#[no_mangle]
pub extern "C" fn roundf(x: f32) -> f32 {
if x >= 0.0 {
floorf(x + 0.5)
} else {
ceilf(x - 0.5)
}
}
#[no_mangle]
pub extern "C" fn truncf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
x as i32 as f32
}
/// f64 truncation toward zero (LLVM libcall on SSE2-only targets).
#[no_mangle]
pub extern "C" fn trunc(x: f64) -> f64 {
// 2^52: every f64 with |x| >= 2^52 is already integral.
if x.abs() >= 4503599627370496.0 {
return x;
}
x as i64 as f64
}
/* ---- sin/cos: musl __sindf/__cosdf + medium Cody-Waite reduction ---- */
// |sin(x)/x - s(x)| < 2**-27.5 on the reduced range.
const S1: f64 = -0.166666666416265235595;
const S2: f64 = 0.0083333293858894631756;
const S3: f64 = -0.000198393348360966317347;
const S4: f64 = 0.0000027183114939898219064;
const C0: f64 = -0.499999997251031003120;
const C1: f64 = 0.0416666233237390631894;
const C2: f64 = -0.00138867637746099294692;
const C3: f64 = 0.0000243904487962774090654;
const PIO2_HI: f64 = 1.57079625129699707031e+00;
const PIO2_LO: f64 = 7.54978941586159635335e-08;
const INV_PIO2: f64 = 6.36619772367581382433e-01; // 2/pi
const TOINT: f64 = 1.5 / f64::EPSILON;
const TWO_PI: f64 = 6.28318530717958647693;
#[inline]
fn sindf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = S3 + z * S4;
let s = z * x;
(x + s * (S1 + z * S2)) + s * w * r
}
#[inline]
fn cosdf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = C2 + z * C3;
((1.0 + z * C0) + w * C1) + (w * z) * r
}
/// Reduce x modulo pi/2 (Cody-Waite, double precision). Returns (n, y)
/// with y = x - n*pi/2 accurate for moderate |x|.
#[inline]
fn rem_pio2_medium(x: f64) -> (i32, f64) {
// Coarse pre-reduction keeps the accurate path valid for any angle egui
// can produce (ellipse/bezier angles are already tiny).
let x = if x.abs() > 105.0 {
x - (x / TWO_PI).trunc() * TWO_PI
} else {
x
};
let fn_ = x * INV_PIO2 + TOINT - TOINT;
let n = fn_ as i32;
(n, x - fn_ * PIO2_HI - fn_ * PIO2_LO)
}
#[no_mangle]
pub extern "C" fn sinf(x: f32) -> f32 {
if x.abs() < 1.0e-30 {
return x;
}
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => sindf(y),
1 => cosdf(y),
2 => sindf(-y),
_ => -cosdf(y),
}) as f32
}
#[no_mangle]
pub extern "C" fn cosf(x: f32) -> f32 {
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => cosdf(y),
1 => sindf(-y),
2 => -cosdf(y),
_ => sindf(y),
}) as f32
}
/* ---- atan2f: musl atanf + quadrant fix ---- */
const ATAN_HI: [f32; 4] = [
4.6364760399e-01,
7.8539812565e-01,
9.8279371262e-01,
1.5707962513e+00,
];
const ATAN_LO: [f32; 4] = [
8.8023035407e-08,
1.2093354704e-08,
1.2587686365e-08,
7.5497894159e-08,
];
const AT: [f32; 5] = [
3.3333328366e-01,
-1.9999158382e-01,
1.4253635705e-01,
-1.0648017377e-01,
6.1687607318e-02,
];
const PI_F: f32 = 3.1415927410e+00;
fn atanf(x: f32) -> f32 {
let mut x = x;
let ix = asu32(x);
let sign = ix >> 31;
let aix = ix & 0x7fffffff;
if aix >= 0x4c800000 {
// |x| >= 2^26
if x.is_nan() {
return x;
}
let z = ATAN_HI[3] + 7.5231638e-37;
return if sign != 0 { -z } else { z };
}
let id: i32;
if aix < 0x3ee00000 {
// |x| < 0.4375
if aix < 0x39800000 {
return x; // |x| < 2^-12: atan(x) ~ x
}
id = -1;
} else {
x = x.abs();
if aix < 0x3f980000 {
if aix < 0x3f300000 {
id = 0;
x = (2.0 * x - 1.0) / (2.0 + x);
} else {
id = 1;
x = (x - 1.0) / (x + 1.0);
}
} else if aix < 0x401c0000 {
id = 2;
x = (x - 1.5) / (1.0 + 1.5 * x);
} else {
id = 3;
x = -1.0 / x;
}
}
let z = x * x;
let w = z * z;
let s1 = z * (AT[0] + w * (AT[2] + w * AT[4]));
let s2 = w * (AT[1] + w * AT[3]);
if id < 0 {
return x - x * (s1 + s2);
}
let i = id as usize;
let z2 = ATAN_HI[i] - ((x * (s1 + s2) - ATAN_LO[i]) - x);
if sign != 0 {
-z2
} else {
z2
}
}
#[no_mangle]
pub extern "C" fn atan2f(y: f32, x: f32) -> f32 {
if y == 0.0 {
return if x < 0.0 { PI_F } else { y }; // crude, matches C for our uses
}
if x == 0.0 {
return if y < 0.0 { -PI_F / 2.0 } else { PI_F / 2.0 };
}
let ax = x.abs();
let ay = y.abs();
let z = if ax > ay {
atanf(ay / ax)
} else {
PI_F / 2.0 - atanf(ax / ay)
};
if x < 0.0 {
if y < 0.0 {
z - PI_F
} else {
PI_F - z
}
} else if y < 0.0 {
-z
} else {
z
}
}
/* ---- exp2f / log2f / powf ---- */
/// ldexp-style scaling by 2^n (n clamped to f32 range by callers).
fn scalbnf(mut r: f32, mut n: i32) -> f32 {
while n > 127 {
r *= asf32(0x7F00_0000); // 2^127
n -= 127;
}
while n < -126 {
r *= asf32(0x0080_0000); // 2^-126
n += 126;
}
r * asf32(((127 + n) as u32) << 23)
}
#[no_mangle]
pub extern "C" fn exp2f(x: f32) -> f32 {
if x > 128.0 {
return f32::INFINITY;
}
if x < -150.0 {
return 0.0;
}
// n = round-to-nearest-even(x) via the magic-number trick.
let t = x + 12582912.0; // 1.5 * 2^23
let nf = t - 12582912.0;
let n = nf as i32;
let f = x - nf; // in [-0.5, 0.5]
// 2^f: Taylor in ln2, degree 6 -> ~1e-10 rel error on [-0.5, 0.5].
let p = 1.0
+ f * (0.6931471805599453
+ f * (0.24022650695910070
+ f * (0.05550410866482158
+ f * (0.009618129107628477
+ f * (0.0013333558146428443 + f * 0.00015403530393381609)))));
scalbnf(p, n)
}
/// log2(x) for x > 0; accuracy ~1e-7 via atanh series in double.
fn log2f(x: f32) -> f32 {
let ix = asu32(x);
let e = ((ix >> 23) as i32) - 127;
let m = asf32((ix & 0x007F_FFFF) | 0x3F80_0000); // [1, 2)
let t = ((m - 1.0) / (m + 1.0)) as f64;
let t2 = t * t;
let mut sum = 0.0f64;
let mut term = t;
for k in 0..8 {
sum += term / (2 * k + 1) as f64;
term *= t2;
}
let ln_m = 2.0 * sum;
(e as f64 + ln_m * 1.4426950408889634) as f32
}
#[no_mangle]
pub extern "C" fn powf(x: f32, y: f32) -> f32 {
if y == 0.0 {
return 1.0;
}
if x == 1.0 {
return 1.0;
}
if x == 0.0 {
return 0.0;
}
if x == 2.0 {
return exp2f(y);
}
if x < 0.0 {
let yi = y as i32;
if yi as f32 == y {
let r = exp2f(y * log2f(-x));
return if yi & 1 != 0 { -r } else { r };
}
return f32::NAN;
}
exp2f(y * log2f(x))
}
/* ---- acosf: musl ---- */
const PIO2_HI_F: f32 = 1.5707962513e+00;
const PIO2_LO_F: f32 = 7.5497894159e-08;
const PS0: f32 = 1.6666586697e-01;
const PS1: f32 = -4.2743422091e-02;
const PS2: f32 = -8.6563630030e-03;
const QS1: f32 = -7.0662963390e-01;
#[inline]
fn acos_r(z: f32) -> f32 {
z * (PS0 + z * (PS1 + z * PS2)) / (1.0 + z * QS1)
}
#[no_mangle]
pub extern "C" fn acosf(x: f32) -> f32 {
let hx = asu32(x);
let ix = hx & 0x7fffffff;
if ix >= 0x3f800000 {
// |x| >= 1
if ix == 0x3f800000 {
if hx >> 31 != 0 {
return 2.0 * PIO2_HI_F; // acos(-1) = pi
}
return 0.0; // acos(1) = 0
}
return f32::NAN;
}
if ix < 0x3f000000 {
// |x| < 0.5
if ix <= 0x32800000 {
return PIO2_HI_F; // |x| < 2^-26
}
return PIO2_HI_F - (x - (PIO2_LO_F - x * acos_r(x * x)));
}
if hx >> 31 != 0 {
// x < -0.5
let z = (1.0 + x) * 0.5;
let s = z.sqrt();
let w = acos_r(z) * s - PIO2_LO_F;
return 2.0 * PIO2_HI_F - 2.0 * (s + w);
}
// x > 0.5
let z = (1.0 - x) * 0.5;
let s = z.sqrt();
let df = asf32(asu32(s) & 0xFFFFF000); // keep df*df rounding-exact
let c = (z - df * df) / (s + df);
let w = acos_r(z) * s + c;
2.0 * (df + w)
}
/* ---- cbrtf / hypotf ---- */
#[no_mangle]
pub extern "C" fn cbrtf(x: f32) -> f32 {
if x == 0.0 {
return x;
}
let r = exp2f(log2f(x.abs()) / 3.0);
if x < 0.0 {
-r
} else {
r
}
}
#[no_mangle]
pub extern "C" fn _hypotf(x: f32, y: f32) -> f32 {
let ax = x.abs();
let ay = y.abs();
let (a, b) = if ax > ay { (ax, ay) } else { (ay, ax) };
if a == 0.0 {
return 0.0;
}
let r = b / a;
a * (1.0 + r * r).sqrt()
}
/// Plain alias in case LLVM references the C99 spelling.
#[no_mangle]
pub extern "C" fn hypotf(x: f32, y: f32) -> f32 {
_hypotf(x, y)
}
+315
View File
@@ -0,0 +1,315 @@
//! Minimal Win32 bindings used by the Deaicup PE backend.
//! Only the API subset that the Deshab UTSM PE shim implements is declared here.
//! All imports use raw-dylib so rust-lld can emit the import table without
//! any MSVC import libraries.
#![allow(non_snake_case, non_camel_case_types, dead_code)]
pub type HWND = isize;
pub type HDC = isize;
pub type HINSTANCE = isize;
pub type HICON = isize;
pub type HCURSOR = isize;
pub type HBRUSH = isize;
pub type HMENU = isize;
pub type HGDIOBJ = isize;
pub type HBITMAP = isize;
pub type HANDLE = isize;
pub type ATOM = u16;
pub type WPARAM = usize;
pub type LPARAM = isize;
pub type LRESULT = isize;
pub type BOOL = i32;
pub type UINT = u32;
pub type DWORD = u32;
pub type WORD = u16;
pub type LPCWSTR = *const u16;
pub const CW_USEDEFAULT: i32 = 0x80000000u32 as i32;
pub const WS_OVERLAPPED: u32 = 0x00000000;
pub const WS_CAPTION: u32 = 0x00C00000;
pub const WS_SYSMENU: u32 = 0x00080000;
pub const WS_THICKFRAME: u32 = 0x00040000;
pub const WS_MINIMIZEBOX: u32 = 0x00020000;
pub const WS_MAXIMIZEBOX: u32 = 0x00010000;
pub const WS_VISIBLE: u32 = 0x10000000;
pub const WS_OVERLAPPEDWINDOW: u32 = WS_OVERLAPPED
| WS_CAPTION
| WS_SYSMENU
| WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX;
pub const SW_SHOW: i32 = 5;
pub const WM_CREATE: u32 = 0x0001;
pub const WM_DESTROY: u32 = 0x0002;
pub const WM_SIZE: u32 = 0x0005;
pub const WM_PAINT: u32 = 0x000F;
pub const WM_CLOSE: u32 = 0x0010;
pub const WM_QUIT: u32 = 0x0012;
pub const WM_ERASEBKGND: u32 = 0x0014;
pub const WM_KEYDOWN: u32 = 0x0100;
pub const WM_KEYUP: u32 = 0x0101;
pub const WM_CHAR: u32 = 0x0102;
pub const WM_TIMER: u32 = 0x0113;
pub const WM_MOUSEMOVE: u32 = 0x0200;
pub const WM_LBUTTONDOWN: u32 = 0x0201;
pub const WM_LBUTTONUP: u32 = 0x0202;
pub const WM_RBUTTONDOWN: u32 = 0x0204;
pub const WM_RBUTTONUP: u32 = 0x0205;
pub const WM_MBUTTONDOWN: u32 = 0x0207;
pub const WM_MBUTTONUP: u32 = 0x0208;
pub const PM_NOREMOVE: u32 = 0x0000;
pub const PM_REMOVE: u32 = 0x0001;
pub const VK_BACK: usize = 0x08;
pub const VK_TAB: usize = 0x09;
pub const VK_RETURN: usize = 0x0D;
pub const VK_SHIFT: usize = 0x10;
pub const VK_CONTROL: usize = 0x11;
pub const VK_MENU: usize = 0x12;
pub const VK_ESCAPE: usize = 0x1B;
pub const VK_SPACE: usize = 0x20;
pub const VK_LEFT: usize = 0x25;
pub const VK_UP: usize = 0x26;
pub const VK_RIGHT: usize = 0x27;
pub const VK_DOWN: usize = 0x28;
pub const VK_DELETE: usize = 0x2E;
pub const IDC_ARROW: usize = 32512;
pub const COLOR_WINDOW: u32 = 5;
pub const GWLP_USERDATA: i32 = -21;
pub const GWLP_WNDPROC: i32 = -4;
pub const SM_CXSCREEN: i32 = 0;
pub const SM_CYSCREEN: i32 = 1;
pub const SRCCOPY: u32 = 0x00CC0020;
pub const DIB_RGB_COLORS: u32 = 0;
pub const BI_RGB: u32 = 0;
pub const WHITE_BRUSH: i32 = 0;
pub const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5;
pub const STD_ERROR_HANDLE: u32 = 0xFFFFFFF4;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct POINT {
pub x: i32,
pub y: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RECT {
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct MSG {
pub hwnd: HWND,
pub message: u32,
pub wParam: WPARAM,
pub lParam: LPARAM,
pub time: u32,
pub pt: POINT,
}
#[repr(C)]
pub struct WNDCLASSEXW {
pub cbSize: UINT,
pub style: UINT,
pub lpfnWndProc: WNDPROC,
pub cbClsExtra: i32,
pub cbWndExtra: i32,
pub hInstance: HINSTANCE,
pub hIcon: HICON,
pub hCursor: HCURSOR,
pub hbrBackground: HBRUSH,
pub lpszMenuName: LPCWSTR,
pub lpszClassName: LPCWSTR,
pub hIconSm: HICON,
}
pub type WNDPROC = extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT;
#[repr(C)]
pub struct PAINTSTRUCT {
pub hdc: HDC,
pub fErase: BOOL,
pub rcPaint: RECT,
pub fRestore: BOOL,
pub fIncUpdate: BOOL,
pub rgbReserved: [u8; 32],
}
impl Default for PAINTSTRUCT {
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFOHEADER {
pub biSize: u32,
pub biWidth: i32,
pub biHeight: i32,
pub biPlanes: u16,
pub biBitCount: u16,
pub biCompression: u32,
pub biSizeImage: u32,
pub biXPelsPerMeter: i32,
pub biYPelsPerMeter: i32,
pub biClrUsed: u32,
pub biClrImportant: u32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RGBQUAD {
pub rgbBlue: u8,
pub rgbGreen: u8,
pub rgbRed: u8,
pub rgbReserved: u8,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFO {
pub bmiHeader: BITMAPINFOHEADER,
pub bmiColors: [RGBQUAD; 1],
}
#[link(name = "user32", kind = "raw-dylib")]
extern "system" {
pub fn RegisterClassExW(lpwcx: *const WNDCLASSEXW) -> ATOM;
pub fn CreateWindowExW(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
x: i32,
y: i32,
nWidth: i32,
nHeight: i32,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: *mut core::ffi::c_void,
) -> HWND;
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
pub fn ShowWindow(hWnd: HWND, nCmdShow: i32) -> BOOL;
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
pub fn GetMessageW(lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
pub fn PeekMessageW(
lpMsg: *mut MSG,
hWnd: HWND,
wMsgFilterMin: u32,
wMsgFilterMax: u32,
wRemoveMsg: UINT,
) -> BOOL;
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
pub fn PostQuitMessage(nExitCode: i32);
pub fn DefWindowProcW(hWnd: HWND, msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
pub fn GetClientRect(hWnd: HWND, lpRect: *mut RECT) -> BOOL;
pub fn InvalidateRect(hWnd: HWND, lpRect: *const RECT, bErase: BOOL) -> BOOL;
pub fn BeginPaint(hWnd: HWND, lpPaint: *mut PAINTSTRUCT) -> HDC;
pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
pub fn GetDC(hWnd: HWND) -> HDC;
pub fn ReleaseDC(hWnd: HWND, hDC: HDC) -> i32;
pub fn LoadCursorW(hInstance: HINSTANCE, lpCursorName: usize) -> HCURSOR;
pub fn SetCursor(hCursor: HCURSOR) -> HCURSOR;
pub fn GetCursorPos(lpPoint: *mut POINT) -> BOOL;
pub fn ScreenToClient(hWnd: HWND, lpPoint: *mut POINT) -> BOOL;
pub fn GetAsyncKeyState(vKey: i32) -> i16;
pub fn SetTimer(hWnd: HWND, nIDEvent: usize, uElapse: UINT, lpTimerFunc: usize) -> usize;
pub fn KillTimer(hWnd: HWND, uIDEvent: usize) -> BOOL;
pub fn MessageBeep(uType: UINT) -> BOOL;
pub fn GetSystemMetrics(nIndex: i32) -> i32;
}
#[link(name = "gdi32", kind = "raw-dylib")]
extern "system" {
pub fn CreateCompatibleDC(hdc: HDC) -> HDC;
pub fn CreateDIBSection(
hdc: HDC,
pbmi: *const BITMAPINFO,
usage: UINT,
ppvBits: *mut *mut core::ffi::c_void,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP;
pub fn SelectObject(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ;
pub fn DeleteObject(ho: HGDIOBJ) -> BOOL;
pub fn DeleteDC(hdc: HDC) -> BOOL;
pub fn BitBlt(
hdc: HDC,
x: i32,
y: i32,
cx: i32,
cy: i32,
hdcSrc: HDC,
x1: i32,
y1: i32,
rop: DWORD,
) -> BOOL;
pub fn GetStockObject(i: i32) -> HGDIOBJ;
pub fn SetDIBitsToDevice(
hdc: HDC,
xDest: i32,
yDest: i32,
w: DWORD,
h: DWORD,
xSrc: i32,
ySrc: i32,
startScan: UINT,
numLines: UINT,
lpvBits: *const core::ffi::c_void,
lpbmi: *const BITMAPINFO,
colorUse: UINT,
) -> i32;
pub fn GdiFlush() -> BOOL;
}
#[link(name = "kernel32", kind = "raw-dylib")]
extern "system" {
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HINSTANCE;
pub fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
pub fn WriteFile(
hFile: HANDLE,
lpBuffer: *const u8,
nNumberOfBytesToWrite: DWORD,
lpNumberOfBytesWritten: *mut DWORD,
lpOverlapped: *mut core::ffi::c_void,
) -> BOOL;
pub fn ExitProcess(uExitCode: UINT) -> !;
pub fn GetCommandLineA() -> *const u8;
pub fn GetTickCount() -> DWORD;
pub fn QueryPerformanceCounter(lpPerformanceCount: *mut i64) -> BOOL;
pub fn QueryPerformanceFrequency(lpFrequency: *mut i64) -> BOOL;
pub fn Sleep(dwMilliseconds: DWORD);
}
/// UTF-16 helper: build a null-terminated wide string.
pub fn wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(core::iter::once(0)).collect()
}
/// Debug log through the shim's stdout (serial).
pub fn log(s: &str) {
unsafe {
let h = GetStdHandle(STD_OUTPUT_HANDLE);
let mut written: DWORD = 0;
WriteFile(h, s.as_ptr(), s.len() as DWORD, &mut written, core::ptr::null_mut());
}
}
+4639
View File
@@ -0,0 +1,4639 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "Deaicup-Software"
version = "0.1.0"
dependencies = [
"eframe",
"egui",
"egui_extras",
]
[[package]]
name = "ab_glyph"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "accesskit"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b"
dependencies = [
"enumn",
"serde",
]
[[package]]
name = "accesskit_consumer"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6"
dependencies = [
"accesskit",
]
[[package]]
name = "accesskit_macos"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7"
dependencies = [
"accesskit",
"accesskit_consumer",
"objc2 0.3.0-beta.3.patch-leaks.3",
"once_cell",
]
[[package]]
name = "accesskit_unix"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08"
dependencies = [
"accesskit",
"accesskit_consumer",
"async-channel",
"async-once-cell",
"atspi",
"futures-lite 1.13.0",
"once_cell",
"serde",
"zbus",
]
[[package]]
name = "accesskit_windows"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b"
dependencies = [
"accesskit",
"accesskit_consumer",
"once_cell",
"paste",
"static_assertions",
"windows 0.48.0",
]
[[package]]
name = "accesskit_winit"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67"
dependencies = [
"accesskit",
"accesskit_macos",
"accesskit_unix",
"accesskit_windows",
"winit",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"getrandom 0.3.4",
"once_cell",
"serde",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-activity"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
dependencies = [
"android-properties",
"bitflags 2.13.1",
"cc",
"cesu8",
"jni",
"jni-sys 0.3.1",
"libc",
"log",
"ndk",
"ndk-context",
"ndk-sys",
"num_enum",
"thiserror 1.0.69",
]
[[package]]
name = "android-properties"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arboard"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
dependencies = [
"clipboard-win",
"log",
"objc2 0.6.4",
"objc2-app-kit",
"objc2-foundation",
"parking_lot",
"percent-encoding",
"windows-sys 0.60.2",
"x11rb",
]
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "as-raw-xcb-connection"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
dependencies = [
"libloading 0.7.4",
]
[[package]]
name = "async-broadcast"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
dependencies = [
"event-listener 2.5.3",
"futures-core",
]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand 2.5.0",
"futures-lite 2.6.1",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-fs"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
dependencies = [
"async-lock 2.8.0",
"autocfg",
"blocking",
"futures-lite 1.13.0",
]
[[package]]
name = "async-io"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock 2.8.0",
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-lite 1.13.0",
"log",
"parking",
"polling 2.8.0",
"rustix 0.37.28",
"slab",
"socket2",
"waker-fn",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite 2.6.1",
"parking",
"polling 3.11.0",
"rustix 1.1.4",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
dependencies = [
"event-listener 2.5.3",
]
[[package]]
name = "async-lock"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener 5.4.2",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-once-cell"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]]
name = "async-process"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
dependencies = [
"async-io 1.13.0",
"async-lock 2.8.0",
"async-signal",
"blocking",
"cfg-if",
"event-listener 3.1.0",
"futures-lite 1.13.0",
"rustix 0.38.44",
"windows-sys 0.48.0",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "async-signal"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
dependencies = [
"async-io 2.6.0",
"async-lock 3.4.2",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.4",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atspi"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca"
dependencies = [
"atspi-common",
"atspi-connection",
"atspi-proxies",
]
[[package]]
name = "atspi-common"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5"
dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zbus",
"zbus_names",
"zvariant",
]
[[package]]
name = "atspi-connection"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939"
dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite 1.13.0",
"zbus",
]
[[package]]
name = "atspi-proxies"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
dependencies = [
"atspi-common",
"serde",
"zbus",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-sys"
version = "0.1.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146"
dependencies = [
"objc-sys 0.2.0-beta.2",
]
[[package]]
name = "block-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
dependencies = [
"objc-sys 0.3.5",
]
[[package]]
name = "block2"
version = "0.2.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42"
dependencies = [
"block-sys 0.1.0-beta.1",
"objc2-encode 2.0.0-pre.2",
]
[[package]]
name = "block2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
dependencies = [
"block-sys 0.2.1",
"objc2 0.4.1",
]
[[package]]
name = "blocking"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite 2.6.1",
"piper",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "calloop"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
dependencies = [
"bitflags 2.13.1",
"log",
"polling 3.11.0",
"rustix 0.38.44",
"slab",
"thiserror 1.0.69",
]
[[package]]
name = "calloop"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7"
dependencies = [
"bitflags 2.13.1",
"polling 3.11.0",
"rustix 1.1.4",
"slab",
"tracing",
]
[[package]]
name = "calloop-wayland-source"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
dependencies = [
"calloop 0.12.4",
"rustix 0.38.44",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "calloop-wayland-source"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
dependencies = [
"calloop 0.14.4",
"rustix 1.1.4",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "cc"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "cgl"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
dependencies = [
"libc",
]
[[package]]
name = "clipboard-win"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
"error-code",
]
[[package]]
name = "cocoa"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
dependencies = [
"bitflags 1.3.2",
"block",
"cocoa-foundation",
"core-foundation",
"core-graphics",
"foreign-types",
"libc",
"objc",
]
[[package]]
name = "cocoa-foundation"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
dependencies = [
"bitflags 1.3.2",
"block",
"core-foundation",
"core-graphics-types",
"libc",
"objc",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "com"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
dependencies = [
"com_macros",
]
[[package]]
name = "com_macros"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
dependencies = [
"com_macros_support",
"proc-macro2",
"syn 1.0.109",
]
[[package]]
name = "com_macros_support"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-graphics"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "cursor-icon"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dispatch2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
]
[[package]]
name = "displaydoc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "dlib"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
dependencies = [
"libloading 0.8.9",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "ecolor"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
dependencies = [
"bytemuck",
"serde",
]
[[package]]
name = "eframe"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2"
dependencies = [
"bytemuck",
"cocoa",
"document-features",
"egui",
"egui-wgpu",
"egui-winit",
"egui_glow",
"glow",
"glutin",
"glutin-winit",
"image",
"js-sys",
"log",
"objc",
"parking_lot",
"percent-encoding",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"static_assertions",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"web-time",
"winapi",
"winit",
]
[[package]]
name = "egui"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
dependencies = [
"accesskit",
"ahash",
"epaint",
"log",
"nohash-hasher",
"serde",
]
[[package]]
name = "egui-wgpu"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
dependencies = [
"bytemuck",
"document-features",
"egui",
"epaint",
"log",
"thiserror 1.0.69",
"type-map",
"web-time",
"wgpu",
"winit",
]
[[package]]
name = "egui-winit"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
dependencies = [
"accesskit_winit",
"arboard",
"egui",
"log",
"raw-window-handle 0.6.2",
"smithay-clipboard",
"web-time",
"webbrowser",
"winit",
]
[[package]]
name = "egui_extras"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
dependencies = [
"egui",
"ehttp",
"enum-map",
"image",
"log",
"mime_guess2",
"resvg",
"serde",
]
[[package]]
name = "egui_glow"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
dependencies = [
"bytemuck",
"egui",
"glow",
"log",
"memoffset 0.9.1",
"wasm-bindgen",
"web-sys",
"winit",
]
[[package]]
name = "ehttp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876"
dependencies = [
"document-features",
"js-sys",
"ureq",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "emath"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
dependencies = [
"bytemuck",
"serde",
]
[[package]]
name = "enum-map"
version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
dependencies = [
"enum-map-derive",
"serde",
]
[[package]]
name = "enum-map-derive"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "enumn"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "epaint"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176"
dependencies = [
"ab_glyph",
"ahash",
"bytemuck",
"ecolor",
"emath",
"log",
"nohash-hasher",
"parking_lot",
"serde",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "error-code"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "event-listener"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener"
version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
dependencies = [
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener 5.4.2",
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-core"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
[[package]]
name = "futures-io"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand 1.9.0",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand 2.5.0",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-sink"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
[[package]]
name = "futures-task"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix 1.1.4",
"windows-link",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi 5.3.0",
"wasip2",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
]
[[package]]
name = "gl_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
dependencies = [
"khronos_api",
"log",
"xml-rs",
]
[[package]]
name = "glow"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
dependencies = [
"js-sys",
"slotmap",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "glutin"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
dependencies = [
"bitflags 2.13.1",
"cfg_aliases",
"cgl",
"core-foundation",
"dispatch",
"glutin_egl_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
"icrate",
"libloading 0.8.9",
"objc2 0.4.1",
"once_cell",
"raw-window-handle 0.5.2",
"wayland-sys",
"windows-sys 0.48.0",
"x11-dl",
]
[[package]]
name = "glutin-winit"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
dependencies = [
"cfg_aliases",
"glutin",
"raw-window-handle 0.5.2",
"winit",
]
[[package]]
name = "glutin_egl_sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd"
dependencies = [
"gl_generator",
"windows-sys 0.48.0",
]
[[package]]
name = "glutin_glx_sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f"
dependencies = [
"gl_generator",
"x11-dl",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
dependencies = [
"gl_generator",
]
[[package]]
name = "gpu-alloc"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9"
dependencies = [
"bitflags 2.13.1",
"gpu-alloc-types",
]
[[package]]
name = "gpu-alloc-types"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "gpu-allocator"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
dependencies = [
"log",
"presser",
"thiserror 1.0.69",
"winapi",
"windows 0.52.0",
]
[[package]]
name = "gpu-descriptor"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
dependencies = [
"bitflags 2.13.1",
"gpu-descriptor-types",
"hashbrown 0.14.5",
]
[[package]]
name = "gpu-descriptor-types"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hassle-rs"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
dependencies = [
"bitflags 2.13.1",
"com",
"libc",
"libloading 0.8.9",
"thiserror 1.0.69",
"widestring",
"winapi",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hexf-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "home"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "icrate"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
dependencies = [
"block2 0.3.0",
"dispatch",
"objc2 0.4.1",
]
[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "image"
version = "0.24.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-traits",
"png",
]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys 0.3.1",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
dependencies = [
"jni-sys 0.4.1",
]
[[package]]
name = "jni-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
dependencies = [
"jni-sys-macros",
]
[[package]]
name = "jni-sys-macros"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
dependencies = [
"quote",
"syn 2.0.119",
]
[[package]]
name = "jobserver"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom 0.4.3",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "khronos-egl"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
dependencies = [
"libc",
"libloading 0.8.9",
"pkg-config",
]
[[package]]
name = "khronos_api"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libredox"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
dependencies = [
"bitflags 2.13.1",
"libc",
"plain",
"redox_syscall 0.9.1",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memmap2"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "metal"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
dependencies = [
"bitflags 2.13.1",
"block",
"core-graphics-types",
"foreign-types",
"log",
"objc",
"paste",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess2"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca"
dependencies = [
"mime",
"phf",
"phf_shared",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "naga"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
dependencies = [
"bit-set",
"bitflags 2.13.1",
"codespan-reporting",
"hexf-parse",
"indexmap",
"log",
"num-traits",
"rustc-hash 1.1.0",
"spirv",
"termcolor",
"thiserror 1.0.69",
"unicode-xid",
]
[[package]]
name = "ndk"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
dependencies = [
"bitflags 2.13.1",
"jni-sys 0.3.1",
"log",
"ndk-sys",
"num_enum",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"thiserror 1.0.69",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-sys"
version = "0.5.0+25.2.9519653"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
dependencies = [
"jni-sys 0.3.1",
]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.7.1",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [
"proc-macro-crate 3.5.0",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
"objc_exception",
]
[[package]]
name = "objc-sys"
version = "0.2.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
[[package]]
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.3.0-beta.3.patch-leaks.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468"
dependencies = [
"block2 0.2.0-alpha.6",
"objc-sys 0.2.0-beta.2",
"objc2-encode 2.0.0-pre.2",
]
[[package]]
name = "objc2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
dependencies = [
"objc-sys 0.3.5",
"objc2-encode 3.0.0",
]
[[package]]
name = "objc2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
dependencies = [
"objc2-encode 4.1.0",
]
[[package]]
name = "objc2-app-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-graphics",
"objc2-foundation",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.13.1",
"dispatch2",
"objc2 0.6.4",
]
[[package]]
name = "objc2-core-graphics"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
dependencies = [
"bitflags 2.13.1",
"dispatch2",
"objc2 0.6.4",
"objc2-core-foundation",
"objc2-io-surface",
]
[[package]]
name = "objc2-encode"
version = "2.0.0-pre.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"
dependencies = [
"objc-sys 0.2.0-beta.2",
]
[[package]]
name = "objc2-encode"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
]
[[package]]
name = "objc2-io-surface"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
dependencies = [
"bitflags 2.13.1",
"objc2 0.6.4",
"objc2-core-foundation",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
dependencies = [
"cc",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "orbclient"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
dependencies = [
"libc",
"libredox",
]
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "owned_ttf_parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.18",
"smallvec",
"windows-link",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.119",
"unicase",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher 1.0.3",
"unicase",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "piper"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
dependencies = [
"atomic-waker",
"fastrand 2.5.0",
"futures-io",
]
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys 0.48.0",
]
[[package]]
name = "polling"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.5.2",
"pin-project-lite",
"rustix 1.1.4",
"windows-sys 0.61.2",
]
[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "presser"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [
"toml_edit 0.25.13+spec-1.1.0",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
[[package]]
name = "quick-xml"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "raw-window-handle"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "redox_syscall"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07507be7b4a5f9f26eeb41eeaebb1f5a7ff29dfb29739facc21d35bf8b11c21e"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "renderdoc-sys"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "resvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
dependencies = [
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roxmltree"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]]
name = "rustix"
version = "0.37.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
"windows-sys 0.48.0",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.13.1",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.13.1",
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sctk-adwaita"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7"
dependencies = [
"ab_glyph",
"log",
"memmap2",
"smithay-client-toolkit 0.18.1",
"tiny-skia",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "serde_repr"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "sha1"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "simplecss"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "siphasher"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "slotmap"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
dependencies = [
"version_check",
]
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "smithay-client-toolkit"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
dependencies = [
"bitflags 2.13.1",
"calloop 0.12.4",
"calloop-wayland-source 0.2.0",
"cursor-icon",
"libc",
"log",
"memmap2",
"rustix 0.38.44",
"thiserror 1.0.69",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols 0.31.2",
"wayland-protocols-wlr 0.2.0",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-client-toolkit"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
dependencies = [
"bitflags 2.13.1",
"calloop 0.14.4",
"calloop-wayland-source 0.4.1",
"cursor-icon",
"libc",
"log",
"memmap2",
"rustix 1.1.4",
"thiserror 2.0.19",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
"wayland-cursor",
"wayland-protocols 0.32.13",
"wayland-protocols-experimental",
"wayland-protocols-misc",
"wayland-protocols-wlr 0.3.12",
"wayland-scanner",
"xkeysym",
]
[[package]]
name = "smithay-clipboard"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
dependencies = [
"libc",
"smithay-client-toolkit 0.20.0",
"wayland-backend",
]
[[package]]
name = "smol_str"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
dependencies = [
"serde",
]
[[package]]
name = "socket2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
"bitflags 2.13.1",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
dependencies = [
"kurbo",
"siphasher 0.3.11",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand 2.5.0",
"getrandom 0.4.3",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.61.2",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
dependencies = [
"thiserror-impl 2.0.19",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "thiserror-impl"
version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "tiny-skia"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"png",
"tiny-skia-path",
]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.25.13+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
dependencies = [
"indexmap",
"toml_datetime 1.1.1+spec-1.1.0",
"toml_parser",
"winnow 1.0.4",
]
[[package]]
name = "toml_parser"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [
"winnow 1.0.4",
]
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.3",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "uds_windows"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset 0.9.1",
"tempfile",
"windows-sys 0.61.2",
]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"url",
"webpki-roots 0.26.11",
]
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "usvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
dependencies = [
"base64 0.21.7",
"log",
"pico-args",
"usvg-parser",
"usvg-tree",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"roxmltree",
"simplecss",
"siphasher 0.3.11",
"svgtypes",
"usvg-tree",
]
[[package]]
name = "usvg-tree"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "waker-fn"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.119",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wayland-backend"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8"
dependencies = [
"cc",
"downcast-rs",
"rustix 1.1.4",
"scoped-tls",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
dependencies = [
"bitflags 2.13.1",
"rustix 1.1.4",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-csd-frame"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [
"bitflags 2.13.1",
"cursor-icon",
"wayland-backend",
]
[[package]]
name = "wayland-cursor"
version = "0.31.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d"
dependencies = [
"rustix 1.1.4",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-experimental"
version = "20250721.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-misc"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-plasma"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
dependencies = [
"bitflags 2.13.1",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.32.13",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [
"dlib",
"log",
"once_cell",
"pkg-config",
]
[[package]]
name = "web-sys"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b"
dependencies = [
"core-foundation",
"home",
"jni",
"log",
"ndk-context",
"objc",
"raw-window-handle 0.5.2",
"url",
"web-sys",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.9",
]
[[package]]
name = "webpki-roots"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "wgpu"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
dependencies = [
"arrayvec",
"cfg-if",
"cfg_aliases",
"js-sys",
"log",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"smallvec",
"static_assertions",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu-core",
"wgpu-hal",
"wgpu-types",
]
[[package]]
name = "wgpu-core"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
dependencies = [
"arrayvec",
"bit-vec",
"bitflags 2.13.1",
"cfg_aliases",
"codespan-reporting",
"indexmap",
"log",
"naga",
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"rustc-hash 1.1.0",
"smallvec",
"thiserror 1.0.69",
"web-sys",
"wgpu-hal",
"wgpu-types",
]
[[package]]
name = "wgpu-hal"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703"
dependencies = [
"android_system_properties",
"arrayvec",
"ash",
"bitflags 2.13.1",
"cfg_aliases",
"core-graphics-types",
"glow",
"glutin_wgl_sys",
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",
"hassle-rs",
"js-sys",
"khronos-egl",
"libc",
"libloading 0.8.9",
"log",
"metal",
"naga",
"ndk-sys",
"objc",
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle 0.6.2",
"renderdoc-sys",
"rustc-hash 1.1.0",
"smallvec",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types",
"winapi",
]
[[package]]
name = "wgpu-types"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
dependencies = [
"bitflags 2.13.1",
"js-sys",
"web-sys",
]
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-implement",
"windows-interface",
"windows-targets 0.48.5",
]
[[package]]
name = "windows"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "windows-interface"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winit"
version = "0.29.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
dependencies = [
"ahash",
"android-activity",
"atomic-waker",
"bitflags 2.13.1",
"bytemuck",
"calloop 0.12.4",
"cfg_aliases",
"core-foundation",
"core-graphics",
"cursor-icon",
"icrate",
"js-sys",
"libc",
"log",
"memmap2",
"ndk",
"ndk-sys",
"objc2 0.4.1",
"once_cell",
"orbclient",
"percent-encoding",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"redox_syscall 0.3.5",
"rustix 0.38.44",
"sctk-adwaita",
"smithay-client-toolkit 0.18.1",
"smol_str",
"unicode-segmentation",
"wasm-bindgen",
"wasm-bindgen-futures",
"wayland-backend",
"wayland-client",
"wayland-protocols 0.31.2",
"wayland-protocols-plasma",
"web-sys",
"web-time",
"windows-sys 0.48.0",
"x11-dl",
"x11rb",
"xkbcommon-dl",
]
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "writeable"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "x11-dl"
version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
dependencies = [
"libc",
"once_cell",
"pkg-config",
]
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading 0.8.9",
"once_cell",
"rustix 1.1.4",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xcursor"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
[[package]]
name = "xdg-home"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "xkbcommon-dl"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
dependencies = [
"bitflags 2.13.1",
"dlib",
"log",
"once_cell",
"xkeysym",
]
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xml-rs"
version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "yoke"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"synstructure",
]
[[package]]
name = "zbus"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
dependencies = [
"async-broadcast",
"async-executor",
"async-fs",
"async-io 1.13.0",
"async-lock 2.8.0",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"byteorder",
"derivative",
"enumflags2",
"event-listener 2.5.3",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix",
"once_cell",
"ordered-stream",
"rand",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tracing",
"uds_windows",
"winapi",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
dependencies = [
"serde",
"static_assertions",
"zvariant",
]
[[package]]
name = "zerocopy"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerotrie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zvariant"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db"
dependencies = [
"byteorder",
"enumflags2",
"libc",
"serde",
"static_assertions",
"zvariant_derive",
]
[[package]]
name = "zvariant_derive"
version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "Deaicup-Software"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "Deaicup-Software"
path = "src/main.rs"
[dependencies]
egui = "0.27"
eframe = "0.27"
egui_extras = { version = "0.27" , features = ["all_loaders"] }
+104
View File
@@ -0,0 +1,104 @@
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
use eframe::egui;
fn main() -> Result<(), eframe::Error>{
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
};
eframe::run_native(
"Deaicup Software",
options,
Box::new(|cc| {
egui_extras::install_image_loaders(&cc.egui_ctx);
Box::new(DeaicupSoftware::default())
},)
)
}
#[derive(Default)]
struct DeaicupSoftware {
current_page: Page,
}
#[derive(Debug, PartialEq, Clone, Copy, Default)]
enum Page {
#[default]
Home,
Tool,
Game,
LinuxWare,
WindowsWare,
Set,
About
}
impl eframe::App for DeaicupSoftware{
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame){
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui|{
let image_url = "https://deaicup.com/DeaicupSoftware/top.png";
ui.set_min_height(75.0);
let size = ui.available_size();
ui.image(egui::Image::new(image_url).fit_to_exact_size(egui::Vec2::new(size.x, 75.0)));
});
//main
egui::SidePanel::left("navigation_panel")
.default_width(200.0)
.resizable(true)
.show(ctx, |ui| {
ui.heading("Menu");
ui.separator();
ui.vertical(|ui| {
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked(){
self.current_page = Page::Home;
}
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked(){
self.current_page = Page::Tool;
}
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked(){
self.current_page = Page::Game;
}
if ui.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE").clicked(){
self.current_page = Page::LinuxWare;
}
if ui.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE").clicked(){
self.current_page = Page::WindowsWare;
}
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked(){
self.current_page = Page::Set;
}
if ui.selectable_label(self.current_page == Page::About, "ABOUT").clicked(){
self.current_page = Page::About;
}
})
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.separator();
match self.current_page {
Page::Home => {
ui.label("Home")
}
Page::Tool => {
ui.label("Tool")
}
Page::Game => {
ui.label("Game")
}
Page::LinuxWare => {
ui.label("LinuxWare")
}
Page::WindowsWare => {
ui.label("WindowsWare")
}
Page::Set => {
ui.label("Set")
}
Page::About => {
ui.label("About")
}
}
});
}
}
+15 -7
View File
@@ -1,5 +1,5 @@
CC := clang
LD := ld.lld
CC ?= clang
LD ?= ld.lld
OBJCOPY := llvm-objcopy
ROOT := ../..
@@ -9,23 +9,31 @@ 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
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)
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S)
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c sched/*.c dkm/*.c test/*.c vmm/*.c pe/*.c brand/*.c pkg/*.c)
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S sched/*.S)
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
DEPS := $(C_SOURCES:.c=.d)
.PHONY: all clean
# 显式指定默认目标:防止 -include 的 .d 文件(halt.d 等)抢占默认目标
.DEFAULT_GOAL := all
all: $(OUT)
# 头文件依赖追踪:clang -MMD 生成 .d,避免 config.h 等头修改后对象文件不重建
# 注意:-include 必须放在 all: 之后,否则第一个 .d 中的目标会成为 make 默认目标
-include $(DEPS)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\boot" mkdir "$(ROOT)\SYSTEM\boot"
@mkdir -p $(dir $@)
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.o: %.S
$(CC) $(ASFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Include *.o,*.d | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
Binary file not shown.
Binary file not shown.
+572 -35
View File
@@ -1,6 +1,8 @@
#include <utsm/idt.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/instr.h>
#include <utsm/panic.h>
/* IDT gate descriptor (64-bit) */
typedef struct __attribute__((packed)) {
@@ -29,7 +31,7 @@ static idt_entry g_idt[IDT_ENTRIES] __attribute__((aligned(16)));
#define ICW1_ICW4 0x01
#define ICW1_INIT 0x10
/* assembly stub declarations */
/* assembly stub declarations — vectors 0-255 全覆盖 */
extern void isr_stub_0(void);
extern void isr_stub_1(void);
extern void isr_stub_2(void);
@@ -78,20 +80,280 @@ extern void isr_stub_44(void);
extern void isr_stub_45(void);
extern void isr_stub_46(void);
extern void isr_stub_47(void);
extern void isr_stub_48(void);
extern void isr_stub_49(void);
extern void isr_stub_50(void);
extern void isr_stub_51(void);
extern void isr_stub_52(void);
extern void isr_stub_53(void);
extern void isr_stub_54(void);
extern void isr_stub_55(void);
extern void isr_stub_56(void);
extern void isr_stub_57(void);
extern void isr_stub_58(void);
extern void isr_stub_59(void);
extern void isr_stub_60(void);
extern void isr_stub_61(void);
extern void isr_stub_62(void);
extern void isr_stub_63(void);
extern void isr_stub_64(void);
extern void isr_stub_65(void);
extern void isr_stub_66(void);
extern void isr_stub_67(void);
extern void isr_stub_68(void);
extern void isr_stub_69(void);
extern void isr_stub_70(void);
extern void isr_stub_71(void);
extern void isr_stub_72(void);
extern void isr_stub_73(void);
extern void isr_stub_74(void);
extern void isr_stub_75(void);
extern void isr_stub_76(void);
extern void isr_stub_77(void);
extern void isr_stub_78(void);
extern void isr_stub_79(void);
extern void isr_stub_80(void);
extern void isr_stub_81(void);
extern void isr_stub_82(void);
extern void isr_stub_83(void);
extern void isr_stub_84(void);
extern void isr_stub_85(void);
extern void isr_stub_86(void);
extern void isr_stub_87(void);
extern void isr_stub_88(void);
extern void isr_stub_89(void);
extern void isr_stub_90(void);
extern void isr_stub_91(void);
extern void isr_stub_92(void);
extern void isr_stub_93(void);
extern void isr_stub_94(void);
extern void isr_stub_95(void);
extern void isr_stub_96(void);
extern void isr_stub_97(void);
extern void isr_stub_98(void);
extern void isr_stub_99(void);
extern void isr_stub_100(void);
extern void isr_stub_101(void);
extern void isr_stub_102(void);
extern void isr_stub_103(void);
extern void isr_stub_104(void);
extern void isr_stub_105(void);
extern void isr_stub_106(void);
extern void isr_stub_107(void);
extern void isr_stub_108(void);
extern void isr_stub_109(void);
extern void isr_stub_110(void);
extern void isr_stub_111(void);
extern void isr_stub_112(void);
extern void isr_stub_113(void);
extern void isr_stub_114(void);
extern void isr_stub_115(void);
extern void isr_stub_116(void);
extern void isr_stub_117(void);
extern void isr_stub_118(void);
extern void isr_stub_119(void);
extern void isr_stub_120(void);
extern void isr_stub_121(void);
extern void isr_stub_122(void);
extern void isr_stub_123(void);
extern void isr_stub_124(void);
extern void isr_stub_125(void);
extern void isr_stub_126(void);
extern void isr_stub_127(void);
extern void isr_stub_128(void);
extern void isr_stub_129(void);
extern void isr_stub_130(void);
extern void isr_stub_131(void);
extern void isr_stub_132(void);
extern void isr_stub_133(void);
extern void isr_stub_134(void);
extern void isr_stub_135(void);
extern void isr_stub_136(void);
extern void isr_stub_137(void);
extern void isr_stub_138(void);
extern void isr_stub_139(void);
extern void isr_stub_140(void);
extern void isr_stub_141(void);
extern void isr_stub_142(void);
extern void isr_stub_143(void);
extern void isr_stub_144(void);
extern void isr_stub_145(void);
extern void isr_stub_146(void);
extern void isr_stub_147(void);
extern void isr_stub_148(void);
extern void isr_stub_149(void);
extern void isr_stub_150(void);
extern void isr_stub_151(void);
extern void isr_stub_152(void);
extern void isr_stub_153(void);
extern void isr_stub_154(void);
extern void isr_stub_155(void);
extern void isr_stub_156(void);
extern void isr_stub_157(void);
extern void isr_stub_158(void);
extern void isr_stub_159(void);
extern void isr_stub_160(void);
extern void isr_stub_161(void);
extern void isr_stub_162(void);
extern void isr_stub_163(void);
extern void isr_stub_164(void);
extern void isr_stub_165(void);
extern void isr_stub_166(void);
extern void isr_stub_167(void);
extern void isr_stub_168(void);
extern void isr_stub_169(void);
extern void isr_stub_170(void);
extern void isr_stub_171(void);
extern void isr_stub_172(void);
extern void isr_stub_173(void);
extern void isr_stub_174(void);
extern void isr_stub_175(void);
extern void isr_stub_176(void);
extern void isr_stub_177(void);
extern void isr_stub_178(void);
extern void isr_stub_179(void);
extern void isr_stub_180(void);
extern void isr_stub_181(void);
extern void isr_stub_182(void);
extern void isr_stub_183(void);
extern void isr_stub_184(void);
extern void isr_stub_185(void);
extern void isr_stub_186(void);
extern void isr_stub_187(void);
extern void isr_stub_188(void);
extern void isr_stub_189(void);
extern void isr_stub_190(void);
extern void isr_stub_191(void);
extern void isr_stub_192(void);
extern void isr_stub_193(void);
extern void isr_stub_194(void);
extern void isr_stub_195(void);
extern void isr_stub_196(void);
extern void isr_stub_197(void);
extern void isr_stub_198(void);
extern void isr_stub_199(void);
extern void isr_stub_200(void);
extern void isr_stub_201(void);
extern void isr_stub_202(void);
extern void isr_stub_203(void);
extern void isr_stub_204(void);
extern void isr_stub_205(void);
extern void isr_stub_206(void);
extern void isr_stub_207(void);
extern void isr_stub_208(void);
extern void isr_stub_209(void);
extern void isr_stub_210(void);
extern void isr_stub_211(void);
extern void isr_stub_212(void);
extern void isr_stub_213(void);
extern void isr_stub_214(void);
extern void isr_stub_215(void);
extern void isr_stub_216(void);
extern void isr_stub_217(void);
extern void isr_stub_218(void);
extern void isr_stub_219(void);
extern void isr_stub_220(void);
extern void isr_stub_221(void);
extern void isr_stub_222(void);
extern void isr_stub_223(void);
extern void isr_stub_224(void);
extern void isr_stub_225(void);
extern void isr_stub_226(void);
extern void isr_stub_227(void);
extern void isr_stub_228(void);
extern void isr_stub_229(void);
extern void isr_stub_230(void);
extern void isr_stub_231(void);
extern void isr_stub_232(void);
extern void isr_stub_233(void);
extern void isr_stub_234(void);
extern void isr_stub_235(void);
extern void isr_stub_236(void);
extern void isr_stub_237(void);
extern void isr_stub_238(void);
extern void isr_stub_239(void);
extern void isr_stub_240(void);
extern void isr_stub_241(void);
extern void isr_stub_242(void);
extern void isr_stub_243(void);
extern void isr_stub_244(void);
extern void isr_stub_245(void);
extern void isr_stub_246(void);
extern void isr_stub_247(void);
extern void isr_stub_248(void);
extern void isr_stub_249(void);
extern void isr_stub_250(void);
extern void isr_stub_251(void);
extern void isr_stub_252(void);
extern void isr_stub_253(void);
extern void isr_stub_254(void);
extern void isr_stub_255(void);
static void *g_isr_table[] = {
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
static void *g_isr_table[256] = {
isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3,
isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7,
isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11,
isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15,
isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19,
isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23,
isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27,
isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31,
isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35,
isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39,
isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43,
isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47,
isr_stub_48, isr_stub_49, isr_stub_50, isr_stub_51,
isr_stub_52, isr_stub_53, isr_stub_54, isr_stub_55,
isr_stub_56, isr_stub_57, isr_stub_58, isr_stub_59,
isr_stub_60, isr_stub_61, isr_stub_62, isr_stub_63,
isr_stub_64, isr_stub_65, isr_stub_66, isr_stub_67,
isr_stub_68, isr_stub_69, isr_stub_70, isr_stub_71,
isr_stub_72, isr_stub_73, isr_stub_74, isr_stub_75,
isr_stub_76, isr_stub_77, isr_stub_78, isr_stub_79,
isr_stub_80, isr_stub_81, isr_stub_82, isr_stub_83,
isr_stub_84, isr_stub_85, isr_stub_86, isr_stub_87,
isr_stub_88, isr_stub_89, isr_stub_90, isr_stub_91,
isr_stub_92, isr_stub_93, isr_stub_94, isr_stub_95,
isr_stub_96, isr_stub_97, isr_stub_98, isr_stub_99,
isr_stub_100, isr_stub_101, isr_stub_102, isr_stub_103,
isr_stub_104, isr_stub_105, isr_stub_106, isr_stub_107,
isr_stub_108, isr_stub_109, isr_stub_110, isr_stub_111,
isr_stub_112, isr_stub_113, isr_stub_114, isr_stub_115,
isr_stub_116, isr_stub_117, isr_stub_118, isr_stub_119,
isr_stub_120, isr_stub_121, isr_stub_122, isr_stub_123,
isr_stub_124, isr_stub_125, isr_stub_126, isr_stub_127,
isr_stub_128, isr_stub_129, isr_stub_130, isr_stub_131,
isr_stub_132, isr_stub_133, isr_stub_134, isr_stub_135,
isr_stub_136, isr_stub_137, isr_stub_138, isr_stub_139,
isr_stub_140, isr_stub_141, isr_stub_142, isr_stub_143,
isr_stub_144, isr_stub_145, isr_stub_146, isr_stub_147,
isr_stub_148, isr_stub_149, isr_stub_150, isr_stub_151,
isr_stub_152, isr_stub_153, isr_stub_154, isr_stub_155,
isr_stub_156, isr_stub_157, isr_stub_158, isr_stub_159,
isr_stub_160, isr_stub_161, isr_stub_162, isr_stub_163,
isr_stub_164, isr_stub_165, isr_stub_166, isr_stub_167,
isr_stub_168, isr_stub_169, isr_stub_170, isr_stub_171,
isr_stub_172, isr_stub_173, isr_stub_174, isr_stub_175,
isr_stub_176, isr_stub_177, isr_stub_178, isr_stub_179,
isr_stub_180, isr_stub_181, isr_stub_182, isr_stub_183,
isr_stub_184, isr_stub_185, isr_stub_186, isr_stub_187,
isr_stub_188, isr_stub_189, isr_stub_190, isr_stub_191,
isr_stub_192, isr_stub_193, isr_stub_194, isr_stub_195,
isr_stub_196, isr_stub_197, isr_stub_198, isr_stub_199,
isr_stub_200, isr_stub_201, isr_stub_202, isr_stub_203,
isr_stub_204, isr_stub_205, isr_stub_206, isr_stub_207,
isr_stub_208, isr_stub_209, isr_stub_210, isr_stub_211,
isr_stub_212, isr_stub_213, isr_stub_214, isr_stub_215,
isr_stub_216, isr_stub_217, isr_stub_218, isr_stub_219,
isr_stub_220, isr_stub_221, isr_stub_222, isr_stub_223,
isr_stub_224, isr_stub_225, isr_stub_226, isr_stub_227,
isr_stub_228, isr_stub_229, isr_stub_230, isr_stub_231,
isr_stub_232, isr_stub_233, isr_stub_234, isr_stub_235,
isr_stub_236, isr_stub_237, isr_stub_238, isr_stub_239,
isr_stub_240, isr_stub_241, isr_stub_242, isr_stub_243,
isr_stub_244, isr_stub_245, isr_stub_246, isr_stub_247,
isr_stub_248, isr_stub_249, isr_stub_250, isr_stub_251,
isr_stub_252, isr_stub_253, isr_stub_254, isr_stub_255,
};
static void outb(u16 port, u8 value) {
@@ -109,9 +371,11 @@ static void io_wait(void) {
}
static void pic_remap(void) {
/* save masks */
/* save masks (kept for reference; fixed masks are applied below) */
u8 m1 = inb(PIC1_DATA);
u8 m2 = inb(PIC2_DATA);
(void)m1;
(void)m2;
/* ICW1: start init + ICW4 */
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
@@ -142,10 +406,58 @@ static void pic_remap(void) {
outb(PIC2_DATA, 0xFF); /* mask all slave */
}
/* P1(万物互联): kern.irq.unmask/mask 总线服务后端 —— PIC IMR 位操作。
* 仅 ISA IRQ 0-15(越界 = 编程错误,IRQ-E01 零降级)。
* unmask IRQ8-15 时自动放开主片 IRQ2 级联。
* 注意 apic_route=1 时 PIC 已全屏蔽、由 IOAPIC 接管(B7);该路径下
* 的解屏蔽随 P6 真 IRQ 输入走 IOAPIC 重定向表接线。 */
void pic_irq_unmask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_unmask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m & (u8)~(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m & (u8)~(1u << (irq - 8)));
u8 m1 = inb(PIC1_DATA);
outb(PIC1_DATA, m1 & (u8)~(1u << 2)); /* 主片 IRQ2 级联 */
}
}
void pic_irq_mask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_mask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m | (u8)(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m | (u8)(1u << (irq - 8)));
}
}
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
* 代码段,异常派发时加载 CS 会 #GP(selector error),异常链升级为
* #DF -> triple fault,且全程无任何日志输出。 */
static u16 idt_kernel_cs(void) {
u16 cs;
__asm__ volatile("mov %%cs, %0" : "=r"(cs));
return cs;
}
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
u64 addr = (u64)handler;
g_idt[vector].offset_low = (u16)(addr & 0xffff);
g_idt[vector].selector = 0x08; /* kernel code segment (from GDT) */
g_idt[vector].selector = idt_kernel_cs();
g_idt[vector].ist = ist;
g_idt[vector].type_attr = type;
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
@@ -156,13 +468,9 @@ static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
void idt_init(void) {
log_info("[IDT] init begin");
for (u16 i = 0; i < 48; i++) {
for (u16 i = 0; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */
}
/* fill unused entries with null */
for (u16 i = 48; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, 0, 0, 0);
}
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
@@ -178,15 +486,171 @@ void idt_init(void) {
log_info("[IDT] init ok");
}
/* IRQ handler table */
static irq_handler_t g_irq_handlers[16];
/* 安全 halt stub: cli; hlt 循环。用于 idt_halt_all()。
* 任何异常/IRQ 触发此 stub 将安全停机,不会三重故障。 */
static void isr_halt_stub(void) {
for (;;) { __asm__ volatile("cli; hlt"); }
}
int irq_register(u8 irq, irq_handler_t handler) {
if (irq >= 16) return -1;
g_irq_handlers[irq] = handler;
void idt_halt_all(void) {
/* 将 IDT 所有 256 个条目替换为安全 halt stub。
* 调用前应已 cli + mask PIC,此函数是额外防御层:
* - 实机 spurious IRQ7/15 即使 PIC 已 mask 也可能触发
* - NMI / SMI 可能导致异常
* - DSK 运行期间 CPU 异常 (PF/GP) 应安全停机而非执行过期 handler */
for (u16 i = 0; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
}
/* 重新加载 IDTR 以确保更改生效 */
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
idtr.base = (u64)&g_idt;
__asm__ volatile("lidt %0" :: "m"(idtr));
}
void idt_install_dsk_diag(void) {
/* BUG-20260801-005: DSK 交接诊断 IDT(替代原 idt_halt_all 静默化)。
* 原 halt_all 将 256 项全部换成静默 halt stub——防御正确,但 DSK
* 阶段任何 CPU 异常(PF/GP/UD)被无声吞掉,零日志无法诊断
* (实测 pe HELLO64.EXE 早期 #PF 直接表现为"卡死/三重故障")。
* 本函数在保留 IRQ 防御的前提下恢复异常可见性:
* vector 0-31 -> 原 isr_stub(走 idt_handler 异常路径,打完整
* vector/err/rip/CR2/寄存器日志后安全停机)
* vector 32-255 -> 保持 halt stub(PIC 已全 mask 且 IF=0,IRQ 本
* 不该到达;spurious IRQ 维持原防御行为)
* 不做 pic_remap / sti——DSK 上下文 IF=0 不变式保持不变。
* isr_stub / idt_handler 是 UTSM .text 代码,SAS-R0 下常驻内存
* 有效(BUG-20260729-013 登记行为:halt_all 引入前 DSK 阶段异常
* 本就能送达 idt_handler);异常路径只用 log/serial,不依赖已过期
* 的 IRQ handler 状态。 */
for (u16 i = 0; i < 32; i++) {
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E);
}
for (u16 i = 32; i < IDT_ENTRIES; i++) {
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
}
idt_ptr idtr;
idtr.limit = (u16)(sizeof(g_idt) - 1);
idtr.base = (u64)&g_idt;
__asm__ volatile("lidt %0" :: "m"(idtr));
log_info("[IDT] DSK diag IDT installed (exc 0-31 logged, IRQ halted)");
}
void idt_install_pe_diag(void) {
/* 语义与 idt_install_dsk_diag 相同:BUG-20260801-005 起 DSK 交接
* 默认装备诊断 IDT,PE 原生执行前重装一次保持幂等(防御 PE 运行
* 期间 IDT 被第三方改写的场景)。 */
idt_install_dsk_diag();
}
/* IRQ handler table — 扩展到 256 个 vector (真机 APIC/IOAPIC/MSI 需要) */
static irq_handler_t g_irq_handlers[256];
/* B7 阶段2: LAPIC EOI 钩子 (由 apic.drv 经 kernel_api.register_apic_eoi 注册)。
* 指向写 LAPIC EOI 寄存器 (0xFEE000B0) 的函数; NULL = PIC 路由模式, 不调用。
* 单核 BSP 场景: 注册发生在 apic.drv init (普通上下文), 调用发生在 IRQ
* 上下文; 函数指针赋值对齐原子, 无需锁。 */
static void (*g_apic_eoi_hook)(void) = 0;
void idt_register_apic_eoi(void (*eoi_fn)(void)) {
g_apic_eoi_hook = eoi_fn;
if (eoi_fn) {
log_info("[IDT] APIC EOI hook registered (LAPIC EOI on IRQ dispatch)");
} else {
log_info("[IDT] APIC EOI hook cleared");
}
}
/* ---- 插桩: IRQ 统计计数器 ---- */
INSTR_STAT_DECL(pic_irq_total);
INSTR_STAT_DECL(apic_irq_total);
/* ---- B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用) ----
* 分配范围 0x40-0xDF (160 个向量):
* 0x00-0x1F CPU 异常
* 0x20-0x2F PIC legacy IRQ (保留不动)
* 0x30-0x3F 保留给 IOAPIC GSI 16-31 未来扩展 (本阶段不动态分配)
* 0x40-0xDF 动态分配池 (MSI/MSI-X)
* 0xE0 APIC tick 自测预留 (阶段1 惯例)
* 0xFF LAPIC spurious vector
* 数据结构: 3 x u64 位图 (192 bit, 用后 160), __builtin_ctzll 找首个
* 零位, 分配/释放均 O(1)。热路径无锁: SAS-R0 单核 BSP, alloc/free 只在
* driver_init 主上下文调用 (MSI 编程期间对应设备尚未解除中断屏蔽,
* IRQ 上下文不可能并发分配), 位图字为对齐 u64 单写原子。
* 动态入口无需新增 stub: idt_stubs.S 已为 0-255 全部向量预生成入口,
* 分配后 irq_register(vector, handler) 直接挂上既有分发表。 */
#define IRQ_VEC_POOL_BASE 0x40
#define IRQ_VEC_POOL_COUNT 160
static u64 g_vec_bitmap[3];
int irq_vector_alloc(void) {
for (u32 w = 0; w < 3; w++) {
u64 free_bits = ~g_vec_bitmap[w];
if (!free_bits) continue;
u32 bit = (u32)__builtin_ctzll(free_bits);
u32 idx = w * 64u + bit;
if (idx >= IRQ_VEC_POOL_COUNT) break;
g_vec_bitmap[w] |= (1ULL << bit);
int vec = IRQ_VEC_POOL_BASE + (int)idx;
log_info("[IDT] vector alloc");
log_hex64("[IDT] vector=", (u64)vec);
return vec;
}
log_warn("[IDT] vector alloc failed: pool exhausted");
panic_full("IDT-E01 VECTOR POOL EXHAUSTED",
"irq_vector_alloc: dynamic vector pool exhausted", 0);
return -1;
}
void irq_vector_free(int vector) {
if (vector < IRQ_VEC_POOL_BASE ||
vector >= IRQ_VEC_POOL_BASE + (int)IRQ_VEC_POOL_COUNT) {
return; /* 越界输入幂等忽略 (静态向量不在池内) */
}
u32 idx = (u32)(vector - IRQ_VEC_POOL_BASE);
g_vec_bitmap[idx / 64u] &= ~(1ULL << (idx % 64u));
log_info("[IDT] vector free");
log_hex64("[IDT] vector=", (u64)vector);
}
int irq_register(u8 vector, irq_handler_t handler) {
/* IRQ号/向量兼容 shim: 0-15 视为 ISA IRQ 号, 换算 vector=0x20+IRQ。
* 依据: vector 0-15 是 CPU 异常向量 (#DE/#DB/...), idt_handler 的 IRQ
* 分发只覆盖 32-255, 故 0-15 作为注册目标永不可达、无歧义。
* 现状调用方两命名空间并存: ps2kbd(1)/e1000(11)/mouseInit(12) 传 IRQ 号,
* apic(0xE0)/virtio_net(0x20+n) 传向量 — 统一在此归一到向量空间。 */
if (vector < 16) {
u8 v = (u8)(0x20u + vector);
log_info("[IDT] irq_register: ISA IRQ number translated to vector");
log_hex64("[IDT] irq=", vector);
log_hex64("[IDT] vector=", v);
vector = v;
}
g_irq_handlers[vector] = handler;
INSTR_LOG(INSTR_LOG_DEBUG, INSTR_F_UTSM_TRACE, "[IDT] IRQ handler registered");
return 0;
}
/* Phase7: 显式 EOI(调度器 tick 等"handler 内可能切换任务"的路径用)。
* 序列复刻 idt_handler 默认 EOI: 先 LAPIC 钩子再 8259(按向量范围)。
* spurious 0xFF 特判与 idt_handler 一致(空 ISR 不该有 EOI)。 */
void idt_irq_eoi(u8 vector) {
if (vector < 16) vector = (u8)(0x20u + vector);
if (vector >= 32 && vector <= 47) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
if (vector >= 0x28) outb(PIC2_CMD, 0x20);
outb(PIC1_CMD, 0x20);
} else if (vector >= 48 && vector != 0xFF) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
}
}
/* Phase8: CPU 异常钩子(DRR 任务级 fault 接管)。 */
static int (*g_exc_hook)(u64 vector, u64 err, u64 rip) = 0;
void idt_set_exception_hook(int (*hook)(u64 vector, u64 err, u64 rip)) {
g_exc_hook = hook;
}
/* Stack frame pushed by isr_common in idt.S */
typedef struct {
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
@@ -197,26 +661,99 @@ typedef struct {
void idt_handler(isr_frame *frame) {
if (frame->vector < 32) {
log_error("[IDT] exception");
log_hex64("[IDT] vector=", frame->vector);
log_hex64("[IDT] err=", frame->error_code);
log_hex64("[IDT] rip=", frame->rip);
for (;;) { __asm__ volatile ("cli; hlt"); }
/* Phase8: DRR 异常钩子最前——返回非 0 = 异常已被消费
* (fault 任务被杀/切换,控制权不回到日志+停机路径) */
if (g_exc_hook &&
g_exc_hook(frame->vector, frame->error_code, frame->rip)) {
return;
}
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
* 寄存器 dump 用于定位 fault 指令的操作数来源。
* F1: 异常统一走莲花崩溃屏(panic_full 内部保留串口 dump +
* DRR 归档 + 帧缓冲直绘,最后 halt——不返回)。 */
u64 cr2, cr3;
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
(void)cr3;
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
panic_regs pr;
pr.rip = frame->rip;
pr.rsp = 0; /* isr_frame 不含 rsp(stub 内使用),置 0 */
pr.rflags = frame->rflags;
pr.cs = frame->cs;
pr.err = frame->error_code;
pr.vector = frame->vector;
pr.cr2 = cr2;
pr.rax = frame->rax;
pr.rcx = frame->rcx;
pr.rdx = frame->rdx;
pr.rsi = frame->rsi;
pr.rdi = frame->rdi;
pr.r8 = frame->r8;
pr.r9 = frame->r9;
pr.r10 = frame->r10;
pr.r11 = frame->r11;
char sym[16];
sym[0] = 'U'; sym[1] = 'T'; sym[2] = 'S'; sym[3] = 'M'; sym[4] = '-';
sym[5] = 'E'; sym[6] = 'X'; sym[7] = 'C';
u32 v = frame->vector;
u32 hi = (v >> 4) & 0xF, lo = v & 0xF;
sym[8] = (char)(hi < 10 ? '0' + hi : 'A' + hi - 10);
sym[9] = (char)(lo < 10 ? '0' + lo : 'A' + lo - 10);
sym[10] = 0;
/* 异常帧上无法安全恢复执行:两种 panic 模式都 halt */
panic_halt(sym, "cpu exception", &pr);
}
/* PIC IRQ: vectors 0x20-0x2F (32-47)
* B7 阶段2: 本分支同时服务两种路由来源:
* - 8259 PIC 路由 (默认): handler 后只需 8259 EOI, 钩子为 NULL 跳过;
* - IOAPIC->LAPIC 路由 (apic_route=1, legacy 线重定向到 0x20-0x2F):
* 中断由 LAPIC 递交, ISR 位置位, 必须写 LAPIC EOI (钩子) 清除,
* 否则同级 (vector[7:4] 相同) 中断被永久阻塞 — 每条线只投递一次。
* LAPIC EOI 先于 8259 EOI; PIC 全屏蔽时 8259 EOI 为无害 no-op
* (8259 无 in-service 位, 非特异性 EOI 不写任何状态)。 */
if (frame->vector >= 32 && frame->vector <= 47) {
INSTR_STAT_INC(pic_irq_total);
u8 irq = (u8)(frame->vector - 0x20);
/* call registered handler */
if (irq < 16 && g_irq_handlers[irq]) {
int suppress_eoi = g_irq_handlers[irq](irq);
/* call registered handler; 返回 1 = 自负 EOI 全责 (两者皆抑制) */
if (g_irq_handlers[frame->vector]) {
int suppress_eoi = g_irq_handlers[frame->vector](irq);
if (suppress_eoi) return;
}
/* send EOI */
/* LAPIC EOI (IOAPIC 路由时必需; PIC 路由时钩子为 NULL) */
if (g_apic_eoi_hook) {
g_apic_eoi_hook();
}
/* send PIC EOI */
if (frame->vector >= 0x28) {
outb(PIC2_CMD, 0x20);
}
outb(PIC1_CMD, 0x20);
return;
}
/* APIC/IOAPIC/MSI IRQ: vectors 0x30-0xFF (48-255)
* handler 约定: 返回 0 -> 由钩子补 LAPIC EOI (安全网);
* 返回 1 -> handler 已自行 LAPIC EOI, 跳过钩子。
* 钩子未注册 (纯 PIC 路由) 时本分支不做任何 EOI — 与旧版行为一致,
* 此时 handler 必须自行 LAPIC EOI (参见 apic.drv tick handler)。 */
if (frame->vector >= 48 && frame->vector <= 255) {
/* LAPIC spurious (SVR 低 8 位 = 0xFF): 规范上不置 ISR 位, 永不需 EOI。
* 若在此调钩子写 EOI, 会误清嵌套场景下真正 in-service 的最高位,
* 故特判直接返回 (EOI 空 ISR 虽无害, 但语义上 spurious 不该有 EOI)。 */
if (frame->vector == 0xFF) return;
INSTR_STAT_INC(apic_irq_total);
int suppress_eoi = 0;
if (g_irq_handlers[frame->vector]) {
suppress_eoi = g_irq_handlers[frame->vector]((u8)frame->vector);
}
if (!suppress_eoi && g_apic_eoi_hook) {
g_apic_eoi_hook();
}
return;
}
}
Binary file not shown.
+61 -1
View File
@@ -1,4 +1,8 @@
/* IDT assembly stubs — SAS-R0 interrupt vectors */
/* IDT assembly stubs — SAS-R0 interrupt vectors
* Vectors 0-31: CPU exceptions (some push error code)
* Vectors 32-47: PIC IRQ remap region
* Vectors 48-255: APIC/IOAPIC/MSI dynamic vectors
*/
.section .text
.macro isr_no_err n
@@ -34,6 +38,62 @@ isr_no_err 36; isr_no_err 37; isr_no_err 38; isr_no_err 39
isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43
isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47
/* Vectors 48-255: APIC/IOAPIC/MSI 真机动态向量
* 真机需要更多 vector: APIC timer (0x30+), IOAPIC GSI, MSI/MSI-X 等。
* 全部生成 stub 确保任何 vector 命中都有 handler, 不会 triple fault。 */
isr_no_err 48; isr_no_err 49; isr_no_err 50; isr_no_err 51
isr_no_err 52; isr_no_err 53; isr_no_err 54; isr_no_err 55
isr_no_err 56; isr_no_err 57; isr_no_err 58; isr_no_err 59
isr_no_err 60; isr_no_err 61; isr_no_err 62; isr_no_err 63
isr_no_err 64; isr_no_err 65; isr_no_err 66; isr_no_err 67
isr_no_err 68; isr_no_err 69; isr_no_err 70; isr_no_err 71
isr_no_err 72; isr_no_err 73; isr_no_err 74; isr_no_err 75
isr_no_err 76; isr_no_err 77; isr_no_err 78; isr_no_err 79
isr_no_err 80; isr_no_err 81; isr_no_err 82; isr_no_err 83
isr_no_err 84; isr_no_err 85; isr_no_err 86; isr_no_err 87
isr_no_err 88; isr_no_err 89; isr_no_err 90; isr_no_err 91
isr_no_err 92; isr_no_err 93; isr_no_err 94; isr_no_err 95
isr_no_err 96; isr_no_err 97; isr_no_err 98; isr_no_err 99
isr_no_err 100; isr_no_err 101; isr_no_err 102; isr_no_err 103
isr_no_err 104; isr_no_err 105; isr_no_err 106; isr_no_err 107
isr_no_err 108; isr_no_err 109; isr_no_err 110; isr_no_err 111
isr_no_err 112; isr_no_err 113; isr_no_err 114; isr_no_err 115
isr_no_err 116; isr_no_err 117; isr_no_err 118; isr_no_err 119
isr_no_err 120; isr_no_err 121; isr_no_err 122; isr_no_err 123
isr_no_err 124; isr_no_err 125; isr_no_err 126; isr_no_err 127
isr_no_err 128; isr_no_err 129; isr_no_err 130; isr_no_err 131
isr_no_err 132; isr_no_err 133; isr_no_err 134; isr_no_err 135
isr_no_err 136; isr_no_err 137; isr_no_err 138; isr_no_err 139
isr_no_err 140; isr_no_err 141; isr_no_err 142; isr_no_err 143
isr_no_err 144; isr_no_err 145; isr_no_err 146; isr_no_err 147
isr_no_err 148; isr_no_err 149; isr_no_err 150; isr_no_err 151
isr_no_err 152; isr_no_err 153; isr_no_err 154; isr_no_err 155
isr_no_err 156; isr_no_err 157; isr_no_err 158; isr_no_err 159
isr_no_err 160; isr_no_err 161; isr_no_err 162; isr_no_err 163
isr_no_err 164; isr_no_err 165; isr_no_err 166; isr_no_err 167
isr_no_err 168; isr_no_err 169; isr_no_err 170; isr_no_err 171
isr_no_err 172; isr_no_err 173; isr_no_err 174; isr_no_err 175
isr_no_err 176; isr_no_err 177; isr_no_err 178; isr_no_err 179
isr_no_err 180; isr_no_err 181; isr_no_err 182; isr_no_err 183
isr_no_err 184; isr_no_err 185; isr_no_err 186; isr_no_err 187
isr_no_err 188; isr_no_err 189; isr_no_err 190; isr_no_err 191
isr_no_err 192; isr_no_err 193; isr_no_err 194; isr_no_err 195
isr_no_err 196; isr_no_err 197; isr_no_err 198; isr_no_err 199
isr_no_err 200; isr_no_err 201; isr_no_err 202; isr_no_err 203
isr_no_err 204; isr_no_err 205; isr_no_err 206; isr_no_err 207
isr_no_err 208; isr_no_err 209; isr_no_err 210; isr_no_err 211
isr_no_err 212; isr_no_err 213; isr_no_err 214; isr_no_err 215
isr_no_err 216; isr_no_err 217; isr_no_err 218; isr_no_err 219
isr_no_err 220; isr_no_err 221; isr_no_err 222; isr_no_err 223
isr_no_err 224; isr_no_err 225; isr_no_err 226; isr_no_err 227
isr_no_err 228; isr_no_err 229; isr_no_err 230; isr_no_err 231
isr_no_err 232; isr_no_err 233; isr_no_err 234; isr_no_err 235
isr_no_err 236; isr_no_err 237; isr_no_err 238; isr_no_err 239
isr_no_err 240; isr_no_err 241; isr_no_err 242; isr_no_err 243
isr_no_err 244; isr_no_err 245; isr_no_err 246; isr_no_err 247
isr_no_err 248; isr_no_err 249; isr_no_err 250; isr_no_err 251
isr_no_err 252; isr_no_err 253; isr_no_err 254; isr_no_err 255
/* Common ISR */
isr_common:
pushq %rdi
Binary file not shown.
Binary file not shown.
+192
View File
@@ -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);
}
}
+45
View File
@@ -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);
}
Binary file not shown.
+30
View File
@@ -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);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+78
View File
@@ -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");
}
Binary file not shown.
+27 -2
View File
@@ -1,15 +1,20 @@
#include <utsm/utsm.h>
#include <utsm/segment.h>
#include <utsm/pckc.h>
#include <utsm/crypto.h>
#include <utsm/process.h>
#include <utsm/panic.h>
#include <utsm/log.h>
#include <utsm/config.h>
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
static u64 g_generation = 1;
void utsm_init(void) {
log_info("[UTSM] segment table reset begin");
for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) {
u32 max_seg = g_utsm_max_segments;
if (max_seg > UTSM_MAX_SEGMENTS) max_seg = UTSM_MAX_SEGMENTS;
for (u32 i = 0; i < max_seg; i++) {
g_segments[i].state = UTSM_SEG_FREE;
g_segments[i].generation = 1;
}
@@ -20,11 +25,31 @@ void utsm_init(void) {
utsm_pckc_init();
log_info("[UTSM] pckc init ok");
/* SAS-R0 特色自证:PCKC 启动期 key 派生往返。派生失败或密钥全零
* = Per-CPU 密钥缓存没有真正立起来,零降级 panic(PCKC-E01)。 */
{
utsm_segment_desc probe;
for (u32 i = 0; i < sizeof(probe); i++) ((u8 *)&probe)[i] = 0;
probe.key_epoch = 1;
utsm_key_material km;
for (u32 i = 0; i < 4; i++) km.words[i] = 0;
if (utsm_pckc_get_or_derive(0xFFFFFFFFu, &probe, &km) != UTSM_OK) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time get_or_derive returned error", 0);
}
if (km.words[0] == 0 && km.words[1] == 0 && km.words[2] == 0 && km.words[3] == 0) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time derive produced all-zero key material", 0);
}
utsm_pckc_invalidate_slot(0xFFFFFFFFu); /* 探针段作废,不留缓存 */
log_info("[UTSM] pckc derive roundtrip ok");
}
utsm_process_init();
}
utsm_segment_desc *utsm_get_segment(u32 slot) {
if (slot >= UTSM_MAX_SEGMENTS) {
if (slot >= g_utsm_max_segments || slot >= UTSM_MAX_SEGMENTS) {
return NULL;
}
return &g_segments[slot];
Binary file not shown.
+137
View File
@@ -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);
}
+60
View File
@@ -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
View File
@@ -1,19 +1,31 @@
#include <utsm/crypto.h>
#include <utsm/drr.h>
/* kdf.c — 段密钥派生(Phase 9:keyed BLAKE2b)
*
* segment_key = BLAKE2b-256(key=root_key, "UTSMSEGK"||p_uuid||s_uuid||key_epoch)
* root key 不直接加密数据;UUID 作为派生调谐因子(非明文密钥)。
* 确定性:同 (puuid, suuid, epoch) 恒得同 key —— PCKC 缓存正确性依赖此性质。
*/
static u64 mix64(u64 x) {
x ^= x >> 30;
x *= 0xbf58476d1ce4e5b9ULL;
x ^= x >> 27;
x *= 0x94d049bb133111ebULL;
x ^= x >> 31;
return x;
}
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
#include <utsm/drr.h>
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) {
const u64 *root = drr_get_root_key();
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));
out_key->words[2] = mix64(root[2] ^ process_uuid.hi ^ segment_uuid.hi ^ (key_epoch << 7));
out_key->words[3] = mix64(root[3] ^ process_uuid.lo ^ segment_uuid.lo ^ (key_epoch << 13));
u8 root_le[32];
u8 data[8 + 8 + 8 + 8 + 8];
u8 digest[32];
for (int i = 0; i < 4; i++) utsm_store64_le(root_le + 8 * i, root[i]);
utsm_store64_le(data + 0, 0x4B4745534D535455ULL); /* "UTSMSEGK" LE */
utsm_store64_le(data + 8, process_uuid.hi);
utsm_store64_le(data + 16, process_uuid.lo);
utsm_store64_le(data + 24, segment_uuid.hi);
utsm_store64_le(data + 32, segment_uuid.lo);
utsm_store64_le(data + 40, key_epoch);
utsm_blake2b(data, sizeof(data), root_le, 32, digest, 32);
for (int i = 0; i < 4; i++) out_key->words[i] = utsm_load64_le(digest + 8 * i);
}
Binary file not shown.
+32
View File
@@ -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);
}
+20 -13
View File
@@ -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/blake2b.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) {
u64 state = key->words[0] ^ key->words[1] ^ key->words[2] ^ key->words[3] ^ tweak;
for (u64 i = 0; i < UTSM_CACHE_LINE_SIZE; i += 8) {
state = next_stream_word(state + i + 0xa5a5a5a5a5a5a5a5ULL);
for (u64 j = 0; j < 8; j++) {
out[i + j] = in[i + j] ^ (u8)(state >> (j * 8));
}
u32 kw[8];
for (int i = 0; i < 4; i++) {
kw[2 * i] = (u32)(key->words[i] & 0xFFFFFFFFu);
kw[2 * i + 1] = (u32)(key->words[i] >> 32);
}
u8 ks[64];
utsm_chacha20_block(kw, tweak, 0, ks);
for (u32 i = 0; i < UTSM_CACHE_LINE_SIZE; i++) {
out[i] = in[i] ^ ks[i];
}
}
Binary file not shown.
Binary file not shown.
+39 -2
View File
@@ -2,6 +2,7 @@
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/arena.h>
#include <utsm/panic.h>
#include "../arch/x86_64/limine.h"
#define ELF_MAGIC0 0x7f
@@ -28,6 +29,10 @@
#define R_X86_64_32 10
#define R_X86_64_32S 11
#define R_X86_64_PC32 2
/* PLT32:lld 对(可能被覆盖的)全局函数调用生成,静态可重定位场景下
* 语义与 PC32 相同(patch = S + A - P)。多编译单元驱动(如 ath9k +
* wpa2_crypto)跨 TU 调用即为此类型。 */
#define R_X86_64_PLT32 4
void *memset(void *dst, int value, usize len);
void *memcpy(void *dst, const void *src, usize len);
@@ -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)) {
log_error("[DKM] ELF has no section table");
panic_full("DKM-E30 ELF NO SECTION TABLE",
"dkm_scan_symbols: driver ELF has no section table", 0);
return -1;
}
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
log_error("[DKM] ELF section table out of range");
panic_full("DKM-E31 ELF SECTION TABLE OUT OF RANGE",
"dkm_scan_symbols: section table out of file range", 0);
return -1;
}
@@ -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) {
log_error("[DKM] missing symtab/strtab");
panic_full("DKM-E32 ELF MISSING SYMTAB",
"dkm_scan_symbols: symtab/strtab missing or invalid", 0);
return -1;
}
if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) {
log_error("[DKM] invalid symtab/strtab range");
panic_full("DKM-E33 ELF SYMTAB RANGE INVALID",
"dkm_scan_symbols: symtab/strtab out of file range", 0);
return -1;
}
@@ -241,6 +254,8 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
return 0;
}
log_error("[DKM] required driver symbols missing");
panic_full("DKM-E34 REQUIRED DRIVER SYMBOLS MISSING",
"dkm_scan_symbols: driver_desc/init/exit symbol missing", 0);
return -1;
}
@@ -271,12 +286,16 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
if (total_size == 0) {
log_error("[DKM] no allocatable sections");
panic_full("DKM-E35 ELF NO ALLOC SECTIONS",
"dkm_load_elf_rel: no allocatable sections", 0);
return -1;
}
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
if (!image) {
log_error("[DKM] failed to allocate module memory");
panic_full("DKM-E36 MODULE MEM ALLOC FAILED",
"dkm_load_elf_rel: module memory allocation failed", 0);
return -1;
}
memset(image, 0, total_size);
@@ -348,18 +367,20 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
patch = S + A;
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
patch = (u64)((i64)(i32)(S + A));
} else if (reloc_type == R_X86_64_PC32) {
} else if (reloc_type == R_X86_64_PC32 || reloc_type == R_X86_64_PLT32) {
patch = (u64)((i64)(i32)(S + A - P));
} else {
log_error("[DKM] unsupported relocation type");
log_hex64("[DKM] reloc type=", (u64)reloc_type);
panic_full("DKM-E37 UNSUPPORTED RELOCATION",
"dkm_load_elf_rel: unsupported relocation type", 0);
return -1;
}
/* Bounds check: use the actual write width, not always 8. */
u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4;
if (rela->offset + reloc_width <= target->size) {
if (reloc_type == R_X86_64_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;
} else {
*(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);
if (desc->magic != DKM_DRIVER_MAGIC) {
log_error("[DKM] external driver_desc magic mismatch");
panic_full("DKM-E38 DRIVER DESC MAGIC MISMATCH",
"dkm_load_elf_rel: external driver_desc magic mismatch", 0);
return -1;
}
@@ -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.flags = desc->flags;
dkm_log_set_driver(desc->name); /* D4: 驱动自己的颜色 */
dkm_bus_set_current_provider(&handle); /* D6: 总线 provider 上下文 */
int result = init(dkm_get_kernel_api(), &handle);
dkm_bus_set_current_provider(0);
dkm_log_set_driver(0);
handle.init_status = result;
if (result != 0) {
handle.state = DKM_STATE_FAILED;
log_error("[DKM] external driver init failed");
panic_full("DKM-E39 EXTERNAL DRIVER INIT FAILED",
"dkm_load_elf_rel: external driver_init returned non-zero", 0);
return result;
}
handle.state = DKM_STATE_ACTIVE;
@@ -406,6 +435,8 @@ void dkm_scan_boot_modules(void) {
struct limine_module_response *response = g_module_request.response;
if (!response) {
log_warn("[DKM] no Limine module response");
panic_full("DKM-E40 NO LIMINE MODULE RESPONSE",
"dkm_scan_boot_modules: no Limine module response", 0);
return;
}
@@ -416,6 +447,8 @@ void dkm_scan_boot_modules(void) {
struct limine_file *file = response->modules[i];
if (!file) {
log_warn("[DKM] null module entry");
panic_full("DKM-E41 NULL MODULE ENTRY",
"dkm_scan_boot_modules: null boot module entry", 0);
continue;
}
@@ -437,11 +470,15 @@ void dkm_scan_boot_modules(void) {
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
if (load_result != 0) {
log_error("[DKM] external driver load failed");
panic_full("DKM-E42 EXTERNAL DRIVER LOAD FAILED",
"dkm_scan_boot_modules: external driver load failed", 0);
}
}
} else {
log_error("[DKM] module ELF64 rejected");
log_hex64("[DKM] elf status=", (u64)(i64)status);
panic_full("DKM-E43 MODULE ELF64 REJECTED",
"dkm_scan_boot_modules: boot module ELF64 check rejected", 0);
}
}
Binary file not shown.
+318
View File
@@ -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);
}
Binary file not shown.
+12 -1
View File
@@ -1,5 +1,6 @@
#include <utsm/dkm.h>
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
static int dkm_validate_desc(const dkm_driver_desc *desc) {
if (!desc) {
@@ -27,18 +28,24 @@ void dkm_init(void) {
log_info("[DKM] init ok");
} else {
log_error("[DKM] init failed");
panic_full("DKM-E50 BUILTIN INIT FAILED",
"dkm_init: builtin console_early load failed", 0);
}
}
int dkm_load_builtin(const dkm_builtin_driver *driver) {
if (!driver || !driver->desc || !driver->init) {
log_error("[DKM] invalid builtin driver");
panic_full("DKM-E51 INVALID BUILTIN DRIVER",
"dkm_load_builtin: builtin driver descriptor invalid", 0);
return -1;
}
int status = dkm_validate_desc(driver->desc);
if (status != 0) {
log_error("[DKM] driver_desc rejected");
panic_full("DKM-E52 DRIVER DESC REJECTED",
"dkm_load_builtin: builtin driver_desc validation failed", 0);
return status;
}
@@ -51,13 +58,17 @@ int dkm_load_builtin(const dkm_builtin_driver *driver) {
log_info("[DKM] builtin driver init begin");
log_info(driver->desc->name);
dkm_log_set_driver(driver->desc->name); /* D4: 驱动自己的颜色 */
handle.state = DKM_STATE_INITING;
status = driver->init(dkm_get_kernel_api(), &handle);
dkm_log_set_driver(0);
handle.init_status = status;
if (status != 0) {
handle.state = DKM_STATE_FAILED;
log_error("[DKM] builtin driver init failed");
panic_full("DKM-E53 BUILTIN DRIVER INIT FAILED",
"dkm_load_builtin: builtin driver_init returned non-zero", 0);
return status;
}
Binary file not shown.
+128 -5
View File
@@ -2,6 +2,11 @@
#include <utsm/log.h>
#include <utsm/idt.h>
#include <utsm/net.h>
#include <utsm/mm.h>
#include <utsm/dma.h>
#include <utsm/drr.h>
#include <utsm/utrw.h>
#include <utsm/panic.h>
static void dkm_api_info(const char *msg) {
log_info(msg);
@@ -16,10 +21,9 @@ static void dkm_api_error(const char *msg) {
}
static void dkm_api_panic(const char *msg) {
log_error(msg);
for (;;) {
__asm__ volatile("cli; hlt");
}
/* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲)。
* fallback 模式下返回驱动,由驱动调用点走其备用路径。 */
panic_full("DKM-PANIC", msg, 0);
}
static const dkm_log_api g_log_api = {
@@ -29,6 +33,27 @@ static const dkm_log_api g_log_api = {
.panic = dkm_api_panic
};
/* ---- D4: DKM 彩色日志——每个驱动有自己的脾气 ----
* 加载器在 driver_init 前调用 dkm_log_set_driver(desc->name):
* 驱动名 FNV-1a hash → 8 色调色板(明亮色系,串口可读),
* 同名驱动颜色确定性可复现。init 返回后由调用方清上下文。 */
void dkm_log_set_driver(const char *name) {
static const char *palette[8] = {
"\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[35m",
"\x1b[96m", "\x1b[92m", "\x1b[93m", "\x1b[95m"
};
if (!name || !*name) {
log_set_color(0);
return;
}
u32 h = 2166136261u;
while (*name) {
h ^= (u8)*name++;
h *= 16777619u;
}
log_set_color(palette[h & 7]);
}
static dkm_kernel_api g_kernel_api = {
.version = DKM_KERNEL_API_VERSION,
.size = sizeof(dkm_kernel_api),
@@ -52,13 +77,63 @@ static dkm_kernel_api g_kernel_api = {
.boot_modules_response = 0,
.irq_register = 0,
.hhdm_offset = 0,
.block = 0
.block = 0,
.mmio = 0,
.mm_map_mmio = 0,
.mm_unmap_mmio = 0,
.register_apic_eoi = 0,
.sched = 0,
.kapi = 0,
.bus = 0
};
const dkm_kernel_api *dkm_get_kernel_api(void) {
return &g_kernel_api;
}
/* ===================================================================
* D3: 按名导出表(kapi)——自由也要有地图
* =================================================================== */
static const dkm_kapi_entry g_kapi_entries[] = {
{ "log.info", (void *)dkm_api_info },
{ "log.warn", (void *)dkm_api_warn },
{ "log.error", (void *)dkm_api_error },
{ "log.panic", (void *)dkm_api_panic },
{ "dma.alloc_pages", (void *)dma_alloc_pages },
{ "irq.register", (void *)irq_register },
{ "irq.vector_alloc", (void *)irq_vector_alloc },
{ "irq.vector_free", (void *)irq_vector_free },
{ "mm.map_mmio", (void *)mm_map_mmio },
{ "mm.unmap_mmio", (void *)mm_unmap_mmio },
{ "apic.register_eoi", (void *)idt_register_apic_eoi },
{ "drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty },
{ "drr.rollback_pages", (void *)drr_rollback_pages },
{ "utrw.debug_dump", (void *)utrw_debug_dump_page },
};
static int kapi_lookup(const char *name, void **out_fn) {
if (!name) return -1;
for (u32 i = 0; i < sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]); i++) {
const dkm_kapi_entry *e = &g_kapi_entries[i];
const char *a = e->name, *b = name;
while (*a && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
if (out_fn) *out_fn = e->fn;
return 0;
}
}
return -1;
}
static const dkm_kapi_table g_kapi_table = {
.magic = DKM_KAPI_MAGIC,
.version = DKM_KAPI_VERSION,
.count = sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]),
.entries = g_kapi_entries,
.lookup = kapi_lookup,
};
#include "../arch/x86_64/limine.h"
extern volatile struct limine_rsdp_request g_rsdp_request;
extern volatile struct limine_framebuffer_request g_fb_request;
@@ -66,9 +141,18 @@ extern volatile struct limine_module_request g_module_request;
extern volatile struct limine_hhdm_request g_hhdm_request;
void dkm_fill_platform_info(void) {
/* D6 万物互联:总线先播种再挂载(必须先于一切驱动加载——
* driver_init 里即可 api->bus->register_service)。 */
dkm_bus_init();
g_kernel_api.bus = dkm_bus_get_api();
g_kernel_api.dma = dma_get_api();
g_kernel_api.net = net_get_api();
g_kernel_api.block = block_get_api();
g_kernel_api.mmio = paging_get_api();
/* M4/Phase7-8: 调度器与恢复根服务表(drr 为原占位字段,此处激活) */
g_kernel_api.sched = utsm_sched_get_api();
g_kernel_api.drr = drr_get_recovery_api();
if (g_rsdp_request.response && g_rsdp_request.response->address) {
g_kernel_api.rsdp_address = g_rsdp_request.response->address;
}
@@ -87,4 +171,43 @@ void dkm_fill_platform_info(void) {
if (g_hhdm_request.response) {
g_kernel_api.hhdm_offset = g_hhdm_request.response->offset;
}
g_kernel_api.mm_map_mmio = mm_map_mmio;
g_kernel_api.mm_unmap_mmio = mm_unmap_mmio;
/* B7 阶段2: 暴露 LAPIC EOI 钩子注册入口(apic.drv LAPIC 接管时调用) */
g_kernel_api.register_apic_eoi = idt_register_apic_eoi;
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
g_kernel_api.irq_vector_free = irq_vector_free;
/* D3: 按名导出表挂入 kernel_api(尾部追加,ABI 兼容) */
g_kernel_api.kapi = &g_kapi_table;
}
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DKM 能力,建立代码骨架,
* 调用即莲花 panic(DKM-E 系列)。当前无调用方(骨架)。
* =================================================================== */
/* D1: DKM 驱动热卸载(引用计数)。driver_desc 已有 NO_UNLOAD 标志位,
* 但卸载路径本身(refcount + driver_exit + 段回收)未实现。 */
void dkm_unload_driver(u32 driver_index) {
(void)driver_index;
panic_full("DKM-E01 DRIVER HOT UNLOAD NOT IMPLEMENTED",
"refcount unload path: designed (D1), not implemented", 0);
}
/* D2: 裸机器码加载器(无 ELF 头,直接映射 .text blob 到可执行页跳转)。
* SAS-R0 单地址空间下合法;加载器未实现。 */
void *dkm_load_bare_code(const void *blob, u64 size) {
(void)blob; (void)size;
panic_full("DKM-E02 BARE CODE LOADER NOT IMPLEMENTED",
"headerless machine-code loader: designed (D2), not implemented", 0);
return 0;
}
/* 多核:SIPI 唤醒 AP(apic.drv 注释预留)。BSP-only 是当前既定边界,
* 一旦被调用即 panic 而非静默单核继续。 */
void dkm_sipi_wake_ap(u32 lapic_id, u64 entry_eip) {
(void)lapic_id; (void)entry_eip;
panic_full("DKM-E03 SIPI AP WAKE NOT IMPLEMENTED",
"AP startup framework: designed, not implemented (BSP-only)", 0);
}
Binary file not shown.
+360 -38
View File
@@ -1,12 +1,18 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/types.h>
#include "../arch/x86_64/limine.h"
#include "../kernel/ini_parser.h"
/* 前向声明:带 FUCK [drivers] 过滤的加载入口(定义见下文) */
void dsm_load_by_manifest_ex(const ini_config *cfg);
#define DSM_MAX_DRIVERS_PER_STAGE 8
#define DSM_MAX_STAGES 4
#define DSM_MAX_DEPENDS 4
#define DSM_MAX_PROVIDES 4
#define DSM_MAX_CAPS 128
typedef struct dsm_driver_entry {
char name[32];
@@ -19,6 +25,9 @@ typedef struct dsm_driver_entry {
u32 provide_count;
struct limine_file *boot_module;
int loaded;
/* P0(万物互联):终态标志——已加载/被 FUCK 关闭/模块缺失/加载失败,
* 不再是本轮不动点迭代的候选。resolved 但 !loaded 的驱动不登记 provides。 */
int resolved;
} dsm_driver_entry;
typedef struct dsm_stage_entry {
@@ -32,6 +41,119 @@ typedef struct dsm_stage_entry {
static dsm_stage_entry g_stages[DSM_MAX_STAGES];
static u32 g_stage_count;
static int streq_static(const char *a, const char *b);
/* ===================================================================
* P0(万物互联):DSM 依赖图强制
*
* capability 集合 = 已成功加载驱动的 provides token 并集。
* 加载前校验 depends 全满足(miss → DKM-E44);
* 同 stage 内按依赖不动点迭代(环 → DKM-E47)。
* 性能不是目标(多趟扫描),正确性第一——设计定稿见
* docs/RE/万物互联与服务总线设计.md §1.5。
* =================================================================== */
static char g_caps[DSM_MAX_CAPS][32];
static u32 g_cap_count;
static int dsm_capability_satisfied(const char *token) {
for (u32 i = 0; i < g_cap_count; i++) {
if (streq_static(g_caps[i], token)) return 1;
}
return 0;
}
static void dsm_capability_add(const char *token) {
if (dsm_capability_satisfied(token)) return;
if (g_cap_count >= DSM_MAX_CAPS) {
panic_full("DKM-E48 CAPABILITY TABLE FULL",
"P0 dependency graph: g_caps overflow (DSM_MAX_CAPS)", 0);
return;
}
for (u32 i = 0; i + 1 < 32 && token[i]; i++) {
g_caps[g_cap_count][i] = token[i];
}
g_caps[g_cap_count][31] = 0;
g_cap_count++;
}
static void dsm_register_provides(const dsm_driver_entry *drv) {
for (u32 i = 0; i < drv->provide_count; i++) {
dsm_capability_add(drv->provides[i]);
}
}
/* P1(万物互联):stage>=4 的 provides 总线契约校验——driver_init 成功后,
* 每个 provides token T 必须已有 "srv.T" 或 "srv.T.*" 服务注册到总线,
* 否则 DKM-E45(零降级:provides 是对消费者/排序者的承诺,注册不到即违约)。
* stage0-3 老 14 驱动豁免:其 provides 由内核注册表兑现(ahci→block
* registry 等),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目。 */
static void dsm_verify_provides(const dsm_stage_entry *stage, const dsm_driver_entry *drv) {
if (stage->id < 4) return;
for (u32 i = 0; i < drv->provide_count; i++) {
char srv_name[40];
const char *token = drv->provides[i];
srv_name[0] = 's'; srv_name[1] = 'r'; srv_name[2] = 'v'; srv_name[3] = '.';
u32 p = 4;
while (token[p - 4] && p + 1 < sizeof(srv_name)) {
srv_name[p] = token[p - 4];
p++;
}
srv_name[p] = 0;
if (!dkm_bus_prefix_registered(srv_name)) {
log_error("[DSM] provides token not registered on bus: ");
log_info(drv->name);
log_info(token);
panic_full("DKM-E45 PROVIDES NOT REGISTERED",
"stage>=4 driver provides token has no matching srv.* bus service", 0);
}
}
}
/* depends 全满足返回 1;否则返回 0 并(可选)写出第一个未满足 token 的下标 */
static int dsm_deps_satisfied(const dsm_driver_entry *drv, u32 *missing_idx) {
for (u32 i = 0; i < drv->depend_count; i++) {
if (!dsm_capability_satisfied(drv->depends[i])) {
if (missing_idx) *missing_idx = i;
return 0;
}
}
return 1;
}
/* token 是否由本 stage 尚未 resolved 的候选提供(用于区分缺依赖 vs 成环) */
static int dsm_dep_pending_in_stage(const dsm_stage_entry *stage, const char *token) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->provide_count; i++) {
if (streq_static(drv->provides[i], token)) return 1;
}
}
return 0;
}
/* 不动点停滞:先判真缺依赖(DKM-E44),全在剩余候选里则判环(DKM-E47)。
* 零降级:两者都是编程/manifest 错误,莲花屏。 */
static void dsm_report_dependency_stall(const dsm_stage_entry *stage) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->depend_count; i++) {
const char *dep = drv->depends[i];
if (!dsm_capability_satisfied(dep) && !dsm_dep_pending_in_stage(stage, dep)) {
log_error("[DSM] dependency not satisfied: ");
log_info(drv->name);
log_info(dep);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"manifest depends token not provided by any loaded/pending driver", 0);
return;
}
}
}
panic_full("DKM-E47 DEPENDENCY CYCLE",
"manifest stage dependency graph has a cycle (no progress)", 0);
}
extern volatile struct limine_module_request g_module_request;
static int dsm_skip_ws(const char **p) {
@@ -131,6 +253,7 @@ static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) {
drv->required = 1;
drv->boot_module = 0;
drv->loaded = 0;
drv->resolved = 0;
drv->class_id = 0;
if (dsm_expect(p, '{') != 0) return;
@@ -262,7 +385,12 @@ static int dsm_parse_manifest(const char *text, u64 size) {
dsm_expect(&p, ':');
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) {
dsm_skip_ws(&p);
if (*p == ']') { p++; break; }
@@ -283,14 +411,26 @@ static int dsm_parse_manifest(const char *text, u64 size) {
}
void dsm_load_by_manifest(void) {
struct limine_module_response *rsp = g_module_request.response;
dsm_load_by_manifest_ex(NULL);
}
static int dsm_ensure_manifest(void) {
struct limine_module_response *rsp;
struct limine_file *manifest_file;
u64 i;
if (g_stage_count) return 0;
rsp = g_module_request.response;
if (!rsp) {
log_warn("[DSM] no Limine module response");
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;
for (u64 i = 0; i < rsp->module_count; i++) {
manifest_file = 0;
for (i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i];
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
manifest_file = f;
@@ -300,60 +440,242 @@ void dsm_load_by_manifest(void) {
if (!manifest_file) {
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");
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
log_info("[DSM] manifest parsed");
log_hex64("[DSM] stages=", (u64)g_stage_count);
return 0;
}
static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max_stage_id) {
if (dsm_ensure_manifest() != 0) return;
for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si];
if (limit_stage && stage->id > max_stage_id) continue;
log_info("[DSM] stage begin");
log_info(stage->name);
log_hex64("[DSM] driver count=", stage->driver_count);
/* P0:同 stage 内按依赖不动点迭代——每趟加载 deps 已满足的候选,
* 直到全部 resolved;停滞即 DKM-E44/E47(见 dsm_report_dependency_stall)。
* 多趟扫描 O(n²·趟数),性能刻意不管,正确性第一。 */
u32 pending = 0;
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
if (!stage->drivers[di].resolved) pending++;
}
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_info("[DSM] module not found");
while (pending > 0) {
u32 pending_before = pending;
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (drv->resolved) continue;
/* === FUCK [drivers] 驱动开关检查 ===
* 关闭即退出候选(provides 不登记,依赖它的模块将 DKM-E44)。 */
if (cfg) {
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
if (!enabled) {
log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name);
drv->resolved = 1;
pending--;
continue;
}
}
/* P0:depends 校验——暂不满足先跳过,等本 stage 后续候选登记
* provides 后下一趟重试;整体停滞时统一判罚(零降级)。 */
if (!dsm_deps_satisfied(drv, 0)) continue;
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_info("[DSM] module not found");
log_info(drv->name);
drv->resolved = 1;
pending--;
if (drv->required && stage->required) {
/* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */
panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING",
"required driver boot module not found in manifest stage", 0);
break;
}
continue;
}
f = drv->boot_module;
log_info("[DSM] loading driver");
log_info(drv->name);
if (drv->required && stage->required) {
log_error("[DSM] required module missing, abort stage");
break;
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
if (drv->required && stage->required) {
panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID",
"required driver boot module failed ELF64 check", 0);
}
drv->resolved = 1;
pending--;
continue;
}
continue;
}
struct limine_file *f = drv->boot_module;
log_info("[DSM] loading driver");
log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
continue;
}
struct dkm_symbol_scan symbols;
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
continue;
}
int result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
if (drv->required && stage->required) {
log_error("[DSM] required driver failed, abort stage");
break;
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
if (drv->required && stage->required) {
panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED",
"required driver symbol scan failed", 0);
}
drv->resolved = 1;
pending--;
continue;
}
} else {
drv->loaded = 1;
result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
if (drv->required && stage->required) {
/* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */
panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED",
"required driver relocation/load failed", 0);
}
drv->resolved = 1;
pending--;
} else {
drv->loaded = 1;
drv->resolved = 1;
pending--;
/* P0:加载成功才登记 provides(disabled/missing/failed 一律不登记) */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
}
}
if (pending > 0 && pending == pending_before) {
/* 一整趟零进展:真缺依赖(DKM-E44)或依赖环(DKM-E47) */
dsm_report_dependency_stall(stage);
/* fallback:停滞的候选全部放弃(不登记 provides),结束本 stage */
for (u32 di = 0; di < stage->driver_count; di++) {
if (stage->drivers[di].resolved) continue;
log_warn("[DSM] fallback: driver skipped (unresolved deps): ");
log_info(stage->drivers[di].name);
stage->drivers[di].resolved = 1;
}
pending = 0;
}
}
log_info("[DSM] stage end");
}
}
void dsm_load_by_manifest_upto(const ini_config *cfg, u32 max_stage_id) {
dsm_load_stage_range(cfg, 1, max_stage_id);
}
/* 按名加载公共实现。strict_deps=1:depends 校验强制(DKM-E44)——供
* kern.dkm.load_by_name(万物互联 P3)等常规路径使用;
* strict_deps=0:顺序豁免版——唯一调用方是 kernel/main.c VMM 路径的
* pci+ahci 预加载(必须先于 apic/stage0,否则 guest 陷入 IDTR=0
* VM-exit 风暴,见 main.c 注释)。设计内顺序豁免,登记于设计文档 §6。 */
static void dsm_load_named_impl(const ini_config *cfg, const char *name, int strict_deps) {
if (!name || dsm_ensure_manifest() != 0) {
/* 严格错误策略插桩:按名加载 manifest 缺失零降级(DKM-E11) */
panic_full("DKM-E11 NAMED LOAD MANIFEST MISSING",
"dsm_load_named: null name or manifest unavailable", 0);
return;
}
for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si];
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (!streq_static(drv->name, name)) continue;
if (drv->loaded) return;
if (cfg && !ini_get_bool(cfg, "drivers", drv->name, 1)) {
log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name);
return;
}
/* P0:按名加载同样强制依赖(此时无同 stage 重试机会,miss 即判罚) */
if (strict_deps && !dsm_deps_satisfied(drv, 0)) {
log_error("[DSM] named driver dependency not satisfied: ");
log_info(drv->name);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"dsm_load_named: depends token not provided by any loaded driver", 0);
return;
}
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_warn("[DSM] named module not found");
log_info(name);
/* 严格错误策略插桩:named 驱动缺模块零降级(DKM-E12) */
panic_full("DKM-E12 NAMED DRIVER MODULE MISSING",
"dsm_load_named: named driver boot module not found", 0);
return;
}
f = drv->boot_module;
log_info("[DSM] loading named driver");
log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
/* 严格错误策略插桩:named 驱动 ELF 校验失败零降级(DKM-E13) */
panic_full("DKM-E13 NAMED DRIVER ELF INVALID",
"dsm_load_named: named driver failed ELF64 check", 0);
return;
}
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
/* 严格错误策略插桩:named 驱动符号扫描失败零降级(DKM-E14) */
panic_full("DKM-E14 NAMED DRIVER SYMBOL SCAN FAILED",
"dsm_load_named: named driver symbol scan failed", 0);
return;
}
result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
/* 严格错误策略插桩:named 驱动加载失败零降级(DKM-E15) */
panic_full("DKM-E15 NAMED DRIVER LOAD FAILED",
"dsm_load_named: named driver relocation/load failed", 0);
return;
}
drv->loaded = 1;
drv->resolved = 1;
/* P0:按名加载成功同样登记 provides */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
return;
}
}
log_warn("[DSM] named driver not in manifest");
log_info(name);
/* 严格错误策略插桩:named 驱动不在 manifest 零降级(DKM-E16) */
panic_full("DKM-E16 NAMED DRIVER NOT IN MANIFEST",
"dsm_load_named: named driver not found in manifest", 0);
}
void dsm_load_named(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 1);
}
void dsm_load_named_boot(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 0);
}
void dsm_load_by_manifest_ex(const ini_config *cfg) {
dsm_load_stage_range(cfg, 0, 0);
}
Binary file not shown.
+527 -6
View File
@@ -10,6 +10,15 @@
#include <utsm/drr.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/dma.h>
#include <utsm/segment.h>
#include <utsm/config.h>
#include <utsm/dkm.h>
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
extern void outb(u16 port, u8 value);
/* ---- 内部内存操作(不依赖 libc) ---- */
static void drr_memzero(void *dst, u64 n) {
@@ -71,14 +80,118 @@ u64 drr_crc64(const void *data, u64 len) {
/* ---- 全局 DRR 状态 ---- */
static drr_state g_drr;
/* ---- Root key(最终应从安全启动链获取) ---- */
static const u64 g_root_key[4] = {
/* ---- Phase 8: 快照区 / 看门狗 / 恢复行为控制 ---- */
#define DRR_SNAP_MAGIC 0x445252534E415021ULL /* "DRRSNAP!" */
#define DRR_SNAP_VERSION 1u
typedef struct {
u64 magic;
u32 version;
u32 capacity; /* 条目容量(page0 空间决定) */
u32 used;
u32 generation;
u64 crc;
} drr_snap_header;
static dkm_dma_buffer g_snap_region;
static int g_snap_ok = 0;
static u32 g_snap_capacity = 0;
static int g_reboot_enabled = 1;
static void (*g_sched_fault_cb)(u32 task_slot) = 0;
typedef struct {
u32 task_slot; /* 0xFFFFFFFF = 空闲 */
u32 timeout_ticks;
u64 last_kick_tick;
} drr_watchdog_entry;
static drr_watchdog_entry g_drr_wd[DRR_WATCHDOG_MAX];
static drr_snap_header *snap_header(void) {
return (drr_snap_header *)g_snap_region.virt;
}
static drr_ckpt_page_entry *snap_entries(void) {
return (drr_ckpt_page_entry *)((u8 *)g_snap_region.virt + 64);
}
static u8 *snap_page_ptr(u32 idx) {
return (u8 *)g_snap_region.virt + 4096ULL * (1ULL + (u64)idx);
}
/* ---- Root key(Phase 9: RDRAND + TSC jitter 混合,boot 生成一次)
* 生成一次后固化:drr_init 可重入(emergency reset),root key 不换,
* 否则旧 segment key 全部失效。持久化(sealed disk region)留待后续。 ---- */
static u64 g_root_key_storage[4];
static int g_root_key_ready = 0;
static const u64 g_root_key_fallback[4] = {
0x4452525554534D31ULL, /* "DRRUTSM1" */
0xFEEDFACECAFEF00DULL,
0x0123456789ABCDEFULL,
0xFEDCBA9876543210ULL
};
static u64 drr_tsc(void) {
u64 v;
__asm__ volatile("rdtsc" : "=A"(v));
return v;
}
static int drr_have_rdrand(void) {
u32 a, b, c, d;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(1u), "c"(0u));
return (int)((c >> 30) & 1u);
}
static int drr_rdrand64(u64 *out) {
u8 ok;
u64 v;
__asm__ volatile("rdrand %0; setc %1" : "=r"(v), "=qm"(ok) :: "cc");
if (!ok) return 0;
*out = v;
return 1;
}
static void drr_root_key_generate(void) {
if (g_root_key_ready) {
g_drr.root_key = g_root_key_storage;
return;
}
int rdrand_ok = 1;
if (drr_have_rdrand()) {
for (int i = 0; i < 4; i++) {
if (!drr_rdrand64(&g_root_key_storage[i])) {
rdrand_ok = 0;
break;
}
}
} else {
rdrand_ok = 0;
}
if (rdrand_ok) {
/* 纵深:混入 TSC jitter,防 RDRAND 单点失效 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] ^= drr_tsc() * 0x9e3779b97f4a7c15ULL;
}
} else {
/* 无 RDRAND:常量基底 + 多轮 TSC jitter 混合 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] = g_root_key_fallback[i];
}
for (int r = 0; r < 16; r++) {
u64 j = drr_tsc();
for (int i = 0; i < 4; i++) {
j = (j ^ drr_tsc()) * 0x9e3779b97f4a7c15ULL;
g_root_key_storage[i] ^= j;
for (volatile int d = 0; d < 50; d++) { }
}
}
}
g_root_key_ready = 1;
g_drr.root_key = g_root_key_storage;
log_info("[DRR] root key generated");
log_hex64("[DRR] rdrand=", (u64)rdrand_ok);
}
/* ---- Emergency Pool 内部分配 ---- */
static void *pool_alloc_internal(u64 size, u64 alignment) {
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
@@ -105,25 +218,58 @@ void drr_emergency_reset(void) {
drr_init();
}
/* ---- R3b: FUCK 配置接线(drr_apply_config 设置,drr_init 消费) ---- */
static u64 g_cfg_pool_bytes; /* 0 = 编译期默认 */
static u64 g_cfg_log_size; /* 0 = 编译期默认 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size) {
g_cfg_pool_bytes = pool_kb ? pool_kb * 1024ULL : 0;
g_cfg_log_size = recovery_log_size;
if (g_cfg_pool_bytes > DRR_EMERGENCY_POOL_SIZE) {
log_warn("[DRR] pool_kb exceeds compile-time pool, clamped");
log_hex64("[DRR] compile-time=", DRR_EMERGENCY_POOL_SIZE);
g_cfg_pool_bytes = DRR_EMERGENCY_POOL_SIZE;
}
}
/* ---- 初始化 ---- */
void drr_init(void) {
log_info("[DRR] init begin");
drr_memzero(&g_drr, sizeof(g_drr));
/* R3b: 池上限 = min(编译期池, FUCK 配置);小于最小子缓冲布局时回退默认 */
g_drr.pool_offset = 0;
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
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.heartbeat_interval = 1000;
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_size = 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);
g_drr.log_capacity = DRR_RECOVERY_LOG_SIZE / sizeof(drr_log_entry);
u64 log_size = DRR_RECOVERY_LOG_SIZE;
if (g_cfg_log_size && g_cfg_log_size >= sizeof(drr_log_entry) &&
g_cfg_log_size <= g_drr.pool_limit - g_drr.pool_offset) {
log_size = g_cfg_log_size;
} else if (g_cfg_log_size) {
log_warn("[DRR] recovery_log_size out of range, default kept");
}
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(log_size, 64);
g_drr.log_capacity = (u32)(log_size / sizeof(drr_log_entry));
g_drr.log_count = 0;
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
@@ -142,9 +288,102 @@ void drr_init(void) {
g_drr.ckpt_b.version = DRR_CKPT_VERSION;
g_drr.initialized = 1;
log_hex64("[DRR] pool_limit=", g_drr.pool_limit);
log_hex64("[DRR] log_capacity=", (u64)g_drr.log_capacity);
/* ---- Phase 8: 快照区(dma 页,独立于普通堆;失败降级 metadata-only) ---- */
g_snap_ok = 0;
g_snap_capacity = 0;
g_reboot_enabled = 1;
g_sched_fault_cb = 0;
for (int wdi = 0; wdi < DRR_WATCHDOG_MAX; wdi++) {
g_drr_wd[wdi].task_slot = 0xFFFFFFFFu;
g_drr_wd[wdi].timeout_ticks = 0;
g_drr_wd[wdi].last_kick_tick = 0;
}
{
/* page0 = 头 + 条目数组,其后 128 页快照数据(共 129 页) */
dkm_dma_buffer buf;
if (dma_alloc_pages(1 + 128, 4096, 0xFFFFFFFFULL, &buf) == 0 && buf.virt) {
g_snap_region = buf;
u32 cap = (u32)((4096 - 64) / sizeof(drr_ckpt_page_entry));
drr_snap_header *h = (drr_snap_header *)buf.virt;
drr_memzero(h, 4096);
h->magic = DRR_SNAP_MAGIC;
h->version = DRR_SNAP_VERSION;
h->capacity = cap;
h->used = 0;
g_snap_capacity = cap;
g_snap_ok = 1;
log_info("[DRR] snapshot region ok");
log_hex64("[DRR] pages=", 1 + 128);
} else {
/* 严格错误策略插桩:快照区是 DRR 页回滚的前提,缺失即降级
* metadata-only——按零降级原则直接 panic(DRR-E01)。 */
panic_full("DRR-E01 SNAPSHOT REGION ALLOC FAILED",
"snapshot dma pages unavailable; metadata-only mode is forbidden", 0);
}
}
log_info("[DRR] init ok");
}
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DRR 能力,建立代码骨架,
* 调用即 panic(DRR-E2x 系列)。当前无调用方(骨架)。
* =================================================================== */
/* Root key 持久化(sealed disk region):drr_init 可重入要求 root key
* 跨 emergency reset 固化。当前实现走内存 fallback key(见
* drr_root_key_generate),sealed disk 尚未落盘。 */
void drr_root_key_seal_to_disk(void) {
panic_full("DRR-E02 ROOT KEY SEAL NOT IMPLEMENTED",
"sealed disk region for root key persistence: designed, not implemented", 0);
}
/* task_kill crypto erase:任务被杀时 key_epoch++ 并擦除其密钥材料。
* 当前 task_exit 只回收 slot,不做 crypto erase。 */
void drr_task_kill_crypto_erase(u32 task_slot) {
(void)task_slot;
panic_full("DRR-E03 TASK KILL CRYPTO ERASE NOT IMPLEMENTED",
"key_epoch++ erase on task kill: designed, not implemented", 0);
}
/* segment/system 级回滚入口(R4 遗留):页级回滚已实现,
* segment/system 级接口的调用方尚未接线。 */
void drr_rollback_segment(u32 seg_slot) {
(void)seg_slot;
panic_full("DRR-E04 SEGMENT ROLLBACK NOT IMPLEMENTED",
"segment-level rollback: designed (R4), not verified/implemented", 0);
}
void drr_rollback_system(void) {
panic_full("DRR-E05 SYSTEM ROLLBACK NOT IMPLEMENTED",
"system-level rollback: designed (R4), not verified/implemented", 0);
}
/* DKM 驱动 recovery ops(DKM README 设计:quiesce/reset/reinit/dump_state)。
* 驱动侧从未提供该回调表,DRR 亦未在恢复路径调用。 */
typedef struct {
u32 magic; /* 'D','R','O','P' */
u32 version;
void (*quiesce)(void *drv);
void (*reset)(void *drv);
void (*reinit)(void *drv);
void (*dump_state)(void *drv);
} dkm_driver_recovery_ops;
void drr_driver_recovery_dispatch(const dkm_driver_recovery_ops *ops, void *drv) {
(void)drv;
if (!ops) {
panic_full("DRR-E06 DRIVER RECOVERY OPS MISSING",
"driver did not provide dkm_driver_recovery_ops", 0);
return;
}
panic_full("DRR-E07 DRIVER RECOVERY NOT IMPLEMENTED",
"quiesce/reset/reinit dispatch: designed (DKM README), not implemented", 0);
}
const u64 *drr_get_root_key(void) {
return g_drr.root_key;
}
@@ -172,6 +411,30 @@ void drr_report_fault(const char *reason) {
g_drr.recovery_generation++;
}
/* ---- F1: 崩溃归档(panic_full 调用;crash_buf 写 symbol+msg+rip 简表) ----
* 返回 0=已归档;-1=DRR 未初始化(crash_buf 不可用)。 */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs) {
if (!g_drr.initialized || !g_drr.crash_buf || g_drr.crash_buf_size == 0) {
return -1;
}
u64 i = 0;
u8 *b = g_drr.crash_buf;
u64 cap = g_drr.crash_buf_size - 1;
/* 头部标记 */
const char *magic = "CRASH1";
for (const char *s = magic; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = symbol ? symbol : "UNKNOWN"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = msg ? msg : "-"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i] = 0;
/* recovery generation 前进(归档计数) */
g_drr.recovery_generation++;
log_error("[DRR] crash archived");
log_hex64("[DRR] generation=", g_drr.recovery_generation);
return 0;
}
/* ---- Recovery Log ---- */
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
if (!g_drr.initialized || !g_drr.log_base) return;
@@ -314,3 +577,261 @@ void drr_heartbeat_tick(void) {
u64 drr_heartbeat_get(void) {
return g_drr.heartbeat_counter;
}
/* ===================================================================
* Phase 8: 真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* =================================================================== */
int drr_ckpt_snapshot_dirty(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2; /* metadata-only 模式不支持页快照 */
drr_checkpoint_begin(DRR_CKPT_DIRTY_PAGE);
drr_snap_header *h = snap_header();
drr_ckpt_page_entry *entries = snap_entries();
u32 used = 0;
for (u32 slot = 0; slot < g_utsm_max_segments; slot++) {
utsm_segment_desc *desc = utsm_get_segment(slot);
if (!desc || desc->state != UTSM_SEG_ACTIVE) continue;
desc->state = UTSM_SEG_CHECKPOINTING;
for (u32 s = 0; s < desc->dirty_shard_count && desc->dirty_shards; s++) {
utsm_dirty_shard *shard = &desc->dirty_shards[s];
if (!shard->bitmap) continue;
u64 words = (shard->page_count + 63) / 64;
for (u64 w = 0; w < words && used < g_snap_capacity; w++) {
u64 word = shard->bitmap[w];
while (word && used < g_snap_capacity) {
u64 bit = (u64)__builtin_ctzll(word);
u64 page = w * 64 + bit;
u8 *src = desc->cipher_base + page * UTSM_PAGE_SIZE;
u8 *dst = snap_page_ptr(used);
drr_memcpy(dst, src, UTSM_PAGE_SIZE);
entries[used].seg_slot = slot;
entries[used].page_index = (u32)page;
entries[used].cipher_vaddr = (u64)src;
entries[used].crc = drr_crc64(dst, UTSM_PAGE_SIZE);
entries[used].key_epoch = desc->key_epoch;
entries[used].mac = utsm_page_mac(g_drr.root_key, slot, page,
desc->key_epoch, dst);
entries[used].region_idx = used;
entries[used].used = 1;
used++;
/* 快照成功才清脏位;区满时保留脏位(部分提交,下轮重试) */
shard->bitmap[w] &= ~(1ULL << bit);
if (shard->dirty_count > 0) shard->dirty_count--;
word &= ~(1ULL << bit);
}
}
shard->last_checkpoint_epoch = g_drr.recovery_generation;
}
desc->state = UTSM_SEG_ACTIVE;
desc->checkpoint_generation = g_drr.recovery_generation;
if (used >= g_snap_capacity) break;
}
/* 区域头(crc 不含自身字段) */
h->used = used;
h->generation = (u32)g_drr.recovery_generation;
h->crc = drr_crc64(h, sizeof(drr_snap_header) - sizeof(u64));
/* 元数据 + A/B 原子切换(begin 已选好 inactive slot,同一表达式取回) */
drr_checkpoint_meta *target = (g_drr.active_slot == 0) ? &g_drr.ckpt_b : &g_drr.ckpt_a;
target->dirty_page_count = used;
/* Phase 9: mac_root = keyed BLAKE2b 链(root_key 派生,绑定全部页 MAC) */
{
u8 kb[32];
u8 chain[64];
for (int i = 0; i < 4; i++) utsm_store64_le(kb + 8 * i, g_drr.root_key[i]);
utsm_blake2b_ctx mc;
utsm_blake2b_init(&mc, 64, kb, 32);
for (u32 i = 0; i < used; i++) {
u8 rec[8 + 8 + 8 + 8];
utsm_store64_le(rec + 0, 0x544F4F524D535455ULL); /* "UTSMROOT" LE */
utsm_store64_le(rec + 8, ((u64)entries[i].seg_slot) |
((u64)entries[i].page_index << 32));
utsm_store64_le(rec + 16, entries[i].key_epoch);
utsm_store64_le(rec + 24, entries[i].mac);
utsm_blake2b_update(&mc, rec, sizeof(rec));
}
utsm_blake2b_final(&mc, chain);
target->mac_root = utsm_load64_le(chain);
}
target->segment_count = 0;
drr_checkpoint_finish();
log_info("[DRR] ckpt snapshot ok");
log_hex64("[DRR] pages=", used);
return (int)used;
}
int drr_rollback_pages(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2;
drr_checkpoint_meta meta;
if (drr_checkpoint_recover(&meta) != 0) return -3;
if (meta.type != DRR_CKPT_DIRTY_PAGE) return -4;
drr_snap_header *h = snap_header();
if (h->magic != DRR_SNAP_MAGIC || h->version != DRR_SNAP_VERSION) return -5;
u32 used = h->used;
if (used > h->capacity) used = h->capacity;
drr_ckpt_page_entry *entries = snap_entries();
/* 触碰段先置 RECOVERING */
for (u32 i = 0; i < used; i++) {
utsm_segment_desc *d = utsm_get_segment(entries[i].seg_slot);
if (d && d->state == UTSM_SEG_ACTIVE) d->state = UTSM_SEG_RECOVERING;
}
int failed = 0;
u32 restored = 0;
for (u32 i = 0; i < used; i++) {
drr_ckpt_page_entry *e = &entries[i];
u8 *snap = snap_page_ptr(e->region_idx);
utsm_segment_desc *d = utsm_get_segment(e->seg_slot);
if (drr_crc64(snap, UTSM_PAGE_SIZE) != e->crc) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback crc fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
/* Phase 9: 页 MAC 校验(伪造/跨纪元重放/区域篡改在此被拒) */
u64 mac = utsm_page_mac(g_drr.root_key, e->seg_slot, e->page_index,
e->key_epoch, snap);
if (mac != e->mac) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback mac fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
drr_memcpy((void *)e->cipher_vaddr, snap, UTSM_PAGE_SIZE);
restored++;
/* 清除该页脏位(P2 之类的回写不再被误判为未 checkpoint) */
if (d && d->dirty_shard_count > 0 && d->dirty_shards && d->dirty_shards[0].bitmap) {
utsm_dirty_shard *sh = &d->dirty_shards[0];
u64 page = e->page_index;
if (page < sh->page_count && (sh->bitmap[page / 64] & (1ULL << (page % 64)))) {
sh->bitmap[page / 64] &= ~(1ULL << (page % 64));
if (sh->dirty_count > 0) sh->dirty_count--;
}
}
if (d && d->state == UTSM_SEG_RECOVERING) d->state = UTSM_SEG_ACTIVE;
}
log_info("[DRR] rollback done");
log_hex64("[DRR] pages=", restored);
return failed ? -2 : (int)restored;
}
void drr_system_rollback(const char *reason) {
log_error("[DRR] SYSTEM ROLLBACK");
if (reason) log_error(reason);
log_hex64("[DRR] generation=", g_drr.recovery_generation);
log_hex64("[DRR] heartbeat=", g_drr.heartbeat_counter);
log_error("[DRR] rebooting (8042 reset)");
for (;;) {
__asm__ volatile("cli");
outb(0x64, 0xFE); /* 8042 复位(A20 gate 已无关紧要) */
for (volatile u32 i = 0; i < 1000000u; i++) { }
__asm__ volatile("hlt");
}
}
void drr_handle_task_fault(u32 task_slot, const char *reason) {
log_error("[DRR] FAULT");
if (reason) log_error(reason);
log_hex64("[DRR] task_slot=", task_slot);
drr_report_fault(reason ? reason : "task fault");
drr_log_write_intent(task_slot, 0, 0);
int pages = drr_rollback_pages();
log_hex64("[DRR] rollback_pages=", (u64)(i64)pages);
if (g_sched_fault_cb) {
g_sched_fault_cb(task_slot); /* 可能不返回(当前任务被杀+切换) */
drr_log_write_commit(task_slot, 0, 0);
return;
}
if (g_reboot_enabled) {
drr_system_rollback(reason ? reason : "task fault (no sched cb)");
}
/* reboot disabled(selftest):仅留日志 */
}
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks) {
if (!g_drr.initialized || timeout_ticks == 0) return -1;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == 0xFFFFFFFFu) {
g_drr_wd[i].task_slot = task_slot;
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
return -2;
}
int drr_watchdog_unregister(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].task_slot = 0xFFFFFFFFu;
return 0;
}
}
return -1;
}
void drr_watchdog_kick(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return;
}
}
}
void drr_watchdog_check(void) {
if (!g_drr.initialized) return;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
u32 slot = g_drr_wd[i].task_slot;
if (slot == 0xFFFFFFFFu) continue;
u64 now = g_drr.heartbeat_counter;
if (now - g_drr_wd[i].last_kick_tick > g_drr_wd[i].timeout_ticks) {
g_drr_wd[i].last_kick_tick = now; /* 防重复触发 */
drr_handle_task_fault(slot, "watchdog timeout");
}
}
}
void drr_set_reboot_enabled(int enabled) {
g_reboot_enabled = enabled;
}
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot)) {
g_sched_fault_cb = cb;
}
/* kernel_api.drr 服务表(dkm_fill_platform_info 挂入) */
static const drr_recovery_api g_drr_api = {
.report_fault = drr_report_fault,
.heartbeat_get = drr_heartbeat_get,
.emergency_alloc = drr_emergency_alloc,
.ckpt_snapshot_dirty = drr_ckpt_snapshot_dirty,
.rollback_pages = drr_rollback_pages
};
const drr_recovery_api *drr_get_recovery_api(void) {
return &g_drr_api;
}
Binary file not shown.
+1
View File
@@ -2,6 +2,7 @@
#define UTSM_ASSERT_H
#include <utsm/log.h>
#include <utsm/panic.h>
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
+29
View File
@@ -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 */
+48
View File
@@ -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 */
+1
View File
@@ -25,6 +25,7 @@ typedef struct dkm_block_api {
u64 (*sector_size)(u32 index);
const char *(*device_name)(u32 index);
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
u64 (*sector_count)(u32 index); /* 0x38: tail-append, 老内核该槽为 0 表示未知 */
} dkm_block_api;
void block_init(void);
+24 -5
View File
@@ -1,13 +1,32 @@
#ifndef UTSM_CONFIG_H
#define UTSM_CONFIG_H
#include <utsm/types.h>
/* ---- 编译期固定常量(不暴露给 FUCK 配置) ---- */
#define UTSM_CACHE_LINE_SIZE 64ULL
#define UTSM_PAGE_SIZE 4096ULL
#define UTSM_MAX_SEGMENTS 1024U
#define UTSM_MAX_CAPABILITIES 256U
#define UTSM_MAX_PCKC_KEYS 8U
#define UTSM_ARENA_SIZE (16ULL * 1024ULL * 1024ULL)
#define UTSM_DIRTY_SHARD_PAGES 1024ULL
#define UTSM_KERNEL_STACK_SIZE 16384ULL
/* ---- 编译期默认值宏(用于静态数组大小上限) ---- */
#define UTSM_MAX_SEGMENTS_DEFAULT 1024U
#define UTSM_MAX_CAPABILITIES_DEFAULT 256U
#define UTSM_MAX_PCKC_KEYS_DEFAULT 8U
#define UTSM_ARENA_SIZE_DEFAULT (64ULL * 1024ULL * 1024ULL)
#define UTSM_DIRTY_SHARD_PAGES_DEFAULT 1024ULL
/* ---- 运行期配置变量(由 kernel_main 从 FUCK 文件初始化) ---- */
extern u32 g_utsm_max_segments;
extern u32 g_utsm_max_capabilities;
extern u32 g_utsm_max_pckc_keys;
extern u64 g_utsm_arena_size;
extern u64 g_utsm_dirty_shard_pages;
/* ---- 保留旧宏名(兼容性),映射到默认值 ---- */
#define UTSM_MAX_SEGMENTS UTSM_MAX_SEGMENTS_DEFAULT
#define UTSM_MAX_CAPABILITIES UTSM_MAX_CAPABILITIES_DEFAULT
#define UTSM_MAX_PCKC_KEYS UTSM_MAX_PCKC_KEYS_DEFAULT
#define UTSM_ARENA_SIZE UTSM_ARENA_SIZE_DEFAULT
#define UTSM_DIRTY_SHARD_PAGES UTSM_DIRTY_SHARD_PAGES_DEFAULT
#endif
+13
View File
@@ -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);
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
+16 -15
View File
@@ -738,27 +738,28 @@ static inline du_u32 du_theme_accent(du_theme t) {
#define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
/* ===================================================================
* L1 — Winux-Kate 语义色值
* L1 — 桌面语义色值(白色调主题,去 winux-Kate 深色风)
* 底色白/浅灰,组件白卡片 + 浅灰描边,文字深灰,强调色深板岩。
* =================================================================== */
#define KS_BG_PRIMARY DP_KATE_BG
#define KS_BG_SECONDARY DP_KATE_PANEL_SOLID
#define KS_BG_TERTIARY DP_KATE_BG2
#define KS_BG_PRIMARY 0xFFF3F4F6u /* 页面底:浅灰 */
#define KS_BG_SECONDARY 0xFFFFFFFFu /* 面板:白 */
#define KS_BG_TERTIARY 0xFFE9ECF0u /* 三级底:稍深浅灰 */
#define KS_TEXT_PRIMARY DP_KATE_TEXT
#define KS_TEXT_DIM DP_KATE_TEXT_DIM
#define KS_TEXT_INVERT DP_KATE_BG
#define KS_TEXT_PRIMARY 0xFF1A1A1Au /* 主文字:近黑 */
#define KS_TEXT_DIM 0xFF6B7280u /* 次文字:中灰 */
#define KS_TEXT_INVERT 0xFFFFFFFFu /* accent 上的文字:白 */
#define KS_ACCENT DP_KATE_ACCENT
#define KS_ACCENT2 DP_KATE_ACCENT2
#define KS_ACCENT_DIM DP_KATE_ACCENT_DIM_SOLID
#define KS_ACCENT 0xFF111827u /* 强调:深板岩(白底上高对比) */
#define KS_ACCENT2 0xFF4B5563u /* 次强调:中板岩 */
#define KS_ACCENT_DIM 0xFFE5E7EBu /* 强调淡底:浅灰 */
#define KS_BORDER DP_KATE_PANEL_BORDER_SOLID
#define KS_BORDER_FOCUS DP_KATE_ACCENT
#define KS_BORDER_DIM 0xFF1A2840u /* 暗边框 */
#define KS_BORDER 0xFFD1D5DBu /* 边框:浅灰 */
#define KS_BORDER_FOCUS 0xFF111827u /* 聚焦边框:深板岩 */
#define KS_BORDER_DIM 0xFFE5E7EBu /* 暗边框:极浅灰 */
#define KS_DANGER DP_KATE_DANGER
#define KS_WARN DP_KATE_WARN
#define KS_DANGER 0xFFDC2626u /* 红 */
#define KS_WARN 0xFFD97706u /* 琥珀 */
/* ===================================================================
* Winux-Kate 布局常量(源自 theme.css / pages.css)
+142 -1
View File
@@ -5,6 +5,7 @@
#include <utsm/dma.h>
#include <utsm/block.h>
#include <utsm/net.h>
#include <utsm/paging.h>
#define DKM_DRIVER_MAGIC 0x444B4D31u
#define DKM_ABI_VERSION 1u
@@ -32,6 +33,76 @@
struct dkm_kernel_api;
struct dkm_driver_handle;
/* ===================================================================
* D3: 内核 API 按名导出表(kapi)
*
* "自由也要有地图。" 固定偏移 struct ABI 之外的按名查找层:
* 驱动/用户态可枚举内核服务并按名取函数指针。
* 通过 kernel_api 尾部追加的 ->kapi 指针访问(ABI 兼容)。
* =================================================================== */
typedef struct {
const char *name; /* 如 "log.info"、"dma.alloc_pages" */
void *fn; /* 服务函数指针(签名见对应服务头) */
} dkm_kapi_entry;
typedef struct {
u32 magic; /* DKM_KAPI_MAGIC */
u32 version; /* DKM_KAPI_VERSION */
u32 count; /* entries 数量 */
const dkm_kapi_entry *entries;
/* 按名查找:命中返回 0 并写 out_fn;未命中 -1 */
int (*lookup)(const char *name, void **out_fn);
} dkm_kapi_table;
#define DKM_KAPI_MAGIC 0x4b415049u /* "KAPI" */
#define DKM_KAPI_VERSION 1u
/* ===================================================================
* D6 万物互联:dkm_bus 双向服务总线
*
* kapi 是内核→模块的单向只读导出表;bus 把它双向化:
* 任何模块在 driver_init 里注册 srv.* 服务,任何模块按名消费。
* 命名空间:kern.* 仅内核可注册,srv.* 仅模块可注册(BUS-E01)。
* 失败策略:lookup miss = BUS-E04 panic(硬依赖未满足=编程错误);
* probe miss = 返回 -1(能力缺席=设计内双路径,白名单登记)。
* 生命周期:driver_init = 注册服务;"运行"也是服务(srv.<mod>.run)。
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#define DKM_BUS_MAGIC 0x53425553u /* "SBUS" */
#define DKM_BUS_VERSION 1u
#define DKM_BUS_MAX_SERVICES 128
#define DKM_BUS_MAX_NAME 48
#define DKM_BUS_F_KERNEL 1u /* 内核注册,不可注销 */
#define DKM_BUS_F_NO_UNLOAD 2u /* provider 声明 NO_UNLOAD,注销即 BUS-E05 */
typedef struct dkm_bus_service {
char name[DKM_BUS_MAX_NAME]; /* NUL 终止;必须带命名空间前缀 */
u32 version; /* 服务契约版本,消费方校验 */
u32 flags; /* DKM_BUS_F_* */
void *fn; /* 服务入口,签名由各服务契约定义 */
const struct dkm_driver_desc *provider; /* NULL = 内核;存 desc 而非
* handle——loader 的 handle 是栈
* 局部变量会悬垂,desc 在驱动
* 镜像内存里持久且每驱动唯一 */
} dkm_bus_service;
typedef struct dkm_bus_api {
u32 magic;
u32 version;
/* 注册:name 必须带命名空间前缀且与调用方身份匹配(BUS-E01)、
* 重名 BUS-E02、表满 BUS-E03——全部 panic,成功返回 0。 */
int (*register_service)(const char *name, u32 version, void *fn);
/* 注销:仅 provider 本人可注销;F_KERNEL/F_NO_UNLOAD 或不存在 → BUS-E05。 */
int (*unregister_service)(const char *name);
/* 必存在查找:未命中 → BUS-E04 panic,绝不返回 NULL 伪装成功。 */
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
/* 可选探测:未命中返回 -1;调用方必须显式处理缺席(白名单登记)。 */
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void); /* 诊断 / /dev/services */
const dkm_bus_service *(*at)(u32 index); /* 按 index 枚举 */
} dkm_bus_api;
typedef struct dkm_driver_desc {
u32 magic;
u16 abi_version;
@@ -62,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_exit_fn)(struct dkm_driver_handle *handle);
/* DRR 恢复根服务表(Phase8;驱动经 kernel_api->drr 使用) */
typedef struct drr_recovery_api {
void (*report_fault)(const char *reason);
u64 (*heartbeat_get)(void);
void *(*emergency_alloc)(u64 size, u64 alignment);
int (*ckpt_snapshot_dirty)(void);
int (*rollback_pages)(void);
} drr_recovery_api;
/* SAS-R0-PCQ 调度器服务表(Phase7;驱动经 kernel_api->sched 使用) */
typedef struct utsm_sched_api {
u64 (*uptime_ns)(void);
void (*yield)(void);
u32 (*current_task_slot)(void);
int (*task_create)(u32 process_slot, u32 priority, void (*entry)(void *), void *arg);
void (*sleep_ms)(u32 ms);
} utsm_sched_api;
typedef struct dkm_log_api {
void (*info)(const char *msg);
void (*warn)(const char *msg);
@@ -83,7 +172,7 @@ typedef struct dkm_kernel_api {
const void *vfs;
const dkm_net_api *net;
const void *timer;
const void *drr;
const drr_recovery_api *drr; /* 恢复根服务(原占位 void* 改具体类型,布局不变) */
const void *rsdp_address; /* ACPI RSDP physical address */
const void *fb_address; /* framebuffer base */
@@ -95,6 +184,31 @@ typedef struct dkm_kernel_api {
int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */
u64 hhdm_offset; /* physical → virtual */
const dkm_block_api *block; /* block provider registry */
const dkm_mmio_api *mmio; /* 页表/高位 MMIO 映射服务(尾部追加,ABI 兼容) */
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
/* B7 阶段2: 驱动向 UTSM 注册 LAPIC EOI 钩子(写 LAPIC EOI 寄存器的函数指针)。
* apic.drv 在 LAPIC 接管时注册;idt_handler 的 IRQ 分发末尾调用该钩子,
* 使 vector 0x20-0x2F 的 legacy 线在 IOAPIC 路由下可持续投递。
* 未注册(PIC 路由)时钩子为 NULL,idt_handler 跳过,行为与旧版一致。 */
void (*register_apic_eoi)(void (*eoi_fn)(void));
/* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),尾部追加保持 ABI。
* 池范围 0x40-0xDF;alloc 返回向量号、失败 -1;free 幂等越界忽略。
* 驱动拿到向量后经 irq_register(vector, handler) 直接注册(现有
* 接口已接受向量命名空间,无需新注册 ABI)。 */
int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector);
/* M4/Phase7-8: 调度器服务(尾部追加,ABI 兼容;drr 服务见上方原占位字段)。
* 驱动可用:task_create/uptime/sleep/current_task_slot。
* DSK 以 api+0xA8 硬偏移读 block 的约定不受影响(仅尾部追加)。 */
const utsm_sched_api *sched;
/* D3: 按名导出表(尾部追加,ABI 兼容)。驱动/用户态枚举内核服务并
* 按名取函数指针;固定偏移 struct 字段不受影响。 */
const dkm_kapi_table *kapi;
/* D6 万物互联:双向服务总线(尾部追加,ABI 兼容)。kern.* 与 srv.* 按名
* 注册-消费;driver_init = 注册服务的地方。 */
const dkm_bus_api *bus;
} dkm_kernel_api;
typedef enum dkm_driver_state {
@@ -120,12 +234,39 @@ typedef struct dkm_builtin_driver {
} dkm_builtin_driver;
void dkm_init(void);
/* D4: 设置当前驱动日志颜色上下文(driver_init 前设置,返回后清 0) */
void dkm_log_set_driver(const char *name);
int dkm_load_builtin(const dkm_builtin_driver *driver);
void dkm_scan_boot_modules(void);
void dsm_load_by_manifest(void);
/* P0(万物互联):按名加载。dsm_load_named 严格版(depends 校验强制,供
* kern.dkm.load_by_name 等常规路径);dsm_load_named_boot 顺序豁免版
* (唯一合法调用方 = kernel/main.c VMM 路径 pci+ahci 预加载,先于 apic,
* 否则 guest 陷入 IDTR=0 VM-exit 风暴——设计内豁免,见
* docs/RE/万物互联与服务总线设计.md §6)。
* 签名(ini_config 定义在 kernel/ini_parser.h,此处避免头依赖):
* void dsm_load_named(const ini_config *cfg, const char *name);
* void dsm_load_named_boot(const ini_config *cfg, const char *name);
* 调用方自行前向声明(同 kernel/main.c 既有模式)。 */
void dkm_fill_platform_info(void);
const dkm_kernel_api *dkm_get_kernel_api(void);
/* 服务表实现(drr_stub.c / sched/sched.c 提供,fill 时挂入 kernel_api) */
const struct drr_recovery_api *drr_get_recovery_api(void);
const struct utsm_sched_api *utsm_sched_get_api(void);
/* D6 万物互联:服务总线(dkm/bus.c 提供)。dkm_bus_init 播种 kern.*
* 并挂入 kernel_api(dkm_fill_platform_info 内调用,先于一切驱动加载);
* loader 在 driver_init 前后 set/clear provider 上下文(仿 D4 颜色上下文),
* kern.* 只能在 provider==NULL 时注册。 */
void dkm_bus_init(void);
const dkm_bus_api *dkm_bus_get_api(void);
void dkm_bus_set_current_provider(struct dkm_driver_handle *h);
/* loader 专用:token T 是否已有 "prefix" 或 "prefix.*" 服务注册
* (stage>=4 provides 校验用,DKM-E45)。命中返回 1。 */
int dkm_bus_prefix_registered(const char *prefix);
/* exposed for manifest parser */
struct dkm_symbol_scan {
const void *driver_desc;
+57
View File
@@ -102,9 +102,15 @@ typedef struct {
/* ---- 公共接口 ---- */
void drr_init(void);
/* R3b: FUCK [utsm] 配置接线(须在 drr_init 前调用;0 = 用编译期默认)。
* pool_kb 钳位到编译期静态池上限(Emergency Pool 为编译期数组,运行期只
* 能缩小可用量);recovery_log_size 钳位到池内剩余容量。 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size);
const u64 *drr_get_root_key(void);
u64 drr_recovery_generation(void);
void drr_report_fault(const char *reason);
/* F1: 崩溃归档(crash_buf 写入;返回 0=已归档,-1=DRR 未初始化) */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs);
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len);
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
@@ -124,4 +130,55 @@ u64 drr_heartbeat_get(void);
/* CRC64 */
u64 drr_crc64(const void *data, u64 len);
/* ===================================================================
* Phase 8 扩展:真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* (尾部追加,不动既有结构与语义)
* =================================================================== */
/* 快照区条目(快照区第 0 页:头(64B) + 条目数组顺序存放) */
typedef struct {
u32 seg_slot; /* utsm_get_segment 的 slot */
u32 page_index; /* 段内页号 */
u64 cipher_vaddr; /* 段内目标页虚拟地址(HHDM,回滚目标) */
u64 crc; /* 快照数据 CRC64(drr_crc64,区域完整性) */
u32 region_idx; /* 快照数据所在页序号(区域第 1+idx 页) */
u32 used;
/* Phase 9: 真实 MAC(keyed BLAKE2b,绑定 seg/page/epoch) */
u64 key_epoch; /* 快照时刻段 key_epoch(MAC 上下文 + 防跨纪元重放) */
u64 mac; /* 页 MAC(utsm_page_mac,截断 64 位) */
} drr_ckpt_page_entry;
/* 真实脏页快照:扫描全部 ACTIVE 段的 dirty shard 位图,逐脏页复制到
* 快照区(先快照后清位;区满则部分提交且不清位),随后走 A/B checkpoint
* 原子切换。O(dirty_shards + dirty_pages)。
* 返回快照页数(>=0);负数失败(未初始化/快照区不可用)。 */
int drr_ckpt_snapshot_dirty(void);
/* 页级回滚:按 active slot 元数据 + 快照区逐页 CRC 校验后恢复密文页
* 并清除对应脏位;触碰段状态 RECOVERING → ACTIVE,CRC 失败段 POISONED。
* 返回恢复页数(>=0);失败 -2(任一页 CRC 不符)。 */
int drr_rollback_pages(void);
/* 系统级回滚:crash 留痕 + 8042 复位(不返回)。
* Emergency Pool 耗尽 / 双槽皆坏 / 无任务可切换时调用。 */
void drr_system_rollback(const char *reason);
/* 统一 fault 入口:report_fault → 页级回滚 → sched fault 回调(可能不
* 返回);回调缺失且 reboot 使能时 → system rollback。 */
void drr_handle_task_fault(u32 task_slot, const char *reason);
/* 看门狗(独立于调度器:由 tick handler 直接调用 check,非调度任务) */
#define DRR_WATCHDOG_MAX 8
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks);
int drr_watchdog_unregister(u32 task_slot);
void drr_watchdog_kick(u32 task_slot);
void drr_watchdog_check(void);
/* 恢复行为控制:关闭后 fault 只留日志不真重启(selftest 用) */
void drr_set_reboot_enabled(int enabled);
/* 调度器 fault 回调(sched enable 时注入;缺失时 fault → system rollback)。
* DRR 独立性:对调度器只依赖这一个函数指针。 */
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot));
#endif /* UTSM_DRR_H */
+37
View File
@@ -9,6 +9,43 @@
#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0)
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
/* FUCK [panic] mode=fallback:DSK/desktop/login/FirstInit 的 *_fatal 记录后返回,
* 调用点走备用路径(见 utsm/panic.h)。未置位 = strict。 */
#define DSK_BOOT_FLAG_PANIC_FALLBACK (1ULL << 4)
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
/* ---- 网络租约信息(netman 写入,DSK 持有存储,经 reserved[7] 指针传递) ----
* reserved[0..6] 已被占用(user.conf/login/dev_mode/pe/lxc/probe),
* 仅 reserved[7] 空闲,故以其存放指向本结构的指针。 */
#define NET_LEASE_MAGIC 0x4E45544Cu /* "NETL" */
#define NET_LEASE_F_VALID (1u << 0) /* ip/gateway/dns 有效 */
#define NET_LEASE_F_STATIC (1u << 1) /* 静态配置(非 DHCP) */
typedef struct net_lease_info {
u32 magic; /* NET_LEASE_MAGIC */
u32 flags; /* NET_LEASE_F_* */
u32 ip; /* guest IP(网络序) */
u32 gateway; /* 网关(网络序) */
u32 dns; /* DNS(网络序) */
u32 server; /* DHCP server(网络序,调试) */
u32 reserved[2];
} net_lease_info;
typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC */
u32 version; /* ABI 版本,当前 1 */
u64 probe_buf_addr; /* probe_entry 缓冲基地址(物理/HHDM 地址) */
u32 probe_cap; /* 缓冲容量(条目数) */
u32 probe_head; /* 当前写入位置(递增取模) */
u32 probe_count; /* 已写入总条目数 */
u32 probe_dropped; /* 因缓冲满而覆盖的条目数 */
u32 instr_enabled; /* g_instr_enabled 当前值 */
u32 instr_probe_enable; /* g_instr_probe_enable 当前值 */
u64 reserved[4];
} probe_info;
typedef struct dsk_boot_context {
u64 magic;
+26 -2
View File
@@ -8,8 +8,9 @@
#define EPT_PAGE_SIZE 4096ULL
#define EPT_ENTRIES 512ULL
/* EPTP 构造:bits [5:0]=memory type, bits [7:6]=walk length-1, bits [51:12]=PML4 HPA */
#define EPTP_WALK_LEN_4 (3ULL << 6) /* 4 级页表 */
/* EPTP 构造:bits [2:0]=memory type, bits [5:3]=walk length-1, bit 6=AD, bits [51:12]=PML4 HPA
* P8.4 Fix: walk length was at bits [7:6] (wrong), should be bits [5:3] per Intel SDM. */
#define EPTP_WALK_LEN_4 (3ULL << 3) /* 4 级页表 (3 = 4-1) at bits [5:3] */
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
@@ -30,4 +31,27 @@ int ept_identity_map(u64 gpa, u64 size, u64 flags);
* Handles 4KB pages and 2MB large pages. */
u64 ept_gpa_to_hpa(u64 gpa);
/* Rewrite an existing EPT leaf (4K or 2MB) as RWX+WB+Ignore PAT.
* Returns 0 if a leaf was found and rewritten, -1 if unmapped.
* Used to recover from nested-KVM EPT misconfig (guest PAT vs EPT type). */
int ept_repair_leaf(u64 gpa, u64 flags);
/* Log PML4/PDPT/PD/PT entries for a GPA (misconfig diagnostics). */
void ept_log_walk(u64 gpa);
/* Map a 2MB large page in EPT (PD-level direct mapping).
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
* Required for real hardware MTRR compliance. */
int ept_map_2m_page(u64 gpa, u64 hpa, u64 flags, u64 memtype);
/* Check INVVPID/INVEPT support from IA32_VMX_EPT_VPID_CAP MSR.
* Call once after VMX init. */
void ept_check_vpid_support(void);
/* Flush EPT TLB entries (INVEPT). Required after EPT mapping changes on real hardware. */
void ept_flush_ept(void);
/* Flush VPID TLB entries (INVVPID). Required after VPID changes on real hardware. */
void ept_flush_vpid(u16 vpid);
#endif
+122
View File
@@ -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 */
+648
View File
@@ -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 */
+60 -3
View File
@@ -5,8 +5,65 @@
void idt_init(void);
/* IRQ handler: return 1 to suppress default ACK, 0 for normal EOI */
typedef int (*irq_handler_t)(u8 irq);
int irq_register(u8 irq, irq_handler_t handler);
/* 将 IDT 所有 256 个条目替换为安全 halt stub (cli; hlt)。
* 用于 UTSM→DSK 跳转前,防止 DSK 运行期间过期 UTSM handler
* 被 spurious IRQ / 异常触发导致三重故障重启。 */
void idt_halt_all(void);
/* DSK 诊断 IDT:vector 0-31 恢复原 isr_stub(异常走 idt_handler
* 打完整日志后停机),vector 32-255 保持 halt stub。
* BUG-20260801-005:UTSM→DSK 交接处以本函数替代 idt_halt_all,
* 恢复 DSK 阶段 CPU 异常可见性(原静默 halt 零诊断)。
* 不做 pic_remap / sti,DSK 上下文 IF=0 不变式保持不变。 */
void idt_install_dsk_diag(void);
/* PE 诊断 IDT:idt_install_dsk_diag 的别名(幂等重装)。
* 用于 PE 兼容层原生执行 PE32+ 前恢复异常可见性。 */
void idt_install_pe_diag(void);
/* IRQ handler: return 1 to suppress default ACK (EOI), 0 for normal EOI.
* 注册参数兼容两种命名空间(二者不重叠,自动判别):
* - ISA IRQ 号 0-15 -> 自动换算为 vector 0x20+IRQ (兼容 ps2kbd/e1000/mouseInit)
* - IDT vector 0x20+ -> 直接按 vector 注册 (apic 0xE0 / virtio_net 0x20+n 现状)
* vector 0-15 是 CPU 异常向量,idt_handler 从不作为 IRQ 分发目标,故无歧义。
* EOI 语义:
* - PIC IRQ (0x20-0x2F): 返回 0 -> 先调 APIC EOI 钩子(若已注册)再发 8259 EOI;
* 返回 1 -> 两者都抑制, handler 自负 EOI 全责。
* - APIC/IOAPIC/MSI (0x30-0xFF): 返回 0 -> 调 APIC EOI 钩子(若已注册);
* 返回 1 -> 抑制(如 handler 内已自行 LAPIC EOI)。未注册钩子时不做任何 EOI。 */
typedef int (*irq_handler_t)(u8 vector);
int irq_register(u8 vector, irq_handler_t handler);
/* B7 阶段2: 注册 LAPIC EOI 钩子(由 apic.drv 在 LAPIC 接管后调用)。
* 钩子语义: 向 LAPIC EOI 寄存器 (MMIO base+0xB0) 写 0,清除当前 in-service
* 最高优先级 ISR 位。重复注册以最后一次为准;传 NULL 可注销。 */
void idt_register_apic_eoi(void (*eoi_fn)(void));
/* B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用)。
* 池范围 0x40-0xDF (160 个), 位图 O(1); 分配返回向量号, 池耗尽返回 -1。
* free 幂等, 越界/静态向量输入忽略。主上下文调用, 无锁。 */
int irq_vector_alloc(void);
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
+181
View File
@@ -0,0 +1,181 @@
/* instr.h — Deshab 统一插桩宏系统
*
* 提供四级插桩原语:
* INSTR_LOG — 带级别和模块过滤的日志输出
* INSTR_STAT — 轻量级计数器 + 周期性摘要
* INSTR_PROBE — 环形探测缓冲写入(结构化事件记录)
* INSTR_TS — 时间戳标注(阶段耗时测量)
*
* 设计原则:
* - 禁用时零开销: g_instr_enabled=0 时所有宏展开为空操作
* - 热路径安全: STAT_INC 仅一条条件自增,PROBE 仅条件写入
* - 统一配置: 通过 FUCK [debug] 段控制开关与粒度
* - 标签编码: 4 字符 ASCII 编码为 u32,避免字符串比较
*
* 使用方式:
* 1. 在 kernel_main() 中调用 instr_init(&g_boot_cfg) 初始化
* 2. 各模块 #include <utsm/instr.h> 后使用 INSTR_* 宏
* 3. 在 FUCK 中设置 [debug] enabled=1 开启插桩
*/
#ifndef UTSM_INSTR_H
#define UTSM_INSTR_H
#include <utsm/types.h>
/* ---- 日志级别 ---- */
#define INSTR_LOG_TRACE 0
#define INSTR_LOG_DEBUG 1
#define INSTR_LOG_INFO 2
#define INSTR_LOG_WARN 3
#define INSTR_LOG_ERROR 4
#define INSTR_LOG_SILENT 5
/* ---- 全局配置变量 (instr.c 定义) ---- */
extern u32 g_instr_enabled; /* 全局插桩开关,FUCK debug.enabled */
extern u32 g_instr_log_level; /* 日志级别阈值,FUCK debug.log_level */
extern u32 g_instr_probe_enable; /* 探测缓冲开关,FUCK debug.probe_enable */
extern u32 g_instr_stat_interval; /* 统计摘要间隔,FUCK debug.stat_summary_interval */
extern u32 g_instr_module_flags; /* 模块级过滤位掩码 */
/* ---- 模块标志位 ---- */
#define INSTR_F_UTSM_TRACE (1u << 0)
#define INSTR_F_VMM_TRACE (1u << 1)
#define INSTR_F_DKM_TRACE (1u << 2)
#define INSTR_F_DSK_TRACE (1u << 3)
#define INSTR_F_BLOCK_TRACE (1u << 4)
#define INSTR_F_NET_TRACE (1u << 5)
#define INSTR_F_INPUT_TRACE (1u << 6)
/* ---- INSTR_LOG: 带级别和模块过滤的日志 ----
* level: INSTR_LOG_TRACE/DEBUG/INFO/WARN/ERROR
* mod_flag: INSTR_F_xxx 或 0(不过滤)
* msg: 常量字符串
*
* 仅当 g_instr_enabled && level >= g_instr_log_level &&
* (mod_flag==0 || mod_flag & g_instr_module_flags) 时输出。
*/
#define INSTR_LOG(level, mod_flag, msg) do { \
if (g_instr_enabled && (level) >= g_instr_log_level && \
((mod_flag) == 0 || (g_instr_module_flags & (mod_flag)))) { \
if ((level) <= INSTR_LOG_ERROR) log_error(msg); \
else if ((level) <= INSTR_LOG_WARN) log_warn(msg); \
else log_info(msg); \
} \
} while(0)
/* ---- INSTR_STAT: 轻量级计数器 (声明+增量+摘要) ----
* INSTR_STAT_DECL(name) — 在函数/文件作用域声明计数器
* INSTR_STAT_INC(name) — 原子性自增 (仅当 enabled)
* INSTR_STAT_SUMMARY(name) — 达到间隔时输出摘要并重置
*/
#define INSTR_STAT_DECL(name) \
static u64 name##_count = 0; \
static u64 name##_last_ts __attribute__((unused)) = 0
#define INSTR_STAT_INC(name) do { \
if (g_instr_enabled) { name##_count++; } \
} while(0)
#define INSTR_STAT_SUMMARY(name) do { \
if (g_instr_enabled && name##_count >= g_instr_stat_interval) { \
u64 _now = instr_rdtsc(); \
u64 _elapsed_ms = (_now - name##_last_ts) / (instr_tsc_per_ms ? instr_tsc_per_ms : 1); \
log_info("[STAT] " #name " count="); \
log_hex64("", name##_count); \
log_info("[STAT] " #name " interval_ms="); \
log_hex64("", _elapsed_ms); \
name##_count = 0; \
name##_last_ts = _now; \
} \
} while(0)
/* ---- INSTR_PROBE: 写入环形探测缓冲 ----
* 标签编码: 4 字符 ASCII,如 "BOOT", "VME0", "EPTV", "IDTI", "AHCI"
* a0-a3: 4 个 u64 参数,语义由标签定义
*
* 仅当 g_instr_enabled && g_instr_probe_enable 时写入。
*/
#define INSTR_PROBE(tag, a0, a1, a2, a3) do { \
if (g_instr_enabled && g_instr_probe_enable) \
probe_write(INSTR_TAG_##tag, (u64)(a0), (u64)(a1), (u64)(a2), (u64)(a3)); \
} while(0)
/* ---- INSTR_TS: 时间戳标注(阶段耗时测量) ----
* INSTR_TS_DECL(name) — 声明时间戳变量
* INSTR_TS_BEGIN(name) — 记录起始 TSC
* INSTR_TS_END(name, label) — 计算耗时并输出(仅当 enabled)
*/
#define INSTR_TS_DECL(name) static u64 name##_ts = 0
#define INSTR_TS_BEGIN(name) do { name##_ts = instr_rdtsc(); } while(0)
#define INSTR_TS_END(name, label) do { \
if (g_instr_enabled) { \
u64 _d = instr_rdtsc() - name##_ts; \
u64 _ms = _d / (instr_tsc_per_ms ? instr_tsc_per_ms : 1); \
log_info("[TS] " label " elapsed_ms="); \
log_hex64("", _ms); \
} \
} while(0)
/* ---- 标签编码常量 ----
* 4 字符 ASCII 编码为大端序 u32:
* 'B'=0x42, 'O'=0x4F, 'O'=0x4F, 'T'=0x54 → 0x424F4F54
*/
#define INSTR_TAG_BOOT 0x424F4F54u /* "BOOT" — 通用启动事件 */
#define INSTR_TAG_IDT0 0x49445430u /* "IDT0" — IDT/中断初始化 */
#define INSTR_TAG_ARE0 0x41524530u /* "ARE0" — Arena 初始化 */
#define INSTR_TAG_DMA0 0x444D4130u /* "DMA0" — DMA 初始化 */
#define INSTR_TAG_NET0 0x4E455430u /* "NET0" — 网络注册 */
#define INSTR_TAG_DRR0 0x44525230u /* "DRR0" — DRR 初始化 */
#define INSTR_TAG_UTS0 0x55545330u /* "UTS0" — UTSM 核心初始化 */
#define INSTR_TAG_DKM0 0x444B4D30u /* "DKM0" — DKM 加载 */
#define INSTR_TAG_DSM0 0x44534D30u /* "DSM0" — 驱动 manifest 加载 */
#define INSTR_TAG_DSK0 0x44534B30u /* "DSK0" — DSK 加载跳转 */
#define INSTR_TAG_SELF 0x53454C46u /* "SELF" — 自检 */
#define INSTR_TAG_VMM0 0x564D4D30u /* "VMM0" — VMM 初始化 */
#define INSTR_TAG_LIN0 0x4C494E30u /* "LIN0" — Linux loader */
#define INSTR_TAG_VME0 0x564D4530u /* "VME0" — VM-Exit 事件 */
#define INSTR_TAG_EPTV 0x45505456u /* "EPTV" — EPT violation */
#define INSTR_TAG_EPTM 0x4550544Du /* "EPTM" — EPT misconfig */
#define INSTR_TAG_AHCI 0x41484349u /* "AHCI" — AHCI 事件 */
#define INSTR_TAG_E1K0 0x45314B30u /* "E1K0" — e1000 事件 */
#define INSTR_TAG_FAT0 0x46415430u /* "FAT0" — FAT32 事件 */
#define INSTR_TAG_NVM0 0x4E564D30u /* "NVM0" — NVMe 事件 */
#define INSTR_TAG_IRQ0 0x49525130u /* "IRQ0" — IRQ 注册/触发 */
#define INSTR_TAG_PIT0 0x50495430u /* "PIT0" — PIT 模拟 */
#define INSTR_TAG_SRL0 0x53524C30u /* "SRL0" — 串口事件 */
#define INSTR_TAG_MMIO 0x4D4D494Fu /* "MMIO" — MMIO 访问 */
#define INSTR_TAG_HCALL 0x4843414Cu /* "HCAL" — Hypercall */
#define INSTR_TAG_EXCP 0x45584350u /* "EXCP" — CPU 异常 */
/* ---- 探测条目结构(与 probe.c 共享) ---- */
typedef struct probe_entry {
u32 tag; /* 4字符ASCII标签 */
u32 tick; /* 低32位TSC(~2s溢出@2GHz,足够诊断) */
u64 args[4]; /* 4个u64参数 */
} probe_entry;
/* ---- 辅助函数声明 (instr.c / probe.c 定义) ---- */
u64 instr_rdtsc(void);
extern u64 instr_tsc_per_ms;
void instr_init(const void *ini_config);
void probe_init(const void *ini_config);
void probe_write(u32 tag, u64 a0, u64 a1, u64 a2, u64 a3);
int probe_read(u32 n, probe_entry *out);
u32 probe_count(void);
u32 probe_capacity(void);
probe_entry *probe_buf_address(void);
u32 probe_head_value(void);
u32 probe_dropped_count(void);
/* 需要前向声明 log 函数 (避免循环 include) */
#ifndef UTSM_LOG_H
void log_info(const char *msg);
void log_warn(const char *msg);
void log_error(const char *msg);
void log_hex64(const char *prefix, u64 value);
#endif
#endif /* UTSM_INSTR_H */
+135
View File
@@ -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 */
+14
View File
@@ -43,6 +43,20 @@ u64 ipc_shm_get_gpa(void);
/* Get the size of the shared memory region. */
u64 ipc_shm_get_size(void);
/* ===== Payload pool access (bulk data channel) =====
*
* The payload_pool area of the SHM region carries bulk file-transfer data
* (Phase 3 FILE_LIST/FILE_READ). Linux daemon writes into it (via
* UTSM_IOCTL_WRITE_POOL) before sending UTSM_MSG_FILE_RESPONSE; UTSM reads
* it directly through its own HHDM mapping. */
/* Host virtual pointer into payload_pool at byte offset.
* Returns NULL if not initialized or offset out of range. */
void *ipc_shm_payload_ptr(u32 offset);
/* Total capacity of the payload pool in bytes. */
u32 ipc_shm_payload_capacity(void);
/* ===== Message API (UTSM-side) ===== */
/* Send a message from UTSM to Linux (enqueues into utsm_to_linux ring).
+38
View File
@@ -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 */
+143
View File
@@ -21,6 +21,34 @@
#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] 传递 */
typedef struct linux_compat_service {
u64 magic; /* LINUX_COMPAT_MAGIC */
@@ -42,10 +70,108 @@ typedef struct linux_compat_service {
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
u64 *exit_code);
/* 列出 Linux 目录内容(Phase 3 文件传输)。
* path : Linux 绝对路径(如 "/usr/bin")
* buf/cap : 接收列表文本的缓冲区(每行一项,目录名带 '/' 后缀)
* out_len : 实际写入字节数(可为 NULL)
* 返回 0 成功,负值失败(-1=服务不可用, -2=IPC 发送失败,
* -3=linux_resume 失败, -4=无响应, -5=Linux 侧错误,
* -6=缓冲区不足被截断,此时 out_len 仍给出截断长度)。 */
int (*file_list)(const char *path, char *buf, u64 cap, u64 *out_len);
/* 读取 Linux 文件内容(Phase 3 文件传输,支持大文件分块)。
* path : Linux 绝对路径(如 "/etc/hostname")
* offset : 文件内起始偏移(首次读传 0)
* buf/cap : 接收文件数据的缓冲区
* out_len : 本次实际读取字节数(可为 NULL)
* out_total : 文件总大小(可为 NULL)
* 返回 0 成功(含 EOF:out_len=0 且 offset>=total),负值失败
* (错误码同 file_list)。 */
int (*file_read)(const char *path, u64 offset, char *buf, u64 cap,
u64 *out_len, u64 *out_total);
/* 查询 Linux guest 状态字符串(调试用),写入 buf,返回字符串长度。 */
int (*status)(char *buf, u64 cap);
/* 写入 Linux 文件(Phase 3 文件传输,支持大文件分块)。
* 调用方以递增 offset 循环调用即可上传任意大小文件。
* path : Linux 绝对路径(不存在则以 0644 创建;
* offset==0 时先截断,即 push 语义为整文件替换)
* offset : 文件内写入偏移(首次写传 0)
* buf/len : 待写数据(len 会被钳制到 payload pool 容量,
* 实际写入量以 out_written 为准;len=0 仅创建/截断)
* out_written : 本次实际写入字节数(可为 NULL)
* 返回 0 成功,负值失败(错误码同 file_list)。 */
int (*file_write)(const char *path, u64 offset, const char *buf, u64 len,
u64 *out_written);
/* 跨内核文件拉取:Linux 文件 → UTSM FAT32 系统盘根目录(≤256KB,整文件)。
* linux_path : Linux 绝对路径
* fat32_name : UTSM FAT32 根目录文件名(如 "HOSTNAME.TXT",8.3 可转换)
* out_size : 实际传输字节数(可为 NULL)
* 返回 0 成功,负值失败(-7=文件超过 256KB 暂存上限, -9=无块设备,
* -10=非法 FAT32 名, -12=FAT32 写失败,其余同 file_list)。 */
int (*file_pull)(const char *linux_path, const char *fat32_name,
u64 *out_size);
/* 跨内核文件推送:UTSM FAT32 系统盘根目录 → Linux 文件(≤256KB,整文件替换)。
* fat32_name : UTSM FAT32 根目录文件名(8.3 可转换)
* linux_path : Linux 绝对路径
* out_size : 实际传输字节数(可为 NULL)
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
int (*file_push)(const char *fat32_name, const char *linux_path,
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;
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
*
* UTSM FAT32 系统盘根目录文件访问(经 block_get_api() + fat32_io)。
* name 为普通文件名(内部做 8.3 转换)。
* 返回 0 成功;负值:-9 无块设备, -10 非法文件名, -11 文件不存在,
* -12 磁盘 I/O 错误。 */
int lxc_f32_read_file(const char *name, u8 **out_data, u32 *out_size);
int lxc_f32_write_file(const char *name, const u8 *data, u32 size);
/* 获取全局 Linux 兼容层服务实例指针(UTSM 内部使用,
* 外部通过 boot context reserved[5] 获取)。 */
const linux_compat_service *linux_compat_get_service(void);
@@ -53,4 +179,21 @@ const linux_compat_service *linux_compat_get_service(void);
/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */
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 */
+105 -20
View File
@@ -49,6 +49,9 @@ struct linux_setup_header {
/* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */
#define BOOT_PARAMS_SIZE 4096
#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 */
#define BZ_BOOT_FLAG_OFF 0x1fe
@@ -88,11 +91,10 @@ struct linux_setup_header {
#define E820_TYPE_UNUSABLE 5
#define E820_TYPE_PMEM 7
/* e820_table entry in boot_params at offset 0x2e0 (actually 0x2d0 in older,
* 0x2e0 in newer). We use 0x2e0 and the e820_entries count at 0x1e8.
* Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
/* e820_table is at 0x2d0 in Linux 6.6 boot_params (arch/x86/include/uapi/asm/bootparam.h).
* e820_entries count is at 0x1e8. Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
#define E820_ENTRIES_OFFSET 0x1e8
#define E820_TABLE_OFFSET 0x2e0
#define E820_TABLE_OFFSET 0x2d0
#define E820_MAX_ENTRIES 128
#define E820_ENTRY_SIZE 20
@@ -103,7 +105,9 @@ struct linux_setup_header {
* 0x01000000-0x02FFFFFF : Linux kernel image
* 0x03000000-0x03FFFFFF : Linux initrd
* 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_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_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_SIZE 0x5B000000ULL /* 1.4GB (0x05000000..0x5FFFFFFF) */
/* ===== virtio-mmio device GPA layout =====
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
@@ -129,15 +144,33 @@ struct linux_setup_header {
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
* 由 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_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_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
#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_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 设备区(含两端) */
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 2 * 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)
/* ===== Linux loader API ===== */
@@ -155,6 +188,13 @@ struct linux_guest_info {
u64 pgt_gpa; /* GPA of guest page tables */
u64 gdt_gpa; /* GPA of guest GDT */
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.
@@ -162,10 +202,28 @@ struct linux_guest_info {
* Sets *size_out to the module size. */
void *linux_find_bzimage_module(u64 *size_out);
/* Parse bzImage header and extract key parameters.
/* Find the Arch rootfs image boot module (path contains "rootfs").
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
void *linux_find_rootfs_module(u64 *size_out);
/* 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. */
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);
/* Initialize the Linux guest: parse bzImage, allocate guest memory,
@@ -176,24 +234,51 @@ int linux_loader_init(void);
/* Get the loaded Linux guest info (NULL if not loaded). */
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.
* Must be called after vmm_init() + linux_loader_init().
* Returns 0 on successful guest termination, negative on error. */
int linux_launch(void);
/* Default Linux command line (can be overridden).
* nohlt: prevent kernel idle loop from using HLT — only the exec daemon's
* explicit ioctl(PARK) HLT should trigger VM-Exit park, so that
* linux_launch() returns only after the daemon is ready.
* Daemon PARK is a VMCALL. Guest idle may HLT; that stays in the
* guest (wait-for-IRQ). Do not use nohlt/idle=poll — poll idle
* NULL-derefs current after the first real sleep.
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
* 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 \
"console=ttyS0,115200 earlyprintk=serial nokaslr " \
"no_timer_check loglevel=7 nohlt idle=poll " \
"noapic nolapic nosmp " \
"earlyprintk=serial,ttyS0,115200 console=ttyS0,115200 " \
"nokaslr debug loglevel=8 noinvpcid nopcid nopti pti=off nogbpages " \
"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@0xF4001000:6"
"virtio_mmio.device=4K@0xF4001000:6 " \
"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
+21
View File
@@ -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
View File
@@ -13,4 +13,17 @@ void log_error(const char *msg);
void log_hex64(const char *prefix, u64 value);
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
+17
View File
@@ -0,0 +1,17 @@
#ifndef UTSM_MM_H
#define UTSM_MM_H
#include <utsm/types.h>
/* MMIO 映射接口:
* mm_map_mmio 在当前页表(Limine 提供的 CR3)的 MMIO 专用窗口中
* 建立 phys → virt 的 4K 页映射,页属性带 PCD|PWT(等效 UC),
* 适用于任意物理地址(含 4G 以上的 PCI BAR)。
* 返回的虚拟地址包含 phys 的页内偏移;失败返回 0。 */
void *mm_map_mmio(u64 phys, u64 size);
/* 解除 mm_map_mmio 建立的映射(按页清除 PTE + invlpg)。
* 窗口虚拟地址与中间页表页不回收,保留复用。 */
void mm_unmap_mmio(void *virt, u64 size);
#endif
+27
View File
@@ -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_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 {
const char *name;
u8 mac[6];
@@ -50,6 +56,9 @@ typedef struct dkm_net_device_desc {
dkm_net_scan_count_fn scan_count;
dkm_net_scan_result_fn scan_result;
dkm_net_is_wireless_fn is_wireless;
/* 关联扩展(尾部追加,ABI 兼容)。 */
dkm_net_connect_fn connect;
dkm_net_disconnect_fn disconnect;
} dkm_net_device_desc;
typedef struct dkm_net_device_info {
@@ -58,6 +67,16 @@ typedef struct dkm_net_device_info {
u32 flags;
} dkm_net_device_info;
/* 网络设备统计(真机诊断用,驱动层累计)。 */
typedef struct dkm_net_stats {
u64 tx_ok; /* TX 成功帧数 */
u64 tx_err; /* TX 失败帧数 */
u64 rx_ok; /* RX 成功帧数 */
u64 rx_err; /* RX 错误帧数 */
u64 rx_overflow; /* RX buffer 溢出丢包 */
u64 link_changes; /* 链路状态变化次数 */
} dkm_net_stats;
typedef struct dkm_net_api {
int (*register_device)(const dkm_net_device_desc *desc);
u32 (*device_count)(void);
@@ -69,9 +88,17 @@ typedef struct dkm_net_api {
int (*scan_count)(u32 index);
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
int (*is_wireless)(u32 index);
/* 无线关联(dispatch 到 wlan 驱动回调;尾部追加,ABI 兼容)。 */
int (*connect)(u32 index, const char *ssid, const char *password);
int (*disconnect)(u32 index);
/* 网络统计查询(返回 0 成功,-1 索引越界)。 */
int (*device_stats)(u32 index, dkm_net_stats *out);
} dkm_net_api;
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);
#endif

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