56 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
616 changed files with 171962 additions and 8738 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
+28 -7
View File
@@ -1,13 +1,34 @@
ISO/deshab.img
*.o *.o
*.obj
*.elf
*.drv
*.img
*.iso
*.vhd
*.raw
*.ppm
target/
*.rlib
*.rmeta
*.pdb
*.dbf
*.rgba
IDEA.md
SYSTEM/boot/linux-extra-rootfs.img
*.d
target/
# ---- 本地工作区 ----
.build_tmp/ .build_tmp/
.trae/ .trae/
docs/workbuddy-planning/ docs/workbuddy-planning/
sata_fat32_dsk.img
# Rust build artifacts # IDE 配置(JetBrains 自动生成的 .idea/.gitignore 只覆盖了部分文件)
CODE/Deaicup Software/src/target/ .idea/*.iml
CODE/Deaicup Software/pe/target/ .idea/inspectionProfiles/
# Large binary images (regenerate from build) # 本地运行日志(如 run_qemu.bat > deshab_log.log 的串口输出)
SYSTEM/boot/linux-rootfs.img *.log
deshab_log.log
+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
+149 -20
View File
@@ -85,8 +85,21 @@ SYSTEM/
SYSTEM/driver/manifest.json SYSTEM/driver/manifest.json
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。 内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
SYSTEM/STARTMENU.CNF
M3 开始菜单注册表(FAT32 根目录,desktop 启动时解析)。
格式 NAME|显示名|path|args;path 支持 builtin:<name> / program/xxx.exe(GUI PE,
g++ 编译,run_windowed 嵌入桌面)/ bin/xxx.elf(ELF 工具,全屏加载)/
/guest/path(Linux guest 应用)。显示名可 UTF-8 中文;文件缺失时回退全部已注册应用。
纯 CLI exe(bin/pciscan.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行,
输出走串口。源码在 CODE/tools/pe-tools/(build.ps1 用 mingw-w64 g++ 编译),
ELF 工具源码在 CODE/tools/diskinfo/(→ SYSTEM/bin/diskinfo.elf)。
SYSTEM/program/ + SYSTEM/bin/ + SYSTEM/lib/ + PEAPPS.CNF + LINUXAPP.CNF + STARTMENU.CNF
桌面 PE/工具链全部经 DSK FAT32 只读 ESP——pack_system_image.sh 会把这几项
额外拷到 FAT32 ESP(p1)。漏拷则开始菜单/PE 应用在 GPT 镜像里静默失效。
SYSTEM/system/deshab64/FUCK SYSTEM/system/deshab64/FUCK
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为。 内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为、panic 策略([panic] mode=strict|fallback)。
UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。 UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。
DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。 DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。
缺失或为空时使用硬编码默认值,行为与无配置文件时一致。 缺失或为空时使用硬编码默认值,行为与无配置文件时一致。
@@ -100,14 +113,18 @@ CODE/DKM/README.md
CODE/UTSM/README.md CODE/UTSM/README.md
UTSM 模块设计说明。 UTSM 模块设计说明。
RE/UTSM_算法记录.md docs/RE/UTSM_算法记录.md
UTSM 核心算法记录。 UTSM 核心算法记录。
RE/UTSM_设计架构.md docs/RE/UTSM_设计架构.md
UTSM 架构记录。 UTSM 架构记录。
RE/驱动模块ABI设计.md docs/RE/驱动模块ABI设计.md
DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。 DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机、boot module 方案和服务总线 ABI(§8.5)。
docs/RE/万物互联与服务总线设计.md
万物互联架构定稿:dkm_bus 按名服务总线(kern.*/srv.*)、dsk_boot_context 逐槽迁移表、
DSK 溶解地图、P0-P6 落地路线与错误码登记(BUS-E/DKM-E44..47/SESSION-E)。
ISO/deshab.img ISO/deshab.img
build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。 build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。
@@ -121,18 +138,40 @@ ISO/run_qemu.bat
已知说明: 已知说明:
```text ```text
SYSTEM/ 是打包为 IMG 后的系统根目录。 SYSTEM/ 是唯一打包输入:build.ps1 只扫描 SYSTEM/,不从 /home 或其它路径取发行物。
所有要进镜像的软件(utsm.elf、deshab.elf、.drv、bzImage/initrd、
linux-rootfs.img、linux-extra-rootfs.img、Python/KDE 内容)必须先落到 SYSTEM/。
打包成 GPT 双分区:p1 FAT32 ESP(SYSTEM/EFI + SYSTEM/limine + SYSTEM/boot
+ SYSTEM/driver + SYSTEM/system — Limine 10 不能读 ext4,boot(): 模块必须在 ESP),
p2 ext4(其余 SYSTEM/,并保留 driver/system 副本)。由 WSL
CODE/linux/pack_system_image.sh 执行 mkfs.vfat + mkfs.ext4。
ext4 UUID 固定为 64657368-6162-4000-8000-000000000002。
内核/驱动/DSK 一律走 boot(): (FAT32 ESP)。
FAT32 单文件上限 4GiB-1 仍适用于 ESP 上的 /boot 文件,linux-extra-rootfs.img 必须小于 4GiB。
SYSTEM/boot/ 存放启动模块,例如 utsm.elf。 SYSTEM/boot/ 存放启动模块,例如 utsm.elf。
SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。 SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。
CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf
build.bat: 调用 build.ps1。 build.bat: 调用 build.ps1,透传参数(如 -Variant dev)。
build.ps1: 编译 UTSM,生成 SYSTEM/boot/utsm.elf,并把 SYSTEM 打包为 ISO/deshab.img。deshab.img 是 GPT 磁盘镜像,包含 FAT32 EFI System Partition。 build.ps1: 编译 UTSM/DSK/工具/驱动,把整个 SYSTEM/ 打成 GPT(FAT32 ESP + ext4) 镜像:
ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab.img,串口输出到 stdio。 - ISO/deshab-dev.img 开发者模式(dev_mode=1,自动跑 shell.elf 测试,调试全开,QEMU 验证选项,panic=strict)
- ISO/deshab-release.img 发布版(dev_mode=0,进桌面+登录,调试零开销,真机安全选项,panic=fallback 即关闭 panic)
- ISO/deshab-realtest.img 真机测试版(release 同款真机选项 + 登录路径,panic=strict 即启用 panic)
参数 -Variant dev|release|realtest|both|all(默认 all = 三者;both = dev+release)。both/all 构建后恢复 SYSTEM 为 dev 配置。
配置模板在 build/configs/{dev,release,realtest}/(FUCK + firstInit.txt),打包前注入到 SYSTEM。
realtest/FUCK 由 release/FUCK 复制而来,仅头注释与 [panic] mode 不同;改 release 真机选项须同步 realtest。
ISO/run_qemu.bat [dev|release|realtest]: 用 QEMU + UEFI 固件启动对应镜像(默认 dev),串口输出到 stdio。
ISO/run_qemu_kvm.sh: 默认 12G 内存(Limine 会把 SYSTEM/boot 里的 rootfs 模块装进内存)。
build.ps1 自动把 Git usr/bin 加入 PATH(sh.exe + mkdir),解决 make `mkdir -p` 在 cmd.exe 下失败的问题。
``` ```
当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT + FAT32 ESP 镜像。早期启动要求:进入 C 前 `cli/cld`,early serial 不允许无限等待硬件 ready 位,未启用 FPU/SSE 前编译必须使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。DATA/BSS 段使用 PF_R|PF_W|PF_X(SAS-R0 下驱动模块内存需可执行)。 当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT(FAT32 引导分区 + ext4 数据分区)镜像。早期启动要求:进入 C 前 `cli/cld`,early serial 不允许无限等待硬件 ready 位,未启用 FPU/SSE 前编译必须使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。DATA/BSS 段使用 PF_R|PF_W|PF_X(SAS-R0 下驱动模块内存需可执行)。
DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32/PLT32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
> **relocation 集合注意**:`R_X86_64_PLT32(4)` 语义等同 PC32(静态可重定位下的 `S+A-P`)。多编译单元驱动(如 `ath9k.drv = ath9k.o + wpa2_crypto.o`)跨 TU 的全局函数调用会被 lld 生成为 PLT32;loader 不支持即启动 `DKM-E37`。新增多源 `.drv` 前用 `llvm-readobj --relocations` 核对类型是否在上述集合内。
> **ath9k 硬件要求(实机 WiFi 试验)**:原生驱动仅支持 **Atheros AR928x 系列 PCIe 无线网卡(vendor `168c`,如 AR9285/9280/9287)**。FUCK `[drivers] ath9k=1` 时才加载。若机器无匹配 Atheros 卡、BAR0 位于 4G 以上、或复位/初始化失败,`driver_init` 返回非零 → `DKM-E39 EXTERNAL DRIVER INIT FAILED` **开机即 panic**(零降级,不豁免);串口会打印本机所有无线控制器 (class 02:80) 的 vendor:device 供确认卡型号。Intel/Realtek/MediaTek 等其他厂商网卡**不支持**。`wlan.drv` 为框架骨架,在有无线控制器的机器上只注册设备,其 scan/connect 调用即 `NETWL-E10/E11` panic。
DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部 14 个外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。
当前已实现 14 个真实 DKM 驱动: 当前已实现 14 个真实 DKM 驱动:
- **timer** (stage0): PIT 校准, 100ms busy-wait demo - **timer** (stage0): PIT 校准, 100ms busy-wait demo
@@ -156,15 +195,14 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp
当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。 当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。
下一阶段优先路线: 下一阶段优先路线(活跃路线 = **万物互联 P0-P6**(D6/D7,设计定稿 `docs/RE/万物互联与服务总线设计.md`)+ `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`):
1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。 0. **万物互联(D6/D7,当前最高优先)**: P0 manifest 依赖图强制 → P1 dkm_bus 双向服务总线(kern.*/srv.*)→ P2 fat32/ext4.drv 归位 → P3 加载器 FS 化 + stage4 + ps2mouse.drv → P4 六 app 模块 DKM 化(netman/shell/firstInit/login/desktop/cmd)→ P5 DSK 退役 + session.drv → P6 常驻调度 + 真 IRQ 输入。每 phase QEMU 验证后提交,进度见 STATUS.md 万物互联表。
2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。 1. **一期 F 系列收尾**: F2 统一控制台后端(fbcon.h 抽象已落,panic 已用)。(F1 莲花 panic / F3 跨平台 Makefile / F4 boot 分级自检 / F5 skip_firstinit 已完成)
3. **IRQ 后端升级**: ~~APIC EOI/IOAPIC redirection~~(B7 已完成:UTSM EOI 钩子 + legacy IRQ1/12/11 持续投递,QEMU 全链路验证;tick 2x 定论 = PIT mode3 IOAPIC edge 双触发 ~200Hz)。剩余:vector allocator 规范化、设备 IRQ 从 PIC 默认全面迁移、MSI/MSI-X(e1000/NVMe)。 2. **二期 U 系列**: ✅ 已完成(U1 UUID 表 / U2 Slow Path 真分支 / U3 @sealed 可选标记+linker .sealed section / U4 密文 dump / U5 RFC 8439 KAT)。chacha20 KAT 三方对拍通过(pycryptodome+官方向量+C),"间歇 FAIL" 确认为从未合并的幽灵缺陷。
4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。 3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表升级为服务总线(并入万物互联 P1)。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本)
5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。 4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核(多核在 P6 常驻调度之后排队)。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS)
6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。 5. **五期 E 系列**: 启动莲花动画、假想时光键彩蛋、提交信息规范、风味 ISO。
7. **FirstInit 完善**: ~~修复按键崩溃(fb_text 优化问题)~~(B6 已验证未复现)、~~实现鼠标光标移动~~(M1 已完成)、~~user.conf 写盘~~(已完成)、网络配置界面。 6. **工程债(Phase 存档遗留)**: vector allocator/MSI-X、NVMe 高位 BAR、virtio-net virtqueue、DKM 零 warning。
8. ~~**鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动~~(M1 已完成)。
## M1 里程碑完成状态(2026-07-29) ## M1 里程碑完成状态(2026-07-29)
@@ -216,3 +254,94 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp
- 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则 - 严格遵守"任何单步失败只记日志继续执行,绝不阻塞或崩溃首次启动"准则
- handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行 - handler 存活性结论:镜像会被后续模块覆盖,IRQ12 保持屏蔽避免野指针执行
- 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志 - 后续集成路径:DKM 输入驱动 或 DSK 加载器增加"常驻"标志
## M4 里程碑完成状态(2026-09-15):SAS-R0-PCQ 调度器 + DRR 恢复根
### 任务 A: SAS-R0-PCQ 调度器(ROADMAP Phase 7)
**状态**: [COMPLETED]
**实现内容**:
1. **O(1) 位图调度器** (`CODE/UTSM/sched/sched.c`):
- 64 槽 TCB 表 + 8 CPU runqueue(当前 BSP-only),ready_bitmap ctz 选最高优先级(0 最高)
- 侵入式 O(1) FIFO runqueue(slot 索引非指针),冻结 ABI 9 API 真实现
- 任务栈从 arena 分配,entry/arg/stack_base 存 task_aux
2. **xv6 式栈驱动切换** (`CODE/UTSM/sched/switch.S`):
- 抢占 = IRQ 帧落在被抢占任务自己的栈上,切换只换 rsp
- trampoline 跳 entry;schedule/yield/sleep/exit 全路径
- 切换路径只触碰 crypto_context 指针(禁止扫描 capability/MAC/checkpoint)
3. **LAPIC timer tick** (`CODE/UTSM/arch/x86_64/lapic_timer.c`):
- MMIO 映射 + TSC 延时校准(10ms 逆计数法)+ 周期模式
- tick_hz/timeslice_ms 由 FUCK [sched] 配置(默认 100Hz/10ms)
- demo 窗口含 tick 失速兜底:心跳无进展自动中止窗口,不卡死 bootstrap
4. **FUCK 配置**: `[sched] enable/tick_hz/timeslice_ms/demo_ms/demo_fault`
**验证结果**:
- selftest: runqueue O(1) PASS、block/wake PASS
- demo 窗口: 3 任务(prio 1/20/40)按优先级交错运行并正常退出
- 窗口结束 quiesce → timer stopped → 正常交接 DSK
### 任务 B: DRR 恢复根(ROADMAP Phase 8)
**状态**: [COMPLETED-MAC占位]
**实现内容** (`CODE/UTSM/drr/drr_stub.c`):
1. **快照区**: 静态区域(128 页容量,编译期),页条目含 seg_slot/page_index/crc
2. **snapshot_dirty**: 遍历 ACTIVE 段 dirty_shard 位图收集脏页 → 拷贝+CRC64 → 清位图
3. **A/B 双槽 checkpoint**: ckpt_a/ckpt_b + CRC + active slot 切换
4. **rollback**: 页级回滚 + CRC verify;segment/system 级接口就位
5. **看门狗**: register/kick/timeout 检测,任务退出自动注销;异常钩子接入 fault 路径
6. **FUCK 配置**: `[drr] watchdog_enabled`;`[utsm] drr_emergency_pool_kb/drr_recovery_log_size`
**验证结果(含负向测试 demo_fault=1)**:
- ckpt snapshot ok(1 page)→ rollback done → rollback verify ok
- 看门狗负向测试: fault 任务(slot 3)死循环 → `watchdog timeout task_slot=0x3` 检出 → 回滚执行 → demo 窗口正常结束
- 证据: `.build_tmp/qemu_serial_sched_m4.log`
**遗留**:
- MAC 为 CRC64 占位(Phase 9 换真实加密)
- task_kill crypto erase(key_epoch++)、Per-CPU 真启用、驱动 recovery ops 待做
---
## 插桩铁律(STUB 规约 · 零降级强制执行)
> "你若为了'能编译/能启动'而隐藏未实现,视为违反 Deshab 哲学。
> 本项目的成功标准是 **panic 坐标清晰**,不是 **QEMU 不崩**。"
### 1. 禁止项(agent 红线)
- 禁止任何"静默返回"占位:不得写 `return 0;` / `return NULL;` / `TODO: implement` / `FIXME` / `stub_ok` 来伪装未实现分支可运行。
- 所有"设计已定但未实现"的函数/分支 **MUST** 调用 panic 原语(见 §2),禁止返回错误码装没事。
- 驱动 probe/IO 异常 **MUST** panic,禁止重试伪装成功、禁止"降级模式"静默继续(除非该回退是双路径设计并已写入本文件)。
- 禁止"先跑通再说"的临时补丁;宁可启动即 panic,不可带病前进。
- 不得声称"已全量插桩",除非 §4 的 grep 自检输出为 CLEAN。
### 2. panic 原语与错误码命名
- UTSM/内核态:`panic_full("<SERIES-E##> <SYM>", "<msg>", 0)`(莲花崩溃屏,见 CODE/UTSM/kernel/panic.c)。
- DKM 驱动态:`g_log->panic("<DRV>-E## <SYM>")`(经 kernel_api.panic 槽,禁止直连内核符号)。
- DSK:`dsk_fatal("<SERIES-E##> <SYM>")`;FirstInit/login:各自 `*_fatal()`。
- 错误码系列:`FS-E`(文件系统/DSK 调度)、`ELF-E`、`HCALL-E`、`DRR-E`、`SCH-E`、`DKM-E`、`PE-E`、`LNX-E`、`VNET/ATH/XHC/E1K/NVME-E`(驱动)、`PKG-E`、`BRAND-E`、`NET-E`(FirstInit)、`LOGIN-E`。新模块按 `<MODULE>-E##` 注册到 docs/STATUS.md。
### 3. 改动节奏(防"一次性插完"幻觉)
- 每次只处理 **一个文件** 的 STUB→panic,改完输出:改动行号 + 剩余 STUB 计数。
- 骨架允许"不接线"(无人调用的 skeleton 函数),但接线后行为必须是 panic。
- 例外白名单(设计内双路径回退,允许非 panic):APIC 自验失败回 PIC、块设备缺失回 boot module、KDE 回落 native、VMX/bzImage/kernel.krl 未配置跳过。新增白名单项必须改本文件并说明理由。
- **panic 双模式(FUCK `[panic] mode=strict|fallback`,默认 strict)**:strict = 本规约原样执行。fallback = 全局白名单开关,用于真机/演示"带病看全貌":
- UTSM `panic_full(sym,msg,NULL)` 只打 `[PANIC:FALLBACK]` + 错误码 + 命中计数后返回,调用点走紧随其后的备用路径(跳过该能力/驱动/段/页面);DKM `kernel_api.panic` / `kern.log.panic` 同样返回驱动,驱动 init 返回非零。
- 模式经 `DSK_BOOT_FLAG_PANIC_FALLBACK (1<<4)` 下传,DSK/FirstInit/login/desktop 的 `*_fatal` 同样只留串口后返回;DSK ext4 内部 fatal 经 `__builtin_setjmp/longjmp` 中止本次操作 → 只用 FAT32 ESP。
- **永远停机(`panic_halt`,两种模式都不返回)**:CPU 异常(带 regs)、LA57 已启用(VMM-E11,4 级页表遍历会写坏内存)、DSK 跳转失败(VMM-E15)、panic 测试钩子(UTSM-E42)。
- 新增 panic 点必须保证 panic 调用后存在安全的继续路径(return 错误码 / continue / 置能力未就绪),否则改用 `panic_halt` 并登记到上一条。
- 启动期能力证明(attest,CODE/UTSM/kernel/attest.c):FUCK `[attest]` `require_<name>=1`(默认)而能力未就绪 → 交接 DSK 前 panic(莲花屏,错误码=该能力登记码)。`require_<name>=0` 豁免行是设计内白名单,仅限两类:① 该构建环境不可能具备(QEMU 无 wlan/嵌套 VMX);② STATUS.md ⬜/🔶 未落地项(实现后逐项删除豁免行)。当前豁免清单见 build/configs/*/FUCK [attest]。
### 4. 自检命令(验收只认这个输出,不认自然语言保证)
```
powershell -Command "Get-ChildItem CODE\UTSM,CODE\DKM,CODE\dsk -Recurse -Include *.c,*.h |
Select-String -Pattern 'TODO|FIXME|stub_ok|\bstub\b|未实现|暂不支持|待实现|not implemented' |
Group-Object Path | Sort-Object Count -Descending"
```
- 命中行 **只允许** 两类:① panic 调用/其注释(含错误码字面量);② 本文件白名单内的设计内回退日志。
- 其余命中 = 未清干净,禁止提交。基线快照(2026-10-03):~118 命中,绝大多数已是 panic 坐标注释;新增代码不得抬高裸命中数。
### 5. 验收格式(每次插桩提交必须附)
```
[STUB] <阶段>/<模块>/<文件>::<函数> | 改动行=<L1-L2> | 剩余STUB=<N> | “<一句疯洁标语>” | 已提交=<hash>
```
- 例:`[STUB] P1/UTSM/vmm/hypercall.c::hcall_utrw_read | 改动行=176-181 | 剩余STUB=112 | “封缄之下,未成之物当自爆”`
+14 -1
View File
@@ -108,6 +108,8 @@ static u32 g_ready_port = 0xffffffffu;
static ahci_cmd_header *g_cmd_header; static ahci_cmd_header *g_cmd_header;
static struct dkm_dma_buffer g_cmd_table; static struct dkm_dma_buffer g_cmd_table;
static struct dkm_dma_buffer g_data; static struct dkm_dma_buffer g_data;
/* IDENTIFY 读出的用户可寻址扇区数;LBA48 优先,回退 LBA28 */
static u64 g_sector_count;
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg); return dkm_pci_read(bus, dev, func, reg);
@@ -534,6 +536,17 @@ static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) {
log_hex("[ahci] IDENTIFY word0=", id[0]); log_hex("[ahci] IDENTIFY word0=", id[0]);
log_hex("[ahci] IDENTIFY word49=", id[49]); log_hex("[ahci] IDENTIFY word49=", id[49]);
log_hex("[ahci] IDENTIFY word83=", id[83]); log_hex("[ahci] IDENTIFY word83=", id[83]);
/* 容量必须在 ahci_read_lba0 复用 data 缓冲区前提取,
* 否则 IDENTIFY 数据被 LBA0 内容覆盖后容量丢失。 */
if ((id[83] & 0x0400u) && (id[103] | id[102] | id[101] | id[100])) {
/* LBA48: word100-103 小端 u16 序列拼接 */
g_sector_count = (u64)id[100] | ((u64)id[101] << 16)
| ((u64)id[102] << 32) | ((u64)id[103] << 48);
} else {
/* LBA28: word60-61 */
g_sector_count = (u64)id[60] | ((u64)id[61] << 16);
}
log_hex("[ahci] IDENTIFY sector_count=", g_sector_count);
ahci_read_lba0(port, hdr, &table, &data); ahci_read_lba0(port, hdr, &table, &data);
g_ready_port = port; g_ready_port = port;
g_cmd_header = hdr; g_cmd_header = hdr;
@@ -678,7 +691,7 @@ int driver_init(const struct dkm_kernel_api *api,
struct dkm_block_device_desc desc; struct dkm_block_device_desc desc;
desc.name = "ahci0"; desc.name = "ahci0";
desc.sector_size = 512; desc.sector_size = 512;
desc.sector_count = 0; desc.sector_count = g_sector_count;
desc.ctx = 0; desc.ctx = 0;
desc.read = ahci_block_read; desc.read = ahci_block_read;
desc.write = ahci_block_write; desc.write = ahci_block_write;
+2 -1
View File
@@ -6,7 +6,8 @@
* 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F * 2. IOAPIC 重定向表初始化: 逐条目编程, 将 ISA IRQ 映射到 vector 0x20-0x2F
* (兼容 PIC 布局), 逐条目 mask * (兼容 PIC 布局), 逐条目 mask
* 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射 * 3. MADT ISO (Interrupt Source Override) 处理: 按 ACPI 规范修正 IRQ→GSI 映射
* 4. SIPI 多核启动框架 (预留, 未实现) * 4. SIPI 多核启动框架 (预留: 调用 dkm_sipi_wake_ap 即 DKM-E03 panic,
* 见 CODE/UTSM/dkm/kernel_api.c 骨架)
* *
* B7 路由接管 (apic_route): * B7 路由接管 (apic_route):
* FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由 * FUCK [drivers] apic_route=1 时, 本驱动在初始化末尾尝试把中断路由
+607 -10
View File
@@ -18,6 +18,7 @@
*/ */
#include "../dkm_shared.h" #include "../dkm_shared.h"
#include "wpa2_crypto.h"
static const char *const g_depends[] = {"pci","irq"}; static const char *const g_depends[] = {"pci","irq"};
static const char *const g_provides[] = {"netdev"}; static const char *const g_provides[] = {"netdev"};
@@ -173,6 +174,12 @@ static struct ath9k_scan_result g_scan_results[ATH9K_MAX_SCAN_RESULTS];
static u32 g_scan_count; static u32 g_scan_count;
static int g_scanning; static int g_scanning;
/* 前向声明:接收路径需要把管理/控制帧交给连接状态机 */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
static void ath9k_handle_eapol(const u8 *eap, u32 len);
static int eapol_find(const u8 *frame, u32 len, u32 *out_off);
static void copy_mac(u8 dst[6], const u8 src[6]);
/* ---- 串口日志(参考 e1000 raw_log) ---- */ /* ---- 串口日志(参考 e1000 raw_log) ---- */
#define COM1 0x3F8 #define COM1 0x3F8
@@ -256,6 +263,23 @@ static int ath9k_find(u8 *bus, u8 *dev) {
raw_log(hex); raw_log("\n"); raw_log(hex); raw_log("\n");
return 0; return 0;
} }
/* 无匹配 Atheros 设备:打印本机所有无线控制器 (class 02:80) 的
* vendor:device,便于确认实机卡型号(ATH 硬件要求见 CLAUDE.md)。 */
{
u32 found = 0;
for (u8 d = 0; d < 32; d++) {
u32 vd = pci_read(0, d, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u32 cls = pci_read(0, d, 0, 0x08);
if (((cls >> 24) & 0xff) != 0x02 || (((cls >> 16) & 0xff) != 0x80)) continue;
raw_hex("[ath9k.raw] wireless ctrl (non-Atheros) dev=", (u64)d);
raw_hex("[ath9k.raw] vendor:device=", ((u64)(vd & 0xffff) << 16) | (u64)(vd >> 16));
found++;
}
raw_hex("[ath9k.raw] wireless controllers found=", (u64)found);
raw_log("[ath9k.raw] NO SUPPORTED ATHEROS DEVICE (need AR928x PCIe, vendor 168c)\n");
}
return -1; return -1;
} }
@@ -478,6 +502,20 @@ static int find_bssid(const u8 bssid[6]) {
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */ /* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) { static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) {
if (len < 24) return;
u16 fctl = (u16)buf[0] | ((u16)buf[1] << 8);
u16 ftype = fctl & 0x000C;
/* 连接状态机:管理帧(认证/关联响应)与数据帧中的 EAPOL-Key */
if (ftype == 0x0000) {
if (ath9k_handle_mgmt(buf, len) == 0) return;
} else if (ftype == 0x0008) { /* data */
u32 eoff = 0;
if (eapol_find(buf, len, &eoff) == 0)
ath9k_handle_eapol(buf + eoff, len - eoff);
/* 数据帧继续走 beacon 解析(多数会被拒绝) */
}
struct ath9k_scan_result tmp; struct ath9k_scan_result tmp;
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0; for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
if (parse_beacon(buf, len, &tmp) != 0) return; if (parse_beacon(buf, len, &tmp) != 0) return;
@@ -531,8 +569,11 @@ static void ath9k_rx_drain(void) {
* 真实硬件验证后增量完善。 * 真实硬件验证后增量完善。
*/ */
static void ath9k_set_channel(u8 channel) { static void ath9k_set_channel(u8 channel) {
(void)channel; /* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel)。
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */ * 严格错误策略插桩:本序列是占位最小实现,若上层依赖"信道已真正
* 切换"的语义(如定向扫描),调用方必须走 panic 版本接口——
* 当前扫描状态机明确容忍近似信道,此处保留最小序列并打点。 */
raw_hex("[ath9k] set_channel (minimal sequence), ch=", channel);
/* 触发 RFBUS 请求,等待 grant */ /* 触发 RFBUS 请求,等待 grant */
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1); ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
dkm_delay_us(5); dkm_delay_us(5);
@@ -601,12 +642,550 @@ static int ath9k_is_wireless_impl(void *ctx) {
return 1; return 1;
} }
/* ath9k 当前不支持 TX(仅扫描) */ /* ============================================================
static int ath9k_tx_stub(void *ctx, const void *packet, u32 length) { * TX 路径 + WPA2-PSK 连接状态机
(void)ctx; (void)packet; (void)length; *
* 说明:TX 描述符采用 AR9002(AR9280/9285)队列描述符布局的
* 最小可用子集;位域与 HAL initvals 需在实机 bring-up 时用
* 串口日志对拍(日志点见 ath9k_tx_frame)。协议层(认证/关联/
* 4-way/密钥安装)由 wpa2_crypto 提供并通过 wpa2_kat 自检。
* ============================================================ */
/* TX 队列(BE)寄存器 */
#define AR_TXDP(_q) (0x0000u + 0x0800u * (_q)) /* TXQ 描述符基址 */
#define AR_TXE 0x0800u
#define AR_TXCFG 0x0008u
#define AR_TXCFG_TXE 0x00000004u
#define ATH9K_TX_DESC_COUNT 8u
#define ATH9K_TX_BUF_SIZE 2048u
/* AR9002 TX 队列描述符(32 字节,最小字段集) */
struct ath9k_tx_desc {
u32 ds_link; /* 下一描述符物理地址 */
u32 ds_data; /* 数据缓冲物理地址 */
u32 ds_ctl0; /* FRAME_LEN(0:12) | TXQ(12:16) */
u32 ds_ctl1; /* BUF_LEN(0:12) | MORE(12) */
u32 ds_ctl2; /* RTS/CTS + 速率索引(0 = 默认 1M CCK) */
u32 ds_ctl3; /* 分片/重试(0) */
u32 ds_ctl4;
u32 ds_ctl5;
} __attribute__((packed));
#define ATH9K_TXCTL0_LEN_S 0
#define ATH9K_TXCTL0_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_LEN_S 0
#define ATH9K_TXCTL1_LEN_M 0x00000FFFu
#define ATH9K_TXCTL1_MORE 0x00001000u
/* ---- 连接状态 ---- */
#define WPA_STATE_IDLE 0
#define WPA_STATE_AUTH_SENT 1
#define WPA_STATE_ASSOC_SENT 2
#define WPA_STATE_M1_WAIT 3
#define WPA_STATE_M3_WAIT 4
#define WPA_STATE_CONNECTED 5
static struct dkm_dma_buffer g_tx_desc_dma;
static struct ath9k_tx_desc *g_tx_desc;
static struct dkm_dma_buffer g_tx_buf_dma;
static u8 *g_tx_buf;
static u32 g_tx_head;
static int g_tx_ready;
static const struct dkm_dma_api *g_dma;
/* WPA2 会话 */
static u8 g_sta_mac[6]; /* SPA */
static u8 g_ap_mac[6]; /* AA / BSSID */
static u8 g_channel;
static char g_target_ssid[ATH9K_SSID_MAX + 1];
static int g_wpa_state;
static u8 g_anonce[32];
static u8 g_snonce[32];
static u8 g_pmk[WPA2_PMK_LEN];
static u8 g_ptk[64]; /* KCK(16) KEK(16) TK(16) + 余量 */
static u64 g_ptk_replay;
static u64 g_tx_pn; /* CCMP 发包序号 */
static u16 g_seq;
static int g_key_installed;
#define ATH9K_STA_MAC_ID0 0x0800u
/* ---- 802.11 管理帧构造 ---- */
#define IEEE80211_STYPE_AUTH 0x00B0u
#define IEEE80211_STYPE_ASSOC_REQ 0x0000u
#define IEEE80211_STYPE_ASSOC_RESP 0x0010u
#define IEEE80211_STYPE_REASSOC_REQ 0x0020u
#define IEEE80211_STYPE_DEAUTH 0x00C0u
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_ERP 42
#define WLAN_EID_RSN 48
static u32 put_le16(u8 *p, u16 v) { p[0]=(u8)v; p[1]=(u8)(v>>8); return 2; }
/* 帧控制字(管理帧,非保护) */
static void mgmt_fc(u8 *f, u16 subtype) {
u16 fc = 0x0000u | subtype; /* proto 0, type=mgmt(0) */
f[0] = (u8)(fc & 0xFF);
f[1] = (u8)(fc >> 8);
f[2] = 0; f[3] = 0; /* duration */
}
/* 填 24 字节管理帧头:DA / SA / BSSID / seq */
static void mgmt_hdr(u8 *f, const u8 *da, const u8 *sa, const u8 *bssid) {
for (int i = 0; i < 6; i++) f[4+i] = da[i];
for (int i = 0; i < 6; i++) f[10+i] = sa[i];
for (int i = 0; i < 6; i++) f[16+i] = bssid[i];
u16 sc = (u16)((++g_seq & 0xFFF) << 4);
f[22] = (u8)(sc & 0xFF); f[23] = (u8)(sc >> 8);
}
/* WPA2-PSK RSN IE(AKM 00-0F-AC:2,CCMP) */
static u32 rsn_ie_psk(u8 *p) {
u8 *q = p;
q[0] = WLAN_EID_RSN;
q[1] = 20; /* 长度 */
q += 2;
q += put_le16(q, 1); /* version */
/* group cipher: OUI 00-0F-AC + type 4 (CCMP) */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* pairwise count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
q += put_le16(q, 1); /* AKM count */
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x02; q += 4; /* PSK */
q += put_le16(q, 0); /* RSN caps */
return (u32)(q - p);
}
/* 发送一帧(构造 802.11 头 → TX 环 → 触发) */
static int ath9k_tx_frame(const u8 *frame, u32 len);
/* ---- 认证请求(open system) ---- */
static int ath9k_send_auth(void) {
u8 f[30 + 4];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_AUTH);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
/* auth body: alg(2)=0, seq(2)=1, status(2)=0 */
put_le16(f + 24, 0);
put_le16(f + 26, 1);
put_le16(f + 28, 0);
raw_log("[ath9k.raw] tx auth request\n");
return ath9k_tx_frame(f, 30);
}
/* ---- 关联请求(含 RSN IE) ---- */
static int ath9k_send_assoc(void) {
u8 f[24 + 4 + 2 + 34 + 4 + 24 + 2];
u32 n = 0;
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
mgmt_fc(f, IEEE80211_STYPE_ASSOC_REQ);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
n = 24;
n += put_le16(f + n, 0x0431); /* capability: ESS + privacy + short preamble */
n += put_le16(f + n, 10); /* listen interval */
/* SSID IE */
{
u32 sl = 0;
while (g_target_ssid[sl]) sl++;
f[n++] = WLAN_EID_SSID;
f[n++] = (u8)sl;
for (u32 i = 0; i < sl; i++) f[n++] = (u8)g_target_ssid[i];
}
/* supported rates: 1,2,5.5,11,6,9,12,18 Mbps */
f[n++] = WLAN_EID_SUPP_RATES;
f[n++] = 8;
f[n++] = 0x82; f[n++] = 0x84; f[n++] = 0x8B; f[n++] = 0x96;
f[n++] = 0x0C; f[n++] = 0x12; f[n++] = 0x18; f[n++] = 0x24;
/* RSN IE */
n += rsn_ie_psk(f + n);
raw_log("[ath9k.raw] tx assoc request\n");
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key 构造(802.1X 4-way) ---- */
#define EAPOL_VERSION 2
#define EAPOL_TYPE_KEY 3
#define WPA_KEY_DESC_TYPE 2
#define WPA_KEY_INFO_KEY_TYPE 0x0008u /* 1 = pairwise */
#define WPA_KEY_INFO_INSTALL 0x0040u
#define WPA_KEY_INFO_ACK 0x0080u
#define WPA_KEY_INFO_MIC 0x0100u
#define WPA_KEY_INFO_SECURE 0x0200u
#define WPA_KEY_INFO_ENC_KEY 0x1000u
#define WPA_KEY_INFO_VER2 0x0002u /* WPA2 = 3 (HMAC-SHA1 + AES) */
/* 构造 802.11 数据帧(toDS)+ LLC/SNAP + EAPOL-Key,做 MIC 后发送。
* key_info 由调用方给,nonce 指针可为 0(M4 用)。 */
static int ath9k_send_eapol(u16 key_info, const u8 *nonce,
const u8 *key_data, u32 kd_len) {
static u8 f[256];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* 802.11 data 头:toDS(AP 为 RA=A1,SA=A2,DA=A3=BSSID) */
u16 fc = 0x0100u | 0x0008u; /* data + ToDS */
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
u32 n = 24;
/* LLC/SNAP:AA AA 03 00 00 00 88 8E (EAPOL) */
static const u8 snap[8] = {0xAA,0xAA,0x03,0x00,0x00,0x00,0x88,0x8E};
for (int i = 0; i < 8; i++) f[n++] = snap[i];
/* EAPOL 头 */
u32 eapol_off = n;
f[n++] = EAPOL_VERSION;
f[n++] = EAPOL_TYPE_KEY;
u32 len_off = n; n += 2; /* 长度稍后回填 */
/* EAPOL-Key 体 */
u32 mic_off;
f[n++] = WPA_KEY_DESC_TYPE;
n += put_le16(f + n, key_info);
n += put_le16(f + n, 16); /* key length (CCMP) */
for (int i = 0; i < 8; i++) f[n++] = (u8)(g_ptk_replay >> (i * 8));
for (int i = 0; i < 32; i++) f[n++] = nonce ? nonce[i] : 0; /* nonce */
n += 16; /* key IV(全 0) */
n += 8; /* key RSC */
n += 8; /* key ID(全 0) */
mic_off = n; n += 16; /* key MIC(待填) */
n += put_le16(f + n, (u16)kd_len);
for (u32 i = 0; i < kd_len; i++) f[n++] = key_data[i];
/* 回填 EAPOL 长度(含 4 字节 EAPOL 头) */
u16 elen = (u16)(n - eapol_off - 4);
f[len_off] = (u8)(elen & 0xFF); f[len_off + 1] = (u8)(elen >> 8);
/* MIC:若置位,对 EAPOL 帧(MIC 字段已清零)用 KCK 计算 HMAC-SHA1-128 */
if (key_info & WPA_KEY_INFO_MIC) {
u8 mic[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, f + eapol_off, n - eapol_off, mic);
for (int i = 0; i < 16; i++) f[mic_off + i] = mic[i];
}
return ath9k_tx_frame(f, n);
}
/* ---- EAPOL-Key M2(SNonce + MIC,4-way 第 2 条) ---- */
static int ath9k_send_m2(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
u8 kd[2 + 20]; /* RSN IE(WPA2 常见,可省) */
u32 kdn = rsn_ie_psk(kd);
raw_log("[ath9k.raw] tx EAPOL M2\n");
return ath9k_send_eapol(ki, g_snonce, kd, kdn);
}
/* ---- EAPOL-Key M4(MIC + secure,4-way 第 4 条) ---- */
static int ath9k_send_m4(void) {
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE |
WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
raw_log("[ath9k.raw] tx EAPOL M4\n");
return ath9k_send_eapol(ki, 0, 0, 0);
}
/* ---- PTK 派生(802.11i H.4) ---- */
static void ath9k_derive_ptk(void) {
u8 seed[64];
const u8 *aa = g_ap_mac, *spa = g_sta_mac;
int swap = 0;
for (int i = 0; i < 6; i++) {
if (aa[i] != spa[i]) { swap = aa[i] > spa[i]; break; }
}
const u8 *m1 = swap ? spa : aa; /* min(AA,SPA) */
const u8 *m2 = swap ? aa : spa; /* max(AA,SPA) */
for (int i = 0; i < 6; i++) { seed[i] = m1[i]; seed[6+i] = m2[i]; }
int nswap = 0;
for (int i = 0; i < 32; i++) {
if (g_anonce[i] != g_snonce[i]) { nswap = g_anonce[i] > g_snonce[i]; break; }
}
const u8 *n1 = nswap ? g_snonce : g_anonce;
const u8 *n2 = nswap ? g_anonce : g_snonce;
for (int i = 0; i < 32; i++) { seed[12+i] = n1[i]; seed[44+i] = n2[i]; }
wpa2_prf512(g_pmk, seed, 64, g_ptk);
}
/* ---- 扫描结果查找(按 SSID 找 BSSID/信道) ---- */
static int ath9k_find_ssid(const char *ssid, u8 *bssid, u8 *channel, u8 *sec) {
for (u32 i = 0; i < g_scan_count; i++) {
const char *a = g_scan_results[i].ssid;
const char *b = ssid;
while (*a && *b && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
for (int j = 0; j < 6; j++) bssid[j] = g_scan_results[i].bssid[j];
*channel = g_scan_results[i].channel;
*sec = g_scan_results[i].security;
return 0;
}
}
return -1; return -1;
} }
/* ---- 随机 SNonce(TSC 混合,实机 TLS 级随机由上层补充) ---- */
static void ath9k_make_snonce(void) {
u64 t = dkm_rdtsc();
for (int i = 0; i < 32; i++) {
t = t * 6364136223846793005ULL + 1442695040888963407ULL;
g_snonce[i] = (u8)(t >> ((i % 8) * 8));
}
}
/* ---- 连接入口 ---- */
static int ath9k_connect_impl(void *ctx, const char *ssid, const char *password) {
(void)ctx;
if (!ssid) return -1;
raw_log("[ath9k.raw] connect request\n");
if (!g_tx_ready) { raw_log("[ath9k.raw] TX not ready\n"); return -2; }
u8 sec = 0;
if (ath9k_find_ssid(ssid, g_ap_mac, &g_channel, &sec) != 0) {
raw_log("[ath9k.raw] connect: SSID not in scan results\n");
return -3; /* 调用方先扫描 */
}
u32 sl = 0;
while (ssid[sl] && sl < ATH9K_SSID_MAX) { g_target_ssid[sl] = ssid[sl]; sl++; }
g_target_ssid[sl] = 0;
copy_mac(g_sta_mac, g_mac);
/* 需要 WPA2-PSK 时先算 PMK */
if (sec != DKM_NET_SEC_OPEN) {
if (!password || !password[0]) { raw_log("[ath9k.raw] connect: empty PSK\n"); return -4; }
wpa2_pbkdf2_sha1(password, (const u8 *)g_target_ssid, sl, g_pmk);
raw_log("[ath9k.raw] PMK derived\n");
}
ath9k_set_channel(g_channel);
g_key_installed = 0;
g_ptk_replay = 0;
g_tx_pn = 0;
g_wpa_state = WPA_STATE_AUTH_SENT;
if (ath9k_send_auth() != 0) { g_wpa_state = WPA_STATE_IDLE; return -5; }
return 0;
}
static int ath9k_disconnect_impl(void *ctx) {
(void)ctx;
raw_log("[ath9k.raw] disconnect\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
/* ---- 管理/控制帧处理(连接状态机驱动) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
/* ath9k TX:把帧写进 TX 环并触发 DMA。
* 描述符位域按 AR9002 队列描述符最小集构造;实机对拍日志见下方 raw_hex。
* 上层把数据帧交给本驱动时,若尚未安装密钥(未连接)则 panic(ATH-E04)。 */
static int ath9k_tx_frame(const u8 *frame, u32 len) {
if (!g_tx_ready || !g_tx_desc) return -1;
if (len > ATH9K_TX_BUF_SIZE) return -1;
u32 idx = g_tx_head;
u8 *buf = g_tx_buf + (u64)idx * ATH9K_TX_BUF_SIZE;
for (u32 i = 0; i < len; i++) buf[i] = frame[i];
struct ath9k_tx_desc *d = &g_tx_desc[idx];
d->ds_data = (u32)(g_tx_buf_dma.phys + (u64)idx * ATH9K_TX_BUF_SIZE);
d->ds_ctl0 = (len & ATH9K_TXCTL0_LEN_M); /* FRAME_LEN,TXQ=0(BE) */
d->ds_ctl1 = (len & ATH9K_TXCTL1_LEN_M); /* BUF_LEN,无 MORE */
d->ds_ctl2 = 0;
d->ds_ctl3 = 0;
__asm__ volatile("" ::: "memory");
/* 触发 TX:写 TXDP 指向队首描述符并置 TXE */
ath9k_reg_write(AR_TXDP(0), (u32)g_tx_desc_dma.phys);
ath9k_reg_write(AR_TXCFG, AR_TXCFG_TXE);
g_tx_head = (g_tx_head + 1) % ATH9K_TX_DESC_COUNT;
raw_hex("[ath9k] tx kick len=", len);
return 0;
}
/* ---- 数据帧 TX(已连接后由 net 栈调用;CCMP 加密) ---- */
static int ath9k_tx_impl(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_key_installed) {
/* 未安装密钥时不允许数据帧上路:零降级 panic(ATH-E04) */
g_log->panic("ATH-E04 DATA TX BEFORE KEY INSTALL");
return -1;
}
static u8 f[ATH9K_TX_BUF_SIZE];
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
/* toDS 数据帧头:A1=BSSID, A2=STA, A3=DA(未解析,用 BSSID 占位) */
u16 fc = 0x0100u | 0x0008u;
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
int rc = wpa2_ccmp_encrypt(f, sizeof(f), (const u8 *)packet, length,
g_ptk + WPA2_KCK_LEN + WPA2_KEK_LEN, ++g_tx_pn);
if (rc <= 0) return -1;
return ath9k_tx_frame(f, (u32)rc);
}
/* ---- 4-way 消息处理 ---- */
/* 定位 802.11 数据帧中的 EAPOL-Key(返回 EAPOL 起始偏移,-1 未找到) */
static int eapol_find(const u8 *frame, u32 len, u32 *out_off) {
/* 24B MAC 头 + 8B LLC/SNAP,可能带 8B CCMP 头(加密 EAPOL 场景) */
u32 off = 24;
if (len < off + 8) return -1;
if (frame[off] == 0xAA && frame[off+1] == 0xAA) {
/* 明文 EAPOL */
} else if (len >= off + 16) {
off += 8; /* 跳过 CCMP 头 */
if (frame[off] != 0xAA) return -1;
} else {
return -1;
}
if (off + 8 + 4 > len) return -1;
if (frame[off+6] != 0x88 || frame[off+7] != 0x8E) return -1; /* ethertype EAPOL */
if (frame[off+8] != EAPOL_VERSION || frame[off+9] != EAPOL_TYPE_KEY) return -1;
*out_off = off + 8;
return 0;
}
static void ath9k_handle_eapol(const u8 *eap, u32 len) {
/* eap 指向 EAPOL 头(version,type,length,然后 Key 体) */
if (len < 4 + 1 + 2 + 2 + 8 + 32 + 16 + 8 + 8 + 16 + 2) return;
const u8 *k = eap + 4; /* Key descriptor 起始 */
u16 key_info = (u16)k[1] | ((u16)k[2] << 8);
const u8 *nonce = k + 13; /* key nonce 偏移:1(desc)+2(info)+2(len)+8(replay) */
const u8 *mic = k + 13 + 32 + 16 + 8 + 8;
u16 kd_len = (u16)k[13 + 32 + 16 + 8 + 8 + 16] | ((u16)k[13+32+16+8+8+16+1] << 8);
const u8 *kd = mic + 16 + 2;
int pairwise = (key_info & WPA_KEY_INFO_KEY_TYPE) != 0;
int ack = (key_info & WPA_KEY_INFO_ACK) != 0;
int has_mic = (key_info & WPA_KEY_INFO_MIC) != 0;
if (!pairwise) {
/* GTK 消息(M3 附带),此处只做 MIC 校验后记录,实际 GTK 由 M3 的 key data 携带 */
raw_log("[ath9k.raw] EAPOL GTK msg\n");
return;
}
if (ack && !has_mic) {
/* ---- M1:带 ANonce ---- */
raw_log("[ath9k.raw] EAPOL M1 recv\n");
for (int i = 0; i < 32; i++) g_anonce[i] = nonce[i];
/* replay counter 8 字节(小端) */
g_ptk_replay = 0;
for (int i = 0; i < 8; i++) g_ptk_replay |= (u64)k[5 + i] << (i * 8);
ath9k_make_snonce();
ath9k_derive_ptk();
raw_log("[ath9k.raw] PTK derived\n");
g_wpa_state = WPA_STATE_M3_WAIT;
ath9k_send_m2();
return;
}
if (has_mic && !ack) {
/* ---- M3:校验 MIC → 安装 PTK/GTK → 回 M4 ---- */
raw_log("[ath9k.raw] EAPOL M3 recv\n");
u64 rc = 0;
for (int i = 0; i < 8; i++) rc |= (u64)k[5 + i] << (i * 8);
g_ptk_replay = rc;
/* MIC 校验:MIC 字段清零后对整段 EAPOL 计算 HMAC-SHA1-128 再比对。
* 用本地副本避免改写 RX 缓冲(const 语义)。 */
u32 mic_rel = (u32)(mic - eap);
u8 tmp[256];
if (len > sizeof(tmp)) { raw_log("[ath9k.raw] EAPOL too long\n"); return; }
for (u32 i = 0; i < len; i++) tmp[i] = eap[i];
for (int i = 0; i < 16; i++) tmp[mic_rel + i] = 0;
u8 calc[20];
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, tmp, len, calc);
u8 diff = 0;
for (int i = 0; i < 16; i++) diff |= calc[i] ^ mic[i];
if (diff) {
raw_log("[ath9k.raw] M3 MIC MISMATCH (bad PSK?)\n");
g_wpa_state = WPA_STATE_IDLE;
return;
}
raw_log("[ath9k.raw] M3 MIC ok\n");
/* key data 中可能含 GTK(KDE 0xDD + OUI 00-0F-AC + type 1,AES key wrap)。
* 若含 ENC_KEY,用 KEK unwrap 取 GTK。 */
if ((key_info & WPA_KEY_INFO_ENC_KEY) && kd_len > 0) {
/* 遍历 KDE */
u32 p = 0;
while (p + 2 <= kd_len) {
u8 ktype = kd[p];
u8 klen = kd[p+1];
if (p + 2 + klen > kd_len) break;
if (ktype == 0xDD && klen >= 6 &&
kd[p+2] == 0x00 && kd[p+3] == 0x0F && kd[p+4] == 0xAC && kd[p+5] == 0x01) {
const u8 *wrapped = kd + p + 6;
u32 wlen = (u32)klen - 6;
u8 gtk[32];
if (wpa2_key_unwrap(g_ptk + WPA2_KCK_LEN, wrapped, wlen, gtk) == 0) {
raw_log("[ath9k.raw] GTK unwrapped\n");
} else {
raw_log("[ath9k.raw] GTK unwrap FAILED\n");
}
}
p += 2 + klen;
}
}
g_key_installed = 1;
g_wpa_state = WPA_STATE_CONNECTED;
ath9k_send_m4();
raw_log("[ath9k.raw] === CONNECTED (WPA2-PSK) ===\n");
}
}
/* ---- 管理帧处理(认证/关联响应) ---- */
static int ath9k_handle_mgmt(const u8 *frame, u32 len) {
if (len < 24) return -1;
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
if ((fctl & 0x000C) != 0x0000) return -1; /* 非管理帧 */
u16 subtype = fctl & 0x00F0;
/* 只处理来自目标 AP 的帧 */
for (int i = 0; i < 6; i++) if (frame[10+i] != g_ap_mac[i]) return -1;
if (subtype == 0x00B0) { /* Authentication */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+4] | ((u16)frame[24+5] << 8);
if (status != 0) { raw_log("[ath9k.raw] auth FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] auth ok\n");
if (g_wpa_state == WPA_STATE_AUTH_SENT) {
g_wpa_state = WPA_STATE_ASSOC_SENT;
ath9k_send_assoc();
}
return 0;
}
if (subtype == 0x0010) { /* Association Response */
if (len < 24 + 6) return -1;
u16 status = (u16)frame[24+2] | ((u16)frame[24+3] << 8);
if (status != 0) { raw_log("[ath9k.raw] assoc FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
raw_log("[ath9k.raw] assoc ok, waiting 4-way\n");
g_wpa_state = WPA_STATE_M1_WAIT;
return 0;
}
if (subtype == 0x00C0) { /* Deauth */
raw_log("[ath9k.raw] deauth from AP\n");
g_wpa_state = WPA_STATE_IDLE;
g_key_installed = 0;
return 0;
}
return -1;
}
/* ---- TX 环初始化 ---- */
static int ath9k_tx_init(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
g_dma = dma;
/* 描述符环 1 页 + 缓冲 1 页 */
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_desc_dma) != 0) {
raw_log("[ath9k.raw] tx_init: desc alloc failed\n");
return -2;
}
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_buf_dma) != 0) {
raw_log("[ath9k.raw] tx_init: buf alloc failed\n");
return -3;
}
g_tx_desc = (struct ath9k_tx_desc *)g_tx_desc_dma.virt;
g_tx_buf = (u8 *)g_tx_buf_dma.virt;
for (u32 i = 0; i < sizeof(struct ath9k_tx_desc) * ATH9K_TX_DESC_COUNT; i++)
((u8 *)g_tx_desc)[i] = 0;
for (u32 i = 0; i < ATH9K_TX_DESC_COUNT; i++) {
g_tx_desc[i].ds_link = (u32)(g_tx_desc_dma.phys +
(((i + 1) % ATH9K_TX_DESC_COUNT) * sizeof(struct ath9k_tx_desc)));
}
__asm__ volatile("" ::: "memory");
g_tx_head = 0;
g_tx_ready = 1;
raw_log("[ath9k.raw] tx_init: rings allocated\n");
return 0;
}
static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) { static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity; (void)ctx; (void)buffer; (void)capacity;
if (out_length) *out_length = 0; if (out_length) *out_length = 0;
@@ -732,15 +1311,29 @@ int driver_init(const struct dkm_kernel_api *api,
return -5; return -5;
} }
/* 9. RX 环初始化 */ /* 9. RX/TX 环初始化 */
if (api->dma && api->dma->alloc_pages) { if (api->dma && api->dma->alloc_pages) {
int rc = ath9k_rx_init(api->dma); int rc = ath9k_rx_init(api->dma);
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc); raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
if (rc != 0) { if (rc != 0) {
raw_log("[ath9k.raw] RX init failed, scan unavailable\n"); raw_log("[ath9k.raw] RX init failed, scan unavailable\n");
} }
int trc = ath9k_tx_init(api->dma);
raw_hex("[ath9k.raw] tx_init rc=", (u64)(i8)trc);
if (trc != 0) raw_log("[ath9k.raw] TX init failed, connect unavailable\n");
} else { } else {
raw_log("[ath9k.raw] no DMA api, scan unavailable\n"); raw_log("[ath9k.raw] no DMA api, scan/TX unavailable\n");
}
/* 9b. WPA2 加密底座自检(KAT):失败即 panic,绝不带病上路 */
{
int krc = wpa2_kat();
if (krc != 0) {
raw_hex("[ath9k.raw] WPA2 KAT FAILED case=", (u64)krc);
g_log->panic("ATH-E02 WPA2 CRYPTO KAT FAILED");
return -7;
}
raw_log("[ath9k.raw] WPA2 crypto KAT ok\n");
} }
/* 10. 注册中断 */ /* 10. 注册中断 */
@@ -749,20 +1342,24 @@ int driver_init(const struct dkm_kernel_api *api,
raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc); raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc);
} }
/* 11. 注册 netdev(标记 WIRELESS + RX_READY) */ /* 11. 注册 netdev(无线 + RX/TX) */
struct dkm_net_device_desc ndesc; struct dkm_net_device_desc ndesc;
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0; for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
static const char devname[] = "ath9k0"; static const char devname[] = "ath9k0";
ndesc.name = devname; ndesc.name = devname;
copy_mac(ndesc.mac, g_mac); copy_mac(ndesc.mac, g_mac);
ndesc.flags = DKM_NET_F_WIRELESS | (g_rx_ready ? DKM_NET_F_RX_READY : 0); ndesc.flags = DKM_NET_F_WIRELESS |
(g_rx_ready ? DKM_NET_F_RX_READY : 0) |
(g_tx_ready ? DKM_NET_F_TX_READY : 0);
ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */ ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */
ndesc.tx = ath9k_tx_stub; ndesc.tx = ath9k_tx_impl;
ndesc.rx_poll = ath9k_rx_poll_impl; ndesc.rx_poll = ath9k_rx_poll_impl;
ndesc.scan_start = ath9k_scan_start_impl; ndesc.scan_start = ath9k_scan_start_impl;
ndesc.scan_count = ath9k_scan_count_impl; ndesc.scan_count = ath9k_scan_count_impl;
ndesc.scan_result = ath9k_scan_result_impl; ndesc.scan_result = ath9k_scan_result_impl;
ndesc.is_wireless = ath9k_is_wireless_impl; ndesc.is_wireless = ath9k_is_wireless_impl;
ndesc.connect = ath9k_connect_impl;
ndesc.disconnect = ath9k_disconnect_impl;
if (api->net && api->net->register_device) { if (api->net && api->net->register_device) {
int rc = api->net->register_device(&ndesc); int rc = api->net->register_device(&ndesc);
+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); void (*panic)(const char *msg);
}; };
/* D6 万物互联:dkm_bus 镜像(布局对齐 UTSM include/utsm/dkm.h) */
struct dkm_bus_service {
char name[48];
u32 version;
u32 flags;
void *fn;
const void *provider;
};
struct dkm_bus_api {
u32 magic;
u32 version;
int (*register_service)(const char *name, u32 version, void *fn);
int (*unregister_service)(const char *name);
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void);
const struct dkm_bus_service *(*at)(u32 index);
};
struct dkm_kernel_api { struct dkm_kernel_api {
u32 version; u32 version;
u32 size; u32 size;
@@ -47,6 +67,19 @@ struct dkm_kernel_api {
u64 fb_pitch; u64 fb_pitch;
u16 fb_bpp; u16 fb_bpp;
const void *boot_modules_response; const void *boot_modules_response;
/* —— 尾部追加字段镜像(DKM_ABI v1;到 bus 为止) —— */
void *irq_register; /* int (*)(u8, void*) */
u64 hhdm_offset;
const void *block;
const void *mmio;
void *mm_map_mmio;
void *mm_unmap_mmio;
void *register_apic_eoi;
void *irq_vector_alloc;
void *irq_vector_free;
const void *sched;
const void *kapi;
const struct dkm_bus_api *bus;
}; };
struct dkm_driver_handle; struct dkm_driver_handle;
@@ -185,6 +218,31 @@ int driver_init(const struct dkm_kernel_api *api,
devfs_file("fb0", info); devfs_file("fb0", info);
} }
/* /dev/services — D6 万物互联:总线已注册服务清单 */
if (api->bus && api->bus->magic == 0x53425553u /* "SBUS" */) {
char cnt[24];
cnt[0] = '0'; cnt[1] = 'x';
u64 v = api->bus->count();
u32 p = 2;
if (v == 0) cnt[p++] = '0';
else {
char r[18]; u32 q = 0;
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
while (q) cnt[p++] = r[--q];
}
cnt[p] = 0;
devfs_file("services", cnt);
g_log->info("[devfs] /dev/services/");
for (u32 i = 0; i < api->bus->count(); i++) {
const struct dkm_bus_service *s = api->bus->at(i);
if (s && s->name[0]) {
g_log->info("[devfs] ");
g_log->info(s->name);
}
}
}
/* /dev/modules — count from boot_modules */ /* /dev/modules — count from boot_modules */
const struct limine_module_response *rsp = const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response; (const struct limine_module_response *)api->boot_modules_response;
+5
View File
@@ -107,6 +107,9 @@ struct dkm_net_device_desc {
int (*scan_count)(void *ctx); int (*scan_count)(void *ctx);
int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(void *ctx); int (*is_wireless)(void *ctx);
/* 关联扩展(尾部追加,ABI 兼容;与 UTSM/include/utsm/net.h 布局一致)。 */
int (*connect)(void *ctx, const char *ssid, const char *password);
int (*disconnect)(void *ctx);
}; };
struct dkm_net_stats { struct dkm_net_stats {
@@ -128,6 +131,8 @@ struct dkm_net_api {
int (*scan_count)(u32 index); int (*scan_count)(u32 index);
int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out);
int (*is_wireless)(u32 index); int (*is_wireless)(u32 index);
int (*connect)(u32 index, const char *ssid, const char *password);
int (*disconnect)(u32 index);
int (*device_stats)(u32 index, struct dkm_net_stats *out); int (*device_stats)(u32 index, struct dkm_net_stats *out);
}; };
+7 -3
View File
@@ -735,11 +735,15 @@ int driver_init(const struct dkm_kernel_api *api,
raw_log("[e1000] MAC from RAL/RAH\n"); raw_log("[e1000] MAC from RAL/RAH\n");
} }
/* 仍为全 0 或多播位错误时使用与虚拟机配置一致的回退 MAC */ /* 仍为全 0 或多播位错误时——严格错误策略插桩:MAC 读不到 = 网卡
* 身份不可知,零降级 panic(E1K-E01),不再用硬编码 MAC 冒充。 */
if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) || if ((mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) ||
(mac[0] & 1)) { (mac[0] & 1)) {
mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56; raw_log("[e1000] MAC read failed (zero/multicast)\n");
raw_log("[e1000] using fallback MAC 52:54:00:12:34:56\n"); g_log->panic("E1K-E01 MAC READ FAILED");
/* fallback:本地管理单播地址(bit1=LAA,bit0=0),"DESHB" */
mac[0]=0x02; mac[1]=0x44; mac[2]=0x45; mac[3]=0x53; mac[4]=0x48; mac[5]=0x42;
raw_log("[e1000] fallback: using locally administered MAC\n");
} }
raw_mac("[e1000] MAC=", mac); raw_mac("[e1000] MAC=", mac);
+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;
}
+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; }
+10 -3
View File
@@ -920,13 +920,17 @@ static int nvme_msix_program(const struct dkm_kernel_api *api,
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */ * 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096); volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
if (!tbl) { if (!tbl) {
g_log->warn("[nvme] MSI-X: table MMIO map failed; polling"); /* 严格错误策略插桩:MSI-X 表映射失败不允许静默转轮询(NVME-E01) */
g_log->error("[nvme] MSI-X: table MMIO map failed");
g_log->panic("NVME-E01 MSIX TABLE MAP FAILED");
return 0; return 0;
} }
int vector = api->irq_vector_alloc(); int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) { if (vector < 0 || vector > 255) {
g_log->warn("[nvme] MSI-X: vector alloc failed; polling"); /* 严格错误策略插桩:向量分配失败不允许静默转轮询(NVME-E02) */
g_log->error("[nvme] MSI-X: vector alloc failed");
g_log->panic("NVME-E02 VECTOR ALLOC FAILED");
return 0; return 0;
} }
log_dec("[nvme] MSI-X vector=", (u64)vector); log_dec("[nvme] MSI-X vector=", (u64)vector);
@@ -958,7 +962,10 @@ static int nvme_msix_program(const struct dkm_kernel_api *api,
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc); log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 || if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) { (rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
g_log->warn("[nvme] MSI-X: readback mismatch; rolling back"); /* 严格错误策略插桩:MSI-X 编程回读不符 = 硬件/虚拟机拒绝消息中断,
* 不允许静默回滚转轮询(NVME-E03)。 */
g_log->error("[nvme] MSI-X: readback mismatch");
g_log->panic("NVME-E03 MSIX READBACK MISMATCH");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap); u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */ rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb); dkm_pci_write(bus, dev, func, (u8)cap, rb);
+10 -8
View File
@@ -775,19 +775,21 @@ static void virtio_net_selftest(void) {
log_hex("[virtio_net] selftest rx frames seen=", frames); log_hex("[virtio_net] selftest rx frames seen=", frames);
} }
/* ================= stub(初始化失败时的安全占位,与历史行为一致) ================= */ /* ================= stub(初始化失败时的安全占位) =================
* 严格错误策略插桩:占位不再静默丢包/静默无数据——一旦被上层调用
* (即有人真的把流量交给未协商 virtqueue 的设备),立即莲花 panic。
* 正常路径 netman 因 flags 缺 LINK_UP 不会选中本驱动,stub 不触发。 */
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) { static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
(void)ctx; (void)ctx; (void)packet;
(void)packet; log_hex("[virtio_net] tx stub hit, len=", length);
log_hex("[virtio_net] tx stub: packet dropped, len=", length); g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)");
return 0; return -1;
} }
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) { static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)ctx; (void)buffer; (void)capacity;
(void)buffer; g_log->panic("VNET-E02 RX VIRTQUEUE NOT IMPLEMENTED (stub rx called)");
(void)capacity;
if (out_length) *out_length = 0; if (out_length) *out_length = 0;
return 0; return 0;
} }
+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;
}
+491 -116
View File
@@ -92,6 +92,9 @@ const struct dkm_driver_desc driver_desc = {
#define XHCI_RT_IR0_ERDP_LO 0x38 #define XHCI_RT_IR0_ERDP_LO 0x38
#define XHCI_RT_IR0_ERDP_HI 0x3C #define XHCI_RT_IR0_ERDP_HI 0x3C
/* ERDP bits: bit3 = Event Handler Busy (软件写 1 清挂起) */
#define ERDP_EHB (1u << 3)
/* USBCMD bits */ /* USBCMD bits */
#define USBCMD_RS (1u << 0) #define USBCMD_RS (1u << 0)
#define USBCMD_RST (1u << 1) #define USBCMD_RST (1u << 1)
@@ -109,8 +112,11 @@ const struct dkm_driver_desc driver_desc = {
#define PORTSC_PR (1u << 4) #define PORTSC_PR (1u << 4)
#define PORTSC_PLS_SHIFT 5 #define PORTSC_PLS_SHIFT 5
#define PORTSC_PLS_MASK 0xF #define PORTSC_PLS_MASK 0xF
#define PORTSC_SPEED_SHIFT 17 /* Port Speed 字段在 PORTSC bits[13:10](xHCI 1.2 §5.4.8)。
#define PORTSC_SPEED_MASK 0x7 * BUG-FIX: 原实现 shift=17/mask=0x7 是错的——QEMU 用 10:13,
* 误读导致 SuperSpeed(4) 被当成 FULL(1),EP0 MPS 错误。 */
#define PORTSC_SPEED_SHIFT 10
#define PORTSC_SPEED_MASK 0xF
/* TRB types */ /* TRB types */
#define TRB_TYPE_NORMAL 0x01 #define TRB_TYPE_NORMAL 0x01
@@ -124,12 +130,14 @@ const struct dkm_driver_desc driver_desc = {
#define TRB_TYPE_CONFIGURE_EP 0x0C #define TRB_TYPE_CONFIGURE_EP 0x0C
#define TRB_TYPE_NOOP 0x17 #define TRB_TYPE_NOOP 0x17
/* Event TRB types — same namespace as command/transfer TRB types. /* Event TRB types — xHCI 1.2 spec Table 6-28 (TRB Type field [15:10]).
* Per xHCI spec §6.4: these are the TRB Type field values in the * 规范值:Transfer=0x20, Command Completion=0x21, Port Status Change=0x22。
* Event TRB control dword [15:10]. */ * BUG-FIX: 原定义 0x01/0x02/0x21 与规范冲突——CMD_COMPLETE(0x21) 被
#define EVT_TYPE_TRANSFER 0x01 /* Transfer Event */ * 误判为 PORT_CHANGE 事件消费,导致 Enable Slot 等命令事件环被提前
#define EVT_TYPE_CMD_COMPLETE 0x02 /* Command Completion Event */ * 清空,后续命令全部超时(QEMU hcd-xhci.c xhci_write_event 佐证)。 */
#define EVT_TYPE_PORT_CHANGE 0x21 /* Port Status Change Event */ #define EVT_TYPE_TRANSFER 0x20 /* Transfer Event */
#define EVT_TYPE_CMD_COMPLETE 0x21 /* Command Completion Event */
#define EVT_TYPE_PORT_CHANGE 0x22 /* Port Status Change Event */
/* TRB control field helpers */ /* TRB control field helpers */
#define TRB_TYPE_SHIFT 10 #define TRB_TYPE_SHIFT 10
@@ -281,6 +289,149 @@ static void log_hex(const char *prefix, u64 value) {
g_log->info(buf); g_log->info(buf);
} }
/* ================================================================
* Framebuffer 阶段诊断(实机无串口时屏幕直接显示 xhci 失败点)
* 失败时整屏填背景色 + 中央画大号两位十六进制状态码,然后 halt。
* 码表(调用点注释中注明):
* 红底 0x01..0x07 基础设施失败(PCI/MMIO/caps/reset/ring/start)
* 红底 0x11..0x14 PCI 自检细分(见 xhci_pci_diag_code)
* 黄底 0x08 所有端口无设备可枚举
* 黄底 0x0A..0x0F 枚举失败(slot/address/descriptor/configure/scsi)
* 依赖 dkm_kernel_api.fb_* 字段(32bpp,与 console_fb 一致)。
* ================================================================ */
static const u8 g_fb_font_hex[16][8] = {
{0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 0 */
{0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
{0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
{0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
{0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
{0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
{0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
{0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00}, /* 7 */
{0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
{0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
{0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
{0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
{0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
{0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
{0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
};
static void fb_draw_char(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, int ch) {
const u8 *g = g_fb_font_hex[ch & 0xF];
for (int row = 0; row < 8; row++) {
u8 bits = g[row];
for (int col = 0; col < 8; col++) {
if (!(bits & (0x80u >> col))) continue;
for (int sy = 0; sy < scale; sy++) {
u32 *line = (u32 *)((u8 *)fb + (u64)(y0 + row * scale + sy) * pitch);
for (int sx = 0; sx < scale; sx++)
line[x0 + col * scale + sx] = color;
}
}
}
}
static void fb_diag_show(const struct dkm_kernel_api *api, u32 bg, u32 code) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return; /* 无 framebuffer 时退化为仅串口日志 */
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
int scale = 8;
int cw = 8 * scale;
int cx = (int)(w / 2) - cw; /* 两位数字整体居中 */
int cy = (int)(h / 2) - (8 * scale) / 2;
u32 fg = 0xFFFFFFFFu;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, scale, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, scale, fg, (int)(code & 0xF));
/* 停住屏幕,等待实机拍照/观察 */
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 任意位数 hex(n=1..8),低位在前填充 */
static void fb_draw_hexn(u32 *fb, u64 pitch, int x0, int y0, int scale,
u32 color, u32 v, int n) {
for (int i = 0; i < n; i++) {
int nib = (int)((v >> (4 * (n - 1 - i))) & 0xF);
fb_draw_char(fb, pitch, x0 + i * 8 * scale, y0, scale, color, nib);
}
}
/* PCI 自检诊断清单项(bus/dev/device_id/class/progif) */
struct usb_ctl_info {
u32 bus;
u32 dev;
u32 devid; /* PCI device ID */
u32 cls; /* (class<<8)|subclass */
u32 progif;
};
/* 扩展诊断:大字标题码 + 下方逐行列出扫描到的 PCI 设备。
* 每行:bus(1) dev(1) deviceid(4) = 左 6 hex,class(2) progif(2) = 右 4 hex。
* scale=2 支持 16 行(640x480 完整容纳)。device ID 用于区分芯片型号:
* Intel 7系: xHCI=1e31, EHCI1=1e2d, EHCI2=1e26
* Intel 6系: EHCI1=1c2d, EHCI2=1c26 (H61 仅 1 个 EHCI)
* 第三方 USB3: ASMedia=1b21, Renesas=1033 */
/* 诊断用,保留 */
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) __attribute__((unused));
static void fb_diag_show_usb(const struct dkm_kernel_api *api, u32 bg, u32 code,
const struct usb_ctl_info *usb, u32 usb_count) {
if (!api || !api->fb_address || !api->fb_width || !api->fb_height)
return;
u32 *fb = (u32 *)api->fb_address;
u64 pitch = api->fb_pitch;
u64 w = api->fb_width, h = api->fb_height;
for (u64 y = 0; y < h; y++) {
u32 *line = (u32 *)((u8 *)fb + y * pitch);
for (u64 x = 0; x < w; x++) line[x] = bg;
}
u32 fg = 0xFFFFFFFFu;
/* 标题大字(scale 6,两位居中,固定顶部) */
int s = 6, cw = 8 * s;
int cx = (int)(w / 2) - cw;
int cy = 8;
if (code >= 0x10) fb_draw_char(fb, pitch, cx, cy, s, fg, (int)(code >> 4));
fb_draw_char(fb, pitch, cx + cw, cy, s, fg, (int)(code & 0xF));
/* 信息行(scale 2,最多 16 行):
* 左:bus(1)+dev(1)+devid(4)=6 hex;右:class(2)+progif(2)=4 hex */
int s2 = 2, cw2 = 8 * s2;
int row_y = cy + 8 * s + 8;
int total = (6 + 1 + 4) * cw2; /* 176px */
int xl = (int)(w / 2) - total / 2;
int xr = xl + (6 + 1) * cw2;
u32 max_rows = usb_count < 16 ? usb_count : 16;
for (u32 i = 0; i < max_rows; i++) {
u32 bd = ((usb[i].bus & 0xFF) << 4) | (usb[i].dev & 0x1F);
fb_draw_hexn(fb, pitch, xl, row_y, s2, fg, bd, 2);
fb_draw_hexn(fb, pitch, xl + 2 * cw2, row_y, s2, fg, usb[i].devid & 0xFFFF, 4);
fb_draw_hexn(fb, pitch, xr, row_y, s2, fg, usb[i].cls & 0xFFFF, 2);
fb_draw_hexn(fb, pitch, xr + 2 * cw2, row_y, s2, fg, usb[i].progif & 0xFFFF, 2);
row_y += 8 * s2 + 4; /* 20px/行 */
}
for (;;) {
__asm__ volatile("cli; hlt");
}
}
/* 红底 = 基础设施失败,halt(0xFFFF0000 在 BGRA 下是纯红;
* 原 0xFF0000FF 实际渲染为蓝——0x01 那次实机测试显示“蓝底”即此原因) */
#define FB_FATAL(api, code) fb_diag_show((api), 0xFFFF0000u, (code))
/* 红底 PCI 自检:标题码 + USB 控制器清单 */
#define FB_FATAL_show_usb(api, code) \
fb_diag_show_usb((api), 0xFFFF0000u, (code), g_diag_usb, g_diag_usb_count)
/* 黄底 = 枚举失败,halt */
#define FB_ENUM_FAIL(code) fb_diag_show(g_api, 0xFFFFC800u, (code))
static void xhci_zero(void *ptr, u32 len) { static void xhci_zero(void *ptr, u32 len) {
u8 *p = (u8 *)ptr; u8 *p = (u8 *)ptr;
for (u32 i = 0; i < len; i++) p[i] = 0; for (u32 i = 0; i < len; i++) p[i] = 0;
@@ -321,7 +472,98 @@ static __inline__ void xhci_mb(void) {
* Phase 1: PCI discovery + MMIO mapping * Phase 1: PCI discovery + MMIO mapping
* ================================================================ */ * ================================================================ */
static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { /* PCI 自检:区分“传统 0xCF8/0xCFC 配置机制失效” vs “确实无 xHCI 控制器”。
* 实机现象:xhci_find 全扫描失败(诊断码 0x01)——USB3 主板必然有 xHCI,
* 扫不到只可能是 (a) 配置机制读不到任何设备 (b) 设备在异常位置 (c) 控制器
* 是 EHCI 等非 xHCI。返回细分诊断码:
* 0x11 bus0/dev0/func0 vendor=0xFFFF → 0xCF8/0xCFC 机制失效
* 0x12 机制可用但全扫描无任何 USB 控制器 (class 0x0C/03)
* 0x13 有 USB 控制器但无 prog_if=0x30(全部 EHCI/UHCI/OHCI)
* 0x14 存在 prog_if=0x30 的 xHCI 但 xhci_find 漏匹配(防御性,理论不可能)
* 同时把扫描到的设备填入 g_diag_usb 供屏幕显示——记录策略:
* 第一遍收集全部 USB 控制器(class 0C/03,含 bus1+ 第三方芯片),
* 第二遍收集其他设备(bus0 优先),总上限 16 行。
* 关键 device ID 判读:Intel 7系 xHCI=1e31 / EHCI1=1e2d / EHCI2=1e26;
* Intel 6系 EHCI1=1c2d / EHCI2=1c26(H61 仅 1 个);ASMedia=1b21;
* Renesas=1033。 */
static struct usb_ctl_info g_diag_usb[16];
static u32 g_diag_usb_count;
#define DIAG_MAX 16
static void diag_add(u32 bus, u32 dev, u32 devid, u32 cls, u32 progif) {
if (g_diag_usb_count >= DIAG_MAX) return;
g_diag_usb[g_diag_usb_count].bus = bus;
g_diag_usb[g_diag_usb_count].dev = dev;
g_diag_usb[g_diag_usb_count].devid = devid;
g_diag_usb[g_diag_usb_count].cls = cls;
g_diag_usb[g_diag_usb_count].progif = progif;
g_diag_usb_count++;
}
/* 诊断用,保留(无 xHCI 场景默认不调用,可通过临时改 driver_init 调试) */
static u32 xhci_pci_diag_code(void) __attribute__((unused));
static u32 xhci_pci_diag_code(void) {
u32 vd0 = dkm_pci_read(0, 0, 0, PCI_VENDOR_ID);
g_diag_usb_count = 0;
if ((vd0 & 0xffff) == 0xffff) return 0x11;
u32 usb_total = 0, xhci_total = 0;
/* 第一遍:USB 控制器(全 bus,含 bus1+ 第三方芯片) */
for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) {
usb_total++;
if (prog_if == 0x30) xhci_total++;
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
/* 第二遍:其他设备补位(bus0 优先) */
if (g_diag_usb_count < DIAG_MAX) {
for (u16 bus = 0; bus < 256 && g_diag_usb_count < DIAG_MAX; bus++) {
for (u8 dev = 0; dev < 32 && g_diag_usb_count < DIAG_MAX; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(dkm_pci_read((u8)bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count && g_diag_usb_count < DIAG_MAX; func++) {
u32 vd2 = dkm_pci_read((u8)bus, dev, func, PCI_VENDOR_ID);
if ((vd2 & 0xffff) == 0xffff) continue;
u32 class_reg = dkm_pci_read((u8)bus, dev, func, 0x08);
u8 prog_if = (u8)((class_reg >> 8) & 0xff);
u8 subclass = (u8)((class_reg >> 16) & 0xff);
u8 class_code = (u8)((class_reg >> 24) & 0xff);
if (class_code == 0x0C && subclass == 0x03) continue; /* 已收集 */
diag_add(bus, dev, vd2 >> 16,
(u32)((class_code << 8) | subclass), prog_if);
}
}
}
}
if (usb_total == 0) return 0x12;
if (xhci_total > 0) return 0x14;
return 0x13;
}
static int xhci_find_nth(int nth, u8 *out_bus, u8 *out_dev, u8 *out_func) {
int seen = 0;
for (u16 bus = 0; bus < 256; bus++) { for (u16 bus = 0; bus < 256; bus++) {
for (u8 dev = 0; dev < 32; dev++) { for (u8 dev = 0; dev < 32; dev++) {
u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID); u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID);
@@ -341,11 +583,14 @@ static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
if (class_code == XHCI_CLASS_SERIAL && if (class_code == XHCI_CLASS_SERIAL &&
subclass == XHCI_SUBCLASS_USB && subclass == XHCI_SUBCLASS_USB &&
prog_if == XHCI_PROGIF_XHCI) { prog_if == XHCI_PROGIF_XHCI) {
if (seen == nth) {
*out_bus = (u8)bus; *out_bus = (u8)bus;
*out_dev = dev; *out_dev = dev;
*out_func = func; *out_func = func;
return 0; return 0;
} }
seen++;
}
} }
} }
} }
@@ -518,9 +763,18 @@ static int xhci_parse_caps(void) {
g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u; g_doorbell_off = mmio_read32(XHCI_CAP_DBOFF) & 0xFFFFFFF0u;
g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u; g_runtime_off = mmio_read32(XHCI_CAP_RTSOFF) & 0xFFFFFFE0u;
/* Page size */ /* Page size: PAGESIZE 寄存器 bit[n] 置位表示支持 2^(n+12) 字节页。
* 取置位的最低 bit(xHCI 规范要求软件使用最小支持页)。
* 原实现 (ps_reg & 0xF) 语义错误,实机多 bit 置位时会得到错误页大小。 */
u32 ps_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE); u32 ps_reg = mmio_read32(g_cap_length + XHCI_OP_PAGESIZE);
g_page_size = 1u << ((ps_reg & 0xF) + 12); u32 ps_bits = ps_reg & 0xFFFFu;
u32 ps_shift = 0;
if (ps_bits) {
while (!(ps_bits & 1u)) { ps_bits >>= 1; ps_shift++; }
g_page_size = 4096u << ps_shift;
} else {
g_page_size = 4096;
}
log_hex("[xhci] CAPLEN=", g_cap_length); log_hex("[xhci] CAPLEN=", g_cap_length);
log_hex("[xhci] MaxSlots=", g_max_slots); log_hex("[xhci] MaxSlots=", g_max_slots);
@@ -591,9 +845,13 @@ static u32 xhci_ring_enqueue(xhci_ring *ring, u64 param, u32 status, u32 control
return idx; return idx;
} }
/* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell */ /* Ring a doorbell: slot_id=0 for command, otherwise endpoint doorbell。
* Doorbell Registers 每个 4 字节(xHCI 1.2 §5.6):offset = slot_id * 4。
* BUG-FIX: 原实现 slot_id * 0x20 偏移错误——QEMU 门铃区 reg>>=2 后
* reg 即 slot_id,0x20 偏移会敲到 slot 8,该 slot 无 EP → 控制器不
* 处理 TRB → 无 Transfer Event → 轮询超时。 */
static void xhci_ring_doorbell(u32 slot_id, u32 target) { static void xhci_ring_doorbell(u32 slot_id, u32 target) {
mmio_write32(g_doorbell_off + slot_id * 0x20, target); mmio_write32(g_doorbell_off + slot_id * 4, target);
} }
/* ================================================================ /* ================================================================
@@ -627,9 +885,9 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1); g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1; if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
/* Update ERDP */ /* Update ERDP: 地址 16 字节对齐,bit0 保持 0,bit3=EHB 清挂起 */
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb); u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | g_evt_ccs); mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Check completion code */ /* Check completion code */
u32 cc = (evt->status >> 24) & 0xFF; u32 cc = (evt->status >> 24) & 0xFF;
@@ -659,7 +917,7 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1); g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1; if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp2 = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb); u64 erdp2 = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | g_evt_ccs); mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp2 | ERDP_EHB);
continue; continue;
} }
@@ -677,7 +935,7 @@ static int xhci_poll_event(u32 expected_type, u64 timeout_ms,
static int xhci_cmd_submit(u64 param, u32 status, u32 control, static int xhci_cmd_submit(u64 param, u32 status, u32 control,
u64 timeout_ms, u64 timeout_ms,
u64 *out_param, u32 *out_status) { u64 *out_param, u32 *out_status, u32 *out_control) {
/* Clear Event Ring interrupt pending */ /* Clear Event Ring interrupt pending */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN, mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u); mmio_read32(g_runtime_off + XHCI_RT_IR0_IMAN) | 2u);
@@ -705,7 +963,7 @@ static int xhci_cmd_submit(u64 param, u32 status, u32 control,
} }
int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms, int rc = xhci_poll_event(EVT_TYPE_CMD_COMPLETE, timeout_ms,
out_param, out_status, 0); out_param, out_status, out_control);
/* Clear EINT */ /* Clear EINT */
mmio_write32(g_cap_length + XHCI_OP_USBSTS, mmio_write32(g_cap_length + XHCI_OP_USBSTS,
@@ -810,8 +1068,8 @@ static int xhci_controller_start(void) {
/* Configure Event Ring */ /* Configure Event Ring */
mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1); mmio_write32(g_runtime_off + XHCI_RT_IR0_ERSTSZ, 1);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys); mmio_write64(g_runtime_off + XHCI_RT_IR0_ERSTBA_LO, g_erst.phys);
u64 erdp = g_evt_ring.dma.phys | (u64)g_evt_ccs; u64 erdp = g_evt_ring.dma.phys;
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp); mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
/* Enable interrupt (IMAN.IE=1) */ /* Enable interrupt (IMAN.IE=1) */
mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN, mmio_write32(g_runtime_off + XHCI_RT_IR0_IMAN,
@@ -850,8 +1108,12 @@ static int xhci_reset_port(u32 port_idx) {
/* Already enabled? */ /* Already enabled? */
if (portsc & PORTSC_PED) return 0; if (portsc & PORTSC_PED) return 0;
/* Clear any change bits (W1C), then assert port reset */ /* 只置 PR,并清掉 RW1C 位。把读到的 PORTSC 原样或上 PR 会把
mmio_write32(port_off, portsc | PORTSC_PR); * PED=0 和已经为 1 的变化位写回去,实机上复位经常起不来。 */
u32 cmd = portsc & ~((1u << 1) | PORTSC_PR | 0x00FE0000u);
cmd |= PORTSC_PR;
if (portsc & (1u << 9)) cmd |= (1u << 9);
mmio_write32(port_off, cmd);
/* Wait for PED (Port Enabled) with 500ms timeout */ /* Wait for PED (Port Enabled) with 500ms timeout */
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500; u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500;
@@ -883,7 +1145,7 @@ static int xhci_reset_port(u32 port_idx) {
g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1); g_evt_ring.enqueue = (g_evt_ring.enqueue + 1) % (g_evt_ring.size - 1);
if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1; if (g_evt_ring.enqueue == 0) g_evt_ccs ^= 1;
u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb); u64 erdp = g_evt_ring.dma.phys + (u64)g_evt_ring.enqueue * sizeof(xhci_trb);
mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | g_evt_ccs); mmio_write64(g_runtime_off + XHCI_RT_IR0_ERDP_LO, erdp | ERDP_EHB);
} }
return 0; return 0;
@@ -911,15 +1173,18 @@ static int xhci_enable_slot(u8 *out_slot_id) {
u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT); u32 control = (TRB_TYPE_ENABLE_SLOT << TRB_TYPE_SHIFT);
u64 out_param = 0; u64 out_param = 0;
u32 out_status = 0; u32 out_status = 0;
u32 out_control = 0;
int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status); int rc = xhci_cmd_submit(0, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) { if (rc != 0) {
g_log->error("[xhci] Enable Slot failed"); g_log->error("[xhci] Enable Slot failed");
return rc; return rc;
} }
/* Slot ID is in the upper bits of the completion event parameter */ /* Slot ID 位于 Command Completion Event 的 TRB control[31:24]。
*out_slot_id = (u8)((out_param >> 24) & 0xFF); * (xHCI 1.2 §6.4.2.2:CMD_COMPLETE 事件 TRB parameter = 命令 TRB 地址,
* slot id 在 control 高字节 —— QEMU hcd-xhci.c xhci_write_event 佐证) */
*out_slot_id = (u8)((out_control >> 24) & 0xFF);
log_hex("[xhci] slot_id=", *out_slot_id); log_hex("[xhci] slot_id=", *out_slot_id);
return 0; return 0;
} }
@@ -940,7 +1205,8 @@ static int xhci_address_device(u8 slot_id, int bsr) {
u64 out_param = 0; u64 out_param = 0;
u32 out_status = 0; u32 out_status = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status); u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) { if (rc != 0) {
g_log->error("[xhci] Address Device failed"); g_log->error("[xhci] Address Device failed");
return rc; return rc;
@@ -958,7 +1224,8 @@ static int xhci_configure_endpoint(u8 slot_id) {
u64 out_param = 0; u64 out_param = 0;
u32 out_status = 0; u32 out_status = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status); u32 out_control = 0;
int rc = xhci_cmd_submit(input_ctx_phys, 0, control, 5000, &out_param, &out_status, &out_control);
if (rc != 0) { if (rc != 0) {
g_log->error("[xhci] Configure Endpoint failed"); g_log->error("[xhci] Configure Endpoint failed");
return rc; return rc;
@@ -979,7 +1246,16 @@ static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24); ((u64)setup_pkt[2] << 16) | ((u64)setup_pkt[3] << 24);
u32 setup_status = 8; /* Setup packet length */ u32 setup_status = 8; /* Setup packet length */
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT) | TRB_IOC; /* Setup Stage TRB(不设 IOC:控制传输只需在最后一个 TRB 上报完成,
* 中间 TRB 设 IOC 会让 QEMU/实机产生多余 Transfer Event,残留事件
* 污染后续 CMD_COMPLETE 轮询——日志表现为 "unexpected event type") */
u32 setup_control = (TRB_TYPE_SETUP_STAGE << TRB_TYPE_SHIFT);
/* Setup Stage TRB 必须设置 IDT (Immediate Data, bit6):
* setup 包 8 字节直接内嵌在 TRB parameter 字段。
* BUG-FIX: 原实现缺 IDT,QEMU xhci_fire_ctl_transfer 校验
* `!(trb_setup->control & TRB_TR_IDT)` 直接返回 -1 → 无 Transfer
* Event → 轮询超时(GET_DESCRIPTOR 失败)。 */
setup_control |= (1u << 6); /* TRB_TR_IDT */
/* Set direction in TRB: bit 16 of Setup Stage TRB */ /* Set direction in TRB: bit 16 of Setup Stage TRB */
if (direction) setup_control |= (1u << 16); if (direction) setup_control |= (1u << 16);
@@ -995,7 +1271,8 @@ static int xhci_control_transfer(const u8 *setup_pkt, u8 *data, u32 data_len,
u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys; u64 data_phys = direction ? g_xfer_buf.phys : g_xfer_buf.phys;
u32 data_status = data_len; u32 data_status = data_len;
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT) | TRB_IOC; /* Data Stage 不设 IOC(避免多余事件残留) */
u32 data_control = (TRB_TYPE_DATA_STAGE << TRB_TYPE_SHIFT);
if (direction) data_control |= (1u << 16); /* DIR=IN */ if (direction) data_control |= (1u << 16); /* DIR=IN */
xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control); xhci_ring_enqueue(&g_ep0_ring, data_phys, data_status, data_control);
@@ -1088,7 +1365,11 @@ static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control); xhci_ring_enqueue(ring, g_xfer_buf.phys, data_len, control);
xhci_mb(); xhci_mb();
xhci_ring_doorbell(g_msc.slot_id, ep_num); /* Doorbell target = Endpoint ID:EPn OUT→2n,EPn IN→2n+1。
* BUG-FIX: 原实现传 EP 号(2/1),QEMU xhci_kick_ep 按 Endpoint ID
* 索引 eps[epid-1] → 敲错端点 → 无 Transfer Event → 轮询超时。 */
u8 db_target = (direction ? (u8)(2u * ep_num + 1) : (u8)(2u * ep_num));
xhci_ring_doorbell(g_msc.slot_id, db_target);
/* Poll for Transfer Event */ /* Poll for Transfer Event */
u64 out_param = 0; u64 out_param = 0;
@@ -1097,6 +1378,7 @@ static int xhci_bulk_transfer(xhci_ring *ring, u8 ep_num,
&out_param, &out_status, 0); &out_param, &out_status, 0);
if (rc != 0) { if (rc != 0) {
g_log->warn("[xhci] bulk transfer failed"); g_log->warn("[xhci] bulk transfer failed");
log_hex("[xhci] bulk rc=", (u64)(i64)rc);
return rc; return rc;
} }
@@ -1343,32 +1625,48 @@ static int xhci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) {
* Phase 3: Build Input Context for Address Device * Phase 3: Build Input Context for Address Device
* ================================================================ */ * ================================================================ */
static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0) { static void xhci_build_input_ctx_slot(u8 slot_id, u8 speed, u16 mps0, u32 port_idx) {
(void)slot_id; (void)slot_id;
u32 ctx_sz = g_ctx_size; u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt; u8 *ic = (u8 *)g_input_ctx.virt;
xhci_zero(ic, g_input_ctx.size); xhci_zero(ic, g_input_ctx.size);
/* Input Control Context: enable Slot Context (bit 0) and EP0 (bit 1) */ /* Input Control Context (xHCI 1.2 §6.4.3):
* DWORD0 = Drop Context Flags(本次全 0,不 drop 任何上下文)
* DWORD1 = Add Context Flags(bit0=Slot, bit1=EP0)
* BUG-FIX: 原实现把 Add flags 写进 DWORD0(Drop 槽),
* QEMU/实机校验 ictl_ctx[0]==0 && ictl_ctx[1]==0x3 直接失败 CC_TRB_ERROR。 */
u32 *icc = (u32 *)ic; u32 *icc = (u32 *)ic;
icc[0] = (1u << 1) | (1u << 0); /* A0=1 (Slot), A1=1 (EP0) */ icc[0] = 0x0; /* Drop Context Flags */
icc[1] = (1u << 0) | (1u << 1); /* Add: Slot + EP0 */
/* Slot Context starts at offset ctx_sz */ /* Slot Context (§6.2.2):
* DWORD0: Route String [0:19] + Speed [20:22] + Context Entries [27:31]
* DWORD1: Max Exit Latency [0:7] + Root Hub Port Number [16:23]
* BUG-FIX: 原实现 speed<<29(占用 Context Entries 位域)、MPS0 写入
* DWORD1(规范 Slot Context 无 MPS0 字段,MPS0 在 EP0 Context)——
* QEMU xhci_lookup_uport 读 DWORD1[16:23] 找端口,原值 port=0 查不到
* 设备 → CC_TRB_ERROR。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz); u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 route_string = 0; /* direct-connect, no hub */ u32 route_string = 0; /* direct-connect, no hub */
slot_ctx[0] = route_string | ((u32)speed << 29); slot_ctx[0] = route_string | ((u32)speed << 20) | (1u << 27);
slot_ctx[1] = (u32)mps0; /* Max Packet Size for EP0 */ slot_ctx[1] = (port_idx + 1) << 16; /* Root Hub Port Number (1-based) */
slot_ctx[2] = (1u << 27); /* Context Entries = 1 (only EP0) */
/* EP0 Context starts at offset ctx_sz * 2 */ /* EP0 Context starts at offset ctx_sz * 2
* Endpoint Context (§6.2.3) 布局:
* DWORD1: CErr[4:5] + EP Type[6:8] + Max Packet Size[16:31]
* DWORD2: Dequeue Pointer Low[4:31](bit0=DCS, bits1:3=reserved)
* DWORD3: Dequeue Pointer High
* DWORD4: Average TRB Length[0:15]
* BUG-FIX: 原实现把 dequeue 写到 DWORD6/7——QEMU xhci_init_epctx
* 从 DWORD2/3 读 dequeue(ctx[2]&~0xf, ctx[3]),读到 0 → ring 无
* dequeue → kick_epctx 直接返回,TRB 永不处理 → 无 Transfer Event。 */
u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2); u32 *ep0_ctx = (u32 *)(ic + ctx_sz * 2);
/* EP Type = Control, Max Packet Size, etc. */
ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */ ep0_ctx[1] = (4u << 3) | ((u32)mps0 << 16); /* EP Type=4 (Control IN), MPS */
ep0_ctx[4] = 8; /* Average TRB Length */
/* Dequeue Pointer: EP0 transfer ring */
u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs; u64 ep0_deq = g_ep0_ring.dma.phys | (u64)g_ep0_ring.ccs;
ep0_ctx[6] = (u32)(ep0_deq & 0xFFFFFFFFu); ep0_ctx[2] = (u32)(ep0_deq & 0xFFFFFFF0u) | (u32)g_ep0_ring.ccs;
ep0_ctx[7] = (u32)(ep0_deq >> 32); ep0_ctx[3] = (u32)(ep0_deq >> 32);
ep0_ctx[4] = 8; /* Average TRB Length */
} }
static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep, static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
@@ -1376,31 +1674,45 @@ static void xhci_build_input_ctx_bulk_eps(u8 bulk_out_ep, u8 bulk_in_ep,
u32 ctx_sz = g_ctx_size; u32 ctx_sz = g_ctx_size;
u8 *ic = (u8 *)g_input_ctx.virt; u8 *ic = (u8 *)g_input_ctx.virt;
/* Update Input Control Context: enable EP0, Bulk OUT, Bulk IN */ /* Input Control Context(Configure Endpoint 语义,xHCI 1.2 §6.4.3):
* DWORD0 = Drop(0),DWORD1 = Add。
* Configure 时 bit0=Slot 必须 Add,bit1=EP0 不可重复 Add
* (QEMU xhci_configure_slot 校验 (ictl[1]&0x3)==0x1)。
* 端点位用 Context Index:EPn OUT→2n,EPn IN→2n+1
* (QEMU 从 i=2 开始遍历 Add flags,按 ictx+32*i 读 EP Context)。 */
u32 *icc = (u32 *)ic; u32 *icc = (u32 *)ic;
icc[0] = (1u << 1) | (1u << 0) | u32 out_idx = 2u * bulk_out_ep;
(1u << bulk_out_ep) | (1u << bulk_in_ep); u32 in_idx = 2u * bulk_in_ep + 1;
icc[0] = 0x0;
icc[1] = (1u << 0) | (1u << out_idx) | (1u << in_idx);
/* Update Slot Context: Context Entries = max(bulk_in_ep, bulk_out_ep) */ /* Update Slot Context: Context Entries = max Context Index
* (DWORD0 bits[27:31],保留 Speed/Route 原值)。 */
u32 *slot_ctx = (u32 *)(ic + ctx_sz); u32 *slot_ctx = (u32 *)(ic + ctx_sz);
u32 max_ep = bulk_out_ep > bulk_in_ep ? bulk_out_ep : bulk_in_ep; u32 max_idx = out_idx > in_idx ? out_idx : in_idx;
slot_ctx[2] = (max_ep << 27); slot_ctx[0] &= ~(0x1Fu << 27);
slot_ctx[0] |= (max_idx << 27);
/* Bulk OUT Endpoint Context (at offset ctx_sz * (1 + bulk_out_ep)) */ /* Bulk OUT Endpoint Context。
u32 *out_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_out_ep)); * 偏移 = ctx_sz * (out_idx + 1):input context 布局是
* [ICC][Slot][EP0][EP1]... 每个占 1 个 ctx_sz,Context Index i 的
* EP context 在 (i+1) 个槽处(QEMU xhci_configure_slot 用
* ictx+32+(32*i) 读取)。BUG-FIX: 原实现 ctx_sz*out_idx 少了 ICC
* 槽 → 全部错位 32 字节 → QEMU 读到零 EP context。 */
u32 *out_ctx = (u32 *)(ic + ctx_sz * (out_idx + 1));
out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16); out_ctx[1] = (EP_TYPE_BULK_OUT << 3) | ((u32)out_mps << 16);
out_ctx[4] = 512; /* Average TRB Length */
u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs; u64 out_deq = g_bulk_out_ring.dma.phys | (u64)g_bulk_out_ring.ccs;
out_ctx[6] = (u32)(out_deq & 0xFFFFFFFFu); out_ctx[2] = (u32)(out_deq & 0xFFFFFFF0u) | (u32)g_bulk_out_ring.ccs;
out_ctx[7] = (u32)(out_deq >> 32); out_ctx[3] = (u32)(out_deq >> 32);
out_ctx[4] = 512; /* Average TRB Length */
/* Bulk IN Endpoint Context (at offset ctx_sz * (1 + bulk_in_ep)) */ /* Bulk IN Endpoint Context (at offset ctx_sz * (in_idx + 1)) */
u32 *in_ctx = (u32 *)(ic + ctx_sz * (1 + bulk_in_ep)); u32 *in_ctx = (u32 *)(ic + ctx_sz * (in_idx + 1));
in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16); in_ctx[1] = (EP_TYPE_BULK_IN << 3) | ((u32)in_mps << 16);
in_ctx[4] = 512; /* Average TRB Length */
u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs; u64 in_deq = g_bulk_in_ring.dma.phys | (u64)g_bulk_in_ring.ccs;
in_ctx[6] = (u32)(in_deq & 0xFFFFFFFFu); in_ctx[2] = (u32)(in_deq & 0xFFFFFFF0u) | (u32)g_bulk_in_ring.ccs;
in_ctx[7] = (u32)(in_deq >> 32); in_ctx[3] = (u32)(in_deq >> 32);
in_ctx[4] = 512; /* Average TRB Length */
} }
/* ================================================================ /* ================================================================
@@ -1411,18 +1723,22 @@ static int xhci_enum_msc_device(u32 port_idx) {
u8 speed = xhci_get_port_speed(port_idx); u8 speed = xhci_get_port_speed(port_idx);
log_hex("[xhci] port speed=", speed); log_hex("[xhci] port speed=", speed);
/* Default MaxPacketSize for EP0 based on speed */ /* Default MaxPacketSize for EP0 based on speed
u16 mps0 = 64; /* High/Super-Speed default */ * (xHCI 1.2 §4.3: Full=8? No — FS/HS EP0 通常 64,LS=8,SS=512) */
if (speed == USB_SPEED_FULL) mps0 = 64; u16 mps0 = 64; /* Full/High-Speed default */
if (speed == USB_SPEED_LOW) mps0 = 8; if (speed == USB_SPEED_LOW) mps0 = 8;
if (speed == USB_SPEED_SUPER) mps0 = 512;
/* Step 1: Enable Slot */ /* Step 1: Enable Slot */
u8 slot_id = 0; u8 slot_id = 0;
if (xhci_enable_slot(&slot_id) != 0) return -1; if (xhci_enable_slot(&slot_id) != 0) {
FB_ENUM_FAIL(0x0A); /* 黄: Enable Slot 失败 */
return -1;
}
g_msc.slot_id = slot_id; g_msc.slot_id = slot_id;
g_msc.speed = speed; g_msc.speed = speed;
/* Step 2: Allocate per-device DMA buffers */ /* Step 1.5: Allocate per-device DMA buffers(在 DCBAA 指向输出上下文之前) */
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_input_ctx) != 0) return -2; if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_input_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_dev_ctx) != 0) return -2; if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_dev_ctx) != 0) return -2;
if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2; if (g_api->dma->alloc_pages(4, 4096, 0x100000000ULL, &g_xfer_buf) != 0) return -2;
@@ -1431,18 +1747,30 @@ static int xhci_enum_msc_device(u32 port_idx) {
if (xhci_ring_init(&g_bulk_out_ring, XFER_RING_SIZE) != 0) return -2; if (xhci_ring_init(&g_bulk_out_ring, XFER_RING_SIZE) != 0) return -2;
if (xhci_ring_init(&g_bulk_in_ring, XFER_RING_SIZE) != 0) return -2; if (xhci_ring_init(&g_bulk_in_ring, XFER_RING_SIZE) != 0) return -2;
/* DCBAA[slot_id] 必须指向输出上下文(Device Context)。
* Address Device/Configure Endpoint 命令都会读取该地址;
* 缺失时 QEMU/实机将 octx=0 → DMA 写失败/CC_TRB_ERROR。 */
xhci_zero(g_dev_ctx.virt, g_dev_ctx.size);
u64 *dcbaa = (u64 *)g_dcbaa.virt;
dcbaa[slot_id] = g_dev_ctx.phys;
log_hex("[xhci] DCBAA[slot]=", dcbaa[slot_id]);
/* Step 3: Build Input Context and Address Device (BSR=1 first) */ /* Step 3: Build Input Context and Address Device (BSR=1 first) */
xhci_build_input_ctx_slot(slot_id, speed, mps0); xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 1) != 0) { if (xhci_address_device(slot_id, 1) != 0) {
/* BSR=1 failed, try without BSR */ /* BSR=1 failed, try without BSR */
xhci_build_input_ctx_slot(slot_id, speed, mps0); xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) return -3; if (xhci_address_device(slot_id, 0) != 0) {
FB_ENUM_FAIL(0x0B); /* 黄: Address Device 失败(BSR=1/0 均失败) */
return -3;
}
} }
/* Step 4: Get Device Descriptor */ /* Step 4: Get Device Descriptor */
u8 dev_desc[18]; u8 dev_desc[18];
if (xhci_get_device_descriptor(dev_desc) != 0) { if (xhci_get_device_descriptor(dev_desc) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed"); g_log->warn("[xhci] GET_DESCRIPTOR(Device) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Device) 失败 */
return -4; return -4;
} }
@@ -1459,7 +1787,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
/* Step 5: Re-address with real MPS if different */ /* Step 5: Re-address with real MPS if different */
if (real_mps0 != mps0 && real_mps0 >= 8) { if (real_mps0 != mps0 && real_mps0 >= 8) {
mps0 = real_mps0; mps0 = real_mps0;
xhci_build_input_ctx_slot(slot_id, speed, mps0); xhci_build_input_ctx_slot(slot_id, speed, mps0, port_idx);
if (xhci_address_device(slot_id, 0) != 0) { if (xhci_address_device(slot_id, 0) != 0) {
g_log->warn("[xhci] re-address failed"); g_log->warn("[xhci] re-address failed");
/* Continue anyway — device may still work */ /* Continue anyway — device may still work */
@@ -1470,12 +1798,16 @@ static int xhci_enum_msc_device(u32 port_idx) {
u8 cfg_desc[255]; u8 cfg_desc[255];
if (xhci_get_config_descriptor(cfg_desc, 255) != 0) { if (xhci_get_config_descriptor(cfg_desc, 255) != 0) {
g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed"); g_log->warn("[xhci] GET_DESCRIPTOR(Config) failed");
FB_ENUM_FAIL(0x0C); /* 黄: GET_DESCRIPTOR(Config) 失败 */
return -5; return -5;
} }
/* Parse configuration descriptor to find MSC interface and endpoints */ /* Parse configuration descriptor to find MSC interface and endpoints */
u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8); u16 total_len = (u16)cfg_desc[2] | ((u16)cfg_desc[3] << 8);
if (total_len < 9) return -5; if (total_len < 9) {
FB_ENUM_FAIL(0x0D); /* 黄: 配置描述符过短 */
return -5;
}
u8 bulk_out_ep = 0, bulk_in_ep = 0; u8 bulk_out_ep = 0, bulk_in_ep = 0;
u16 bulk_out_mps = 512, bulk_in_mps = 512; u16 bulk_out_mps = 512, bulk_in_mps = 512;
@@ -1497,10 +1829,16 @@ static int xhci_enum_msc_device(u32 port_idx) {
msc_found = 1; msc_found = 1;
g_log->info("[xhci] MSC BOT interface found"); g_log->info("[xhci] MSC BOT interface found");
/* Scan following endpoint descriptors */ /* Scan following endpoint descriptors.
* SuperSpeed 设备在 endpoint 后紧跟 6 字节 SS Endpoint
* Companion descriptor (type 0x30)——必须跳过继续找下一
* 个 endpoint,不能因 type!=5 提前终止(Bulk OUT 常在
* IN 之后且被 companion 隔开)。 */
u32 ep_pos = pos + desc_len; u32 ep_pos = pos + desc_len;
while (ep_pos + 7 <= (u32)total_len && while (ep_pos + 2 <= (u32)total_len) {
cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */ u8 ep_len = cfg_desc[ep_pos];
if (ep_len == 0 || ep_pos + ep_len > (u32)total_len) break;
if (cfg_desc[ep_pos + 1] == 5) { /* endpoint descriptor */
u8 ep_addr = cfg_desc[ep_pos + 2]; u8 ep_addr = cfg_desc[ep_pos + 2];
u8 ep_attr = cfg_desc[ep_pos + 3]; u8 ep_attr = cfg_desc[ep_pos + 3];
u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8); u16 ep_mps = (u16)cfg_desc[ep_pos + 4] | ((u16)cfg_desc[ep_pos + 5] << 8);
@@ -1514,7 +1852,11 @@ static int xhci_enum_msc_device(u32 port_idx) {
bulk_out_mps = ep_mps; bulk_out_mps = ep_mps;
} }
} }
ep_pos += cfg_desc[ep_pos]; } else if (cfg_desc[ep_pos + 1] == 4) {
/* 下一个 interface — 停止扫描 */
break;
}
ep_pos += ep_len;
} }
break; break;
} }
@@ -1524,6 +1866,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) { if (!msc_found || bulk_out_ep == 0 || bulk_in_ep == 0) {
g_log->warn("[xhci] no MSC BOT interface found"); g_log->warn("[xhci] no MSC BOT interface found");
FB_ENUM_FAIL(0x0D); /* 黄: 未找到 MSC BOT 接口/端点 */
return -6; return -6;
} }
@@ -1543,6 +1886,7 @@ static int xhci_enum_msc_device(u32 port_idx) {
bulk_out_mps, bulk_in_mps); bulk_out_mps, bulk_in_mps);
if (xhci_configure_endpoint(slot_id) != 0) { if (xhci_configure_endpoint(slot_id) != 0) {
g_log->warn("[xhci] Configure Endpoint failed"); g_log->warn("[xhci] Configure Endpoint failed");
FB_ENUM_FAIL(0x0E); /* 黄: Configure Endpoint 失败 */
return -7; return -7;
} }
@@ -1584,6 +1928,8 @@ static int xhci_enum_msc_device(u32 port_idx) {
* driver_init — main entry point * driver_init — main entry point
* ================================================================ */ * ================================================================ */
#define XHCI_TRY_NEXT do { xhci_nth++; if (xhci_nth < 4) goto again_ctrl; return 0; } while (0)
__attribute__((visibility("default"))) __attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api, int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) { struct dkm_driver_handle *handle) {
@@ -1599,9 +1945,15 @@ int driver_init(const struct dkm_kernel_api *api,
/* ---- Phase 1: PCI discovery ---- */ /* ---- Phase 1: PCI discovery ---- */
u8 bus = 0, dev = 0, func = 0; u8 bus = 0, dev = 0, func = 0;
if (xhci_find(&bus, &dev, &func) != 0) { /* 实机常有不止一只 xHCI。U 盘可能不在第一只上,找不到 MSC 就试下一只。 */
int xhci_nth = 0;
again_ctrl:
if (xhci_find_nth(xhci_nth, &bus, &dev, &func) != 0) {
g_log->info("[xhci] xHCI controller not found"); g_log->info("[xhci] xHCI controller not found");
g_log->info("[xhci] driver ready"); /* BUG-FIX: 不 halt。xhci 是可选驱动(manifest required=false),
* 无 xHCI 的平台(Intel 6 系/纯 EHCI)必须继续引导到 EHCI/DSK,
* 否则 `for(;;)hlt` 卡死整条启动链。仅记日志,如需诊断可开
* xhci_pci_diag_code() 打印控制器清单(见注释)。 */
return 0; return 0;
} }
@@ -1616,6 +1968,18 @@ int driver_init(const struct dkm_kernel_api *api,
log_hex("[xhci] vendor=", vd & 0xffff); log_hex("[xhci] vendor=", vd & 0xffff);
log_hex("[xhci] device=", vd >> 16); log_hex("[xhci] device=", vd >> 16);
/* Intel PCH 默认把 USB2 口留在 EHCI。不把路由切到 xHCI,
* U 盘在 xHCI 上永远没有 CCS,块设备就不会注册。 */
if ((vd & 0xffff) == 0x8086) {
u32 usb2_mask = dkm_pci_read(bus, dev, func, 0xD4);
u32 usb3_mask = dkm_pci_read(bus, dev, func, 0xDC);
if (usb2_mask) dkm_pci_write(bus, dev, func, 0xD0, usb2_mask);
if (usb3_mask) dkm_pci_write(bus, dev, func, 0xD8, usb3_mask);
log_hex("[xhci] intel usb2 route=", usb2_mask);
log_hex("[xhci] intel usb3 route=", usb3_mask);
dkm_delay_ms(200);
}
/* Enable PCI MEM + BUSM, disable INTx (polling mode) */ /* Enable PCI MEM + BUSM, disable INTx (polling mode) */
u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10); command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10);
@@ -1628,8 +1992,8 @@ int driver_init(const struct dkm_kernel_api *api,
u64 bar_phys; u64 bar_phys;
if (bar0_lo & 1u) { if (bar0_lo & 1u) {
g_log->warn("[xhci] BAR0 is IO space; unsupported"); g_log->warn("[xhci] BAR0 is IO space; unsupported");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x02); /* 红: BAR0 是 IO 空间 */
return 0; XHCI_TRY_NEXT;
} }
if ((bar0_lo & 0x6u) == 0x4u) { if ((bar0_lo & 0x6u) == 0x4u) {
bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u); bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u);
@@ -1644,23 +2008,23 @@ int driver_init(const struct dkm_kernel_api *api,
if (!api->hhdm_offset || !bar_phys) { if (!api->hhdm_offset || !bar_phys) {
g_log->warn("[xhci] missing HHDM or BAR0; skip"); g_log->warn("[xhci] missing HHDM or BAR0; skip");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x02); /* 红: 缺 HHDM/BAR */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 1: MMIO mapping ---- */ /* ---- Phase 1: MMIO mapping ---- */
g_mmio = xhci_map_bar(api, bar_phys); g_mmio = xhci_map_bar(api, bar_phys);
if (!g_mmio) { if (!g_mmio) {
g_log->error("[xhci] BAR MMIO unavailable; driver inactive"); g_log->error("[xhci] BAR MMIO unavailable; driver inactive");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x03); /* 红: BAR MMIO 映射失败 */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 1: Parse capabilities ---- */ /* ---- Phase 1: Parse capabilities ---- */
if (xhci_parse_caps() != 0) { if (xhci_parse_caps() != 0) {
g_log->error("[xhci] capability parse failed"); g_log->error("[xhci] capability parse failed");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x04); /* 红: capability 解析失败 */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 1: BIOS Handoff ---- */ /* ---- Phase 1: BIOS Handoff ---- */
@@ -1669,28 +2033,28 @@ int driver_init(const struct dkm_kernel_api *api,
/* ---- Phase 1: Controller Reset ---- */ /* ---- Phase 1: Controller Reset ---- */
if (xhci_controller_reset() != 0) { if (xhci_controller_reset() != 0) {
g_log->error("[xhci] controller reset failed"); g_log->error("[xhci] controller reset failed");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x05); /* 红: 控制器 reset 失败 */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */ /* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */
if (!api->dma) { if (!api->dma) {
g_log->warn("[xhci] DMA API unavailable"); g_log->warn("[xhci] DMA API unavailable");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x06); /* 红: DMA API 缺失 */
return 0; XHCI_TRY_NEXT;
} }
if (xhci_setup_rings() != 0) { if (xhci_setup_rings() != 0) {
g_log->error("[xhci] ring setup failed"); g_log->error("[xhci] ring setup failed");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x06); /* 红: ring 初始化失败 */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 2: Start Controller ---- */ /* ---- Phase 2: Start Controller ---- */
if (xhci_controller_start() != 0) { if (xhci_controller_start() != 0) {
g_log->error("[xhci] controller start failed"); g_log->error("[xhci] controller start failed");
g_log->info("[xhci] driver ready"); FB_FATAL(api, 0x07); /* 红: 控制器启动失败 */
return 0; XHCI_TRY_NEXT;
} }
/* ---- Phase 3: Port Scan + Device Enumeration ---- */ /* ---- Phase 3: Port Scan + Device Enumeration ---- */
@@ -1700,53 +2064,57 @@ int driver_init(const struct dkm_kernel_api *api,
* attachment and update port status. Without this delay, PORTSC.CCS * attachment and update port status. Without this delay, PORTSC.CCS
* may read 0 even though a device is physically connected. * may read 0 even though a device is physically connected.
*/ */
g_log->info("[xhci] waiting 100ms for port settle..."); /* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。
dkm_delay_ms(100); * 20ms 就读 CCS 时,实机上的 U 盘还没出现,于是不会注册块设备。 */
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
if (g_ppc && !(portsc & (1u << 9))) {
mmio_write32(port_off, portsc | (1u << 9));
}
}
g_log->info("[xhci] waiting 1500ms for port settle...");
dkm_delay_ms(1500);
g_msc.ready = 0; g_msc.ready = 0;
int saw_ccs = 0;
int saw_reset_fail = 0;
for (int pass = 0; pass < 2 && !g_msc.ready; pass++) {
if (pass == 1) {
g_log->info("[xhci] second port scan");
dkm_delay_ms(1500);
}
for (u32 port = 0; port < g_max_ports; port++) { for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC; u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off); u32 portsc = mmio_read32(port_off);
/* Always log PORTSC for debugging, even if no device is connected */
log_hex("[xhci] port=", port); log_hex("[xhci] port=", port);
log_hex("[xhci] PORTSC=", portsc); log_hex("[xhci] PORTSC=", portsc);
if (!(portsc & PORTSC_CCS)) { if (!(portsc & PORTSC_CCS)) continue;
/*
* If PPC (Per-Port Power Control) is supported, the port may
* need to be powered on before CCS reflects device connection.
* Set PP (Port Power) bit in PORTSC to enable port power.
* If PPC=0, ports are always powered and this write is a no-op.
*/
if (g_ppc && !(portsc & (1u << 9))) {
g_log->info("[xhci] enabling port power");
mmio_write32(port_off, portsc | (1u << 9));
/* Wait briefly for power to stabilize */
dkm_delay_ms(20);
portsc = mmio_read32(port_off);
log_hex("[xhci] PORTSC after PP=", portsc);
}
if (!(portsc & PORTSC_CCS)) continue; /* Still no device */
}
saw_ccs = 1;
g_log->info("[xhci] device connected"); g_log->info("[xhci] device connected");
/* Port Reset */
if (xhci_reset_port(port) != 0) { if (xhci_reset_port(port) != 0) {
g_log->warn("[xhci] port reset failed; skipping"); g_log->warn("[xhci] port reset failed");
saw_reset_fail = 1;
continue; continue;
} }
g_log->info("[xhci] port reset OK"); g_log->info("[xhci] port reset OK");
/* Try to enumerate as MSC device */
if (xhci_enum_msc_device(port) == 0) { if (xhci_enum_msc_device(port) == 0) {
g_log->info("[xhci] MSC enumeration successful"); g_log->info("[xhci] MSC enumeration successful");
break; /* First MSC device is enough for now */ break;
} }
g_log->warn("[xhci] MSC enumeration failed; trying next port"); g_log->warn("[xhci] MSC enumeration failed; trying next port");
} }
}
if (!g_msc.ready && saw_ccs && xhci_nth >= 3) {
g_log->panic(saw_reset_fail ? "XHC-E01 PORT RESET FAILED"
: "XHC-E02 MSC ENUM FAILED");
}
/* ---- Phase 5: Register Block Device ---- */ /* ---- Phase 5: Register Block Device ---- */
if (g_msc.ready) { if (g_msc.ready) {
@@ -1765,11 +2133,18 @@ int driver_init(const struct dkm_kernel_api *api,
} }
} else { } else {
g_log->info("[xhci] no MSC device found; block provider not registered"); g_log->info("[xhci] no MSC device found; block provider not registered");
if (xhci_nth < 3) {
xhci_nth++;
g_log->info("[xhci] trying next controller");
goto again_ctrl;
}
FB_FATAL(api, 0x08); /* 黄: 所有端口无设备/枚举全部失败 */
} }
g_log->info("[xhci] driver ready"); g_log->info("[xhci] driver ready");
return 0; return 0;
} }
#undef XHCI_TRY_NEXT
__attribute__((visibility("default"))) __attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) { int driver_exit(struct dkm_driver_handle *handle) {
+4 -4
View File
@@ -1,5 +1,5 @@
CC := clang CC ?= clang
LD := ld.lld LD ?= ld.lld
OBJCOPY := llvm-objcopy OBJCOPY := llvm-objcopy
ROOT := ../.. ROOT := ../..
@@ -9,8 +9,8 @@ CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-
ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude -I../utsm-ipc ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude -I../utsm-ipc
LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld
C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c vmm/*.c pe/*.c) C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c sched/*.c dkm/*.c test/*.c vmm/*.c pe/*.c brand/*.c pkg/*.c)
S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S) S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S sched/*.S)
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o) OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
DEPS := $(C_SOURCES:.c=.d) DEPS := $(C_SOURCES:.c=.d)
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
arch/x86_64/halt.o: arch\x86_64\halt.c
Binary file not shown.
+97 -19
View File
@@ -2,6 +2,7 @@
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/types.h> #include <utsm/types.h>
#include <utsm/instr.h> #include <utsm/instr.h>
#include <utsm/panic.h>
/* IDT gate descriptor (64-bit) */ /* IDT gate descriptor (64-bit) */
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
@@ -405,6 +406,43 @@ static void pic_remap(void) {
outb(PIC2_DATA, 0xFF); /* mask all slave */ outb(PIC2_DATA, 0xFF); /* mask all slave */
} }
/* P1(万物互联): kern.irq.unmask/mask 总线服务后端 —— PIC IMR 位操作。
* 仅 ISA IRQ 0-15(越界 = 编程错误,IRQ-E01 零降级)。
* unmask IRQ8-15 时自动放开主片 IRQ2 级联。
* 注意 apic_route=1 时 PIC 已全屏蔽、由 IOAPIC 接管(B7);该路径下
* 的解屏蔽随 P6 真 IRQ 输入走 IOAPIC 重定向表接线。 */
void pic_irq_unmask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_unmask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m & (u8)~(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m & (u8)~(1u << (irq - 8)));
u8 m1 = inb(PIC1_DATA);
outb(PIC1_DATA, m1 & (u8)~(1u << 2)); /* 主片 IRQ2 级联 */
}
}
void pic_irq_mask(u8 irq) {
if (irq > 15) {
panic_full("IRQ-E01 IRQ OUT OF RANGE",
"pic_irq_mask: ISA IRQ range is 0-15", 0);
return;
}
if (irq < 8) {
u8 m = inb(PIC1_DATA);
outb(PIC1_DATA, m | (u8)(1u << irq));
} else {
u8 m = inb(PIC2_DATA);
outb(PIC2_DATA, m | (u8)(1u << (irq - 8)));
}
}
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。 /* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设 * 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位 * OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
@@ -559,6 +597,8 @@ int irq_vector_alloc(void) {
return vec; return vec;
} }
log_warn("[IDT] vector alloc failed: pool exhausted"); log_warn("[IDT] vector alloc failed: pool exhausted");
panic_full("IDT-E01 VECTOR POOL EXHAUSTED",
"irq_vector_alloc: dynamic vector pool exhausted", 0);
return -1; return -1;
} }
@@ -591,6 +631,26 @@ int irq_register(u8 vector, irq_handler_t handler) {
return 0; return 0;
} }
/* Phase7: 显式 EOI(调度器 tick 等"handler 内可能切换任务"的路径用)。
* 序列复刻 idt_handler 默认 EOI: 先 LAPIC 钩子再 8259(按向量范围)。
* spurious 0xFF 特判与 idt_handler 一致(空 ISR 不该有 EOI)。 */
void idt_irq_eoi(u8 vector) {
if (vector < 16) vector = (u8)(0x20u + vector);
if (vector >= 32 && vector <= 47) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
if (vector >= 0x28) outb(PIC2_CMD, 0x20);
outb(PIC1_CMD, 0x20);
} else if (vector >= 48 && vector != 0xFF) {
if (g_apic_eoi_hook) g_apic_eoi_hook();
}
}
/* Phase8: CPU 异常钩子(DRR 任务级 fault 接管)。 */
static int (*g_exc_hook)(u64 vector, u64 err, u64 rip) = 0;
void idt_set_exception_hook(int (*hook)(u64 vector, u64 err, u64 rip)) {
g_exc_hook = hook;
}
/* Stack frame pushed by isr_common in idt.S */ /* Stack frame pushed by isr_common in idt.S */
typedef struct { typedef struct {
u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi; u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi;
@@ -601,30 +661,48 @@ typedef struct {
void idt_handler(isr_frame *frame) { void idt_handler(isr_frame *frame) {
if (frame->vector < 32) { if (frame->vector < 32) {
/* Phase8: DRR 异常钩子最前——返回非 0 = 异常已被消费
* (fault 任务被杀/切换,控制权不回到日志+停机路径) */
if (g_exc_hook &&
g_exc_hook(frame->vector, frame->error_code, frame->rip)) {
return;
}
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。 /* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
* 寄存器 dump 用于定位 fault 指令的操作数来源。 */ * 寄存器 dump 用于定位 fault 指令的操作数来源。
* F1: 异常统一走莲花崩溃屏(panic_full 内部保留串口 dump +
* DRR 归档 + 帧缓冲直绘,最后 halt——不返回)。 */
u64 cr2, cr3; u64 cr2, cr3;
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2)); __asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3)); __asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
(void)cr3;
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0); INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
log_error("[IDT] exception"); panic_regs pr;
log_hex64("[IDT] vector=", frame->vector); pr.rip = frame->rip;
log_hex64("[IDT] err=", frame->error_code); pr.rsp = 0; /* isr_frame 不含 rsp(stub 内使用),置 0 */
log_hex64("[IDT] rip=", frame->rip); pr.rflags = frame->rflags;
log_hex64("[IDT] cr2=", cr2); pr.cs = frame->cs;
log_hex64("[IDT] cr3=", cr3); pr.err = frame->error_code;
log_hex64("[IDT] rax=", frame->rax); pr.vector = frame->vector;
log_hex64("[IDT] rcx=", frame->rcx); pr.cr2 = cr2;
log_hex64("[IDT] rdx=", frame->rdx); pr.rax = frame->rax;
log_hex64("[IDT] rsi=", frame->rsi); pr.rcx = frame->rcx;
log_hex64("[IDT] rdi=", frame->rdi); pr.rdx = frame->rdx;
log_hex64("[IDT] r8=", frame->r8); pr.rsi = frame->rsi;
log_hex64("[IDT] r9=", frame->r9); pr.rdi = frame->rdi;
log_hex64("[IDT] r10=", frame->r10); pr.r8 = frame->r8;
log_hex64("[IDT] r11=", frame->r11); pr.r9 = frame->r9;
log_hex64("[IDT] cs=", frame->cs); pr.r10 = frame->r10;
log_hex64("[IDT] rflags=", frame->rflags); pr.r11 = frame->r11;
for (;;) { __asm__ volatile ("cli; hlt"); } char sym[16];
sym[0] = 'U'; sym[1] = 'T'; sym[2] = 'S'; sym[3] = 'M'; sym[4] = '-';
sym[5] = 'E'; sym[6] = 'X'; sym[7] = 'C';
u32 v = frame->vector;
u32 hi = (v >> 4) & 0xF, lo = v & 0xF;
sym[8] = (char)(hi < 10 ? '0' + hi : 'A' + hi - 10);
sym[9] = (char)(lo < 10 ? '0' + lo : 'A' + lo - 10);
sym[10] = 0;
/* 异常帧上无法安全恢复执行:两种 panic 模式都 halt */
panic_halt(sym, "cpu exception", &pr);
} }
/* PIC IRQ: vectors 0x20-0x2F (32-47) /* PIC IRQ: vectors 0x20-0x2F (32-47)
-6
View File
@@ -1,6 +0,0 @@
arch/x86_64/idt.o: arch\x86_64\idt.c include\utsm\idt.h \
include\utsm\types.h include\utsm\log.h include\utsm\instr.h
include\utsm\idt.h:
include\utsm\types.h:
include\utsm\log.h:
include\utsm\instr.h:
Binary file not shown.
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
arch/x86_64/io.o: arch\x86_64\io.c include\utsm\types.h
include\utsm\types.h:
Binary file not shown.
+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);
}
-2
View File
@@ -1,2 +0,0 @@
arch/x86_64/serial.o: arch\x86_64\serial.c include\utsm\types.h
include\utsm\types.h:
Binary file not shown.
+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);
}
-9
View File
@@ -1,9 +0,0 @@
core/capability.o: core\capability.c include\utsm\capability.h \
include\utsm\types.h include\utsm\status.h include\utsm\segment.h \
include\utsm\uuid.h include\utsm\dirty.h
include\utsm\capability.h:
include\utsm\types.h:
include\utsm\status.h:
include\utsm\segment.h:
include\utsm\uuid.h:
include\utsm\dirty.h:
Binary file not shown.
-11
View File
@@ -1,11 +0,0 @@
core/dirty.o: core\dirty.c include\utsm\dirty.h include\utsm\types.h \
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
include\utsm\capability.h include\utsm\arena.h include\utsm\config.h
include\utsm\dirty.h:
include\utsm\types.h:
include\utsm\segment.h:
include\utsm\uuid.h:
include\utsm\status.h:
include\utsm\capability.h:
include\utsm\arena.h:
include\utsm\config.h:
Binary file not shown.
-10
View File
@@ -1,10 +0,0 @@
core/dmp.o: core\dmp.c include\utsm\dmp.h include\utsm\types.h \
include\utsm\segment.h include\utsm\uuid.h include\utsm\status.h \
include\utsm\capability.h include\utsm\dirty.h
include\utsm\dmp.h:
include\utsm\types.h:
include\utsm\segment.h:
include\utsm\uuid.h:
include\utsm\status.h:
include\utsm\capability.h:
include\utsm\dirty.h:
Binary file not shown.
-13
View File
@@ -1,13 +0,0 @@
core/process.o: core\process.c include\utsm\process.h \
include\utsm\types.h include\utsm\uuid.h include\utsm\capability.h \
include\utsm\status.h include\utsm\config.h include\utsm\drr.h \
include\utsm\arena.h include\utsm\log.h
include\utsm\process.h:
include\utsm\types.h:
include\utsm\uuid.h:
include\utsm\capability.h:
include\utsm\status.h:
include\utsm\config.h:
include\utsm\drr.h:
include\utsm\arena.h:
include\utsm\log.h:
Binary file not shown.
+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");
}
-14
View File
@@ -1,14 +0,0 @@
core/segment.o: core\segment.c include\utsm\segment.h \
include\utsm\types.h include\utsm\uuid.h include\utsm\status.h \
include\utsm\capability.h include\utsm\dirty.h include\utsm\process.h \
include\utsm\config.h include\utsm\arena.h include\utsm\dmp.h
include\utsm\segment.h:
include\utsm\types.h:
include\utsm\uuid.h:
include\utsm\status.h:
include\utsm\capability.h:
include\utsm\dirty.h:
include\utsm\process.h:
include\utsm\config.h:
include\utsm\arena.h:
include\utsm\dmp.h:
Binary file not shown.
+22
View File
@@ -1,7 +1,9 @@
#include <utsm/utsm.h> #include <utsm/utsm.h>
#include <utsm/segment.h> #include <utsm/segment.h>
#include <utsm/pckc.h> #include <utsm/pckc.h>
#include <utsm/crypto.h>
#include <utsm/process.h> #include <utsm/process.h>
#include <utsm/panic.h>
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/config.h> #include <utsm/config.h>
@@ -23,6 +25,26 @@ void utsm_init(void) {
utsm_pckc_init(); utsm_pckc_init();
log_info("[UTSM] pckc init ok"); log_info("[UTSM] pckc init ok");
/* SAS-R0 特色自证:PCKC 启动期 key 派生往返。派生失败或密钥全零
* = Per-CPU 密钥缓存没有真正立起来,零降级 panic(PCKC-E01)。 */
{
utsm_segment_desc probe;
for (u32 i = 0; i < sizeof(probe); i++) ((u8 *)&probe)[i] = 0;
probe.key_epoch = 1;
utsm_key_material km;
for (u32 i = 0; i < 4; i++) km.words[i] = 0;
if (utsm_pckc_get_or_derive(0xFFFFFFFFu, &probe, &km) != UTSM_OK) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time get_or_derive returned error", 0);
}
if (km.words[0] == 0 && km.words[1] == 0 && km.words[2] == 0 && km.words[3] == 0) {
panic_full("PCKC-E01 KEY DERIVE FAILED",
"PCKC boot-time derive produced all-zero key material", 0);
}
utsm_pckc_invalidate_slot(0xFFFFFFFFu); /* 探针段作废,不留缓存 */
log_info("[UTSM] pckc derive roundtrip ok");
}
utsm_process_init(); utsm_process_init();
} }
-25
View File
@@ -1,25 +0,0 @@
core/utsm_init.o: core\utsm_init.c include\utsm\utsm.h \
include\utsm\types.h include\utsm\status.h include\utsm\config.h \
include\utsm\uuid.h include\utsm\process.h include\utsm\capability.h \
include\utsm\segment.h include\utsm\dirty.h include\utsm\utrw.h \
include\utsm\vmm.h include\utsm\linux_loader.h include\utsm\ipc_shm.h \
..\utsm-ipc\ipc_proto.h include\utsm\hypercall.h include\utsm\pckc.h \
include\utsm\crypto.h include\utsm\log.h
include\utsm\utsm.h:
include\utsm\types.h:
include\utsm\status.h:
include\utsm\config.h:
include\utsm\uuid.h:
include\utsm\process.h:
include\utsm\capability.h:
include\utsm\segment.h:
include\utsm\dirty.h:
include\utsm\utrw.h:
include\utsm\vmm.h:
include\utsm\linux_loader.h:
include\utsm\ipc_shm.h:
..\utsm-ipc\ipc_proto.h:
include\utsm\hypercall.h:
include\utsm\pckc.h:
include\utsm\crypto.h:
include\utsm\log.h:
Binary file not shown.
+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> /* kdf.c — 段密钥派生(Phase 9:keyed BLAKE2b)
#include <utsm/drr.h> *
* segment_key = BLAKE2b-256(key=root_key, "UTSMSEGK"||p_uuid||s_uuid||key_epoch)
* root key 不直接加密数据;UUID 作为派生调谐因子(非明文密钥)。
* 确定性:同 (puuid, suuid, epoch) 恒得同 key —— PCKC 缓存正确性依赖此性质。
*/
static u64 mix64(u64 x) { #include <utsm/crypto.h>
x ^= x >> 30; #include <utsm/blake2b.h>
x *= 0xbf58476d1ce4e5b9ULL; #include <utsm/drr.h>
x ^= x >> 27;
x *= 0x94d049bb133111ebULL;
x ^= x >> 31;
return x;
}
void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) { void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) {
const u64 *root = drr_get_root_key(); const u64 *root = drr_get_root_key();
out_key->words[0] = mix64(root[0] ^ process_uuid.hi ^ segment_uuid.lo ^ key_epoch);
out_key->words[1] = mix64(root[1] ^ process_uuid.lo ^ segment_uuid.hi ^ (key_epoch << 1)); u8 root_le[32];
out_key->words[2] = mix64(root[2] ^ process_uuid.hi ^ segment_uuid.hi ^ (key_epoch << 7)); u8 data[8 + 8 + 8 + 8 + 8];
out_key->words[3] = mix64(root[3] ^ process_uuid.lo ^ segment_uuid.lo ^ (key_epoch << 13)); u8 digest[32];
for (int i = 0; i < 4; i++) utsm_store64_le(root_le + 8 * i, root[i]);
utsm_store64_le(data + 0, 0x4B4745534D535455ULL); /* "UTSMSEGK" LE */
utsm_store64_le(data + 8, process_uuid.hi);
utsm_store64_le(data + 16, process_uuid.lo);
utsm_store64_le(data + 24, segment_uuid.hi);
utsm_store64_le(data + 32, segment_uuid.lo);
utsm_store64_le(data + 40, key_epoch);
utsm_blake2b(data, sizeof(data), root_le, 32, digest, 32);
for (int i = 0; i < 4; i++) out_key->words[i] = utsm_load64_le(digest + 8 * i);
} }
-7
View File
@@ -1,7 +0,0 @@
crypto/kdf.o: crypto\kdf.c include\utsm\crypto.h include\utsm\types.h \
include\utsm\uuid.h include\utsm\drr.h include\utsm\config.h
include\utsm\crypto.h:
include\utsm\types.h:
include\utsm\uuid.h:
include\utsm\drr.h:
include\utsm\config.h:
Binary file not shown.
+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);
}
+19 -12
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/crypto.h>
#include <utsm/blake2b.h>
#include <utsm/config.h> #include <utsm/config.h>
static u64 next_stream_word(u64 x) { void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) {
x ^= x << 13; u32 kw[8];
x ^= x >> 7; for (int i = 0; i < 4; i++) {
x ^= x << 17; kw[2 * i] = (u32)(key->words[i] & 0xFFFFFFFFu);
return x; kw[2 * i + 1] = (u32)(key->words[i] >> 32);
} }
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) { u8 ks[64];
u64 state = key->words[0] ^ key->words[1] ^ key->words[2] ^ key->words[3] ^ tweak; utsm_chacha20_block(kw, tweak, 0, ks);
for (u64 i = 0; i < UTSM_CACHE_LINE_SIZE; i += 8) {
state = next_stream_word(state + i + 0xa5a5a5a5a5a5a5a5ULL); for (u32 i = 0; i < UTSM_CACHE_LINE_SIZE; i++) {
for (u64 j = 0; j < 8; j++) { out[i] = in[i] ^ ks[i];
out[i + j] = in[i + j] ^ (u8)(state >> (j * 8));
}
} }
} }
-6
View File
@@ -1,6 +0,0 @@
crypto/stream.o: crypto\stream.c include\utsm\crypto.h \
include\utsm\types.h include\utsm\uuid.h include\utsm\config.h
include\utsm\crypto.h:
include\utsm\types.h:
include\utsm\uuid.h:
include\utsm\config.h:
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
crypto/tweak.o: crypto\tweak.c include\utsm\crypto.h include\utsm\types.h \
include\utsm\uuid.h
include\utsm\crypto.h:
include\utsm\types.h:
include\utsm\uuid.h:
Binary file not shown.
+39 -2
View File
@@ -2,6 +2,7 @@
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/types.h> #include <utsm/types.h>
#include <utsm/arena.h> #include <utsm/arena.h>
#include <utsm/panic.h>
#include "../arch/x86_64/limine.h" #include "../arch/x86_64/limine.h"
#define ELF_MAGIC0 0x7f #define ELF_MAGIC0 0x7f
@@ -28,6 +29,10 @@
#define R_X86_64_32 10 #define R_X86_64_32 10
#define R_X86_64_32S 11 #define R_X86_64_32S 11
#define R_X86_64_PC32 2 #define R_X86_64_PC32 2
/* PLT32:lld 对(可能被覆盖的)全局函数调用生成,静态可重定位场景下
* 语义与 PC32 相同(patch = S + A - P)。多编译单元驱动(如 ath9k +
* wpa2_crypto)跨 TU 调用即为此类型。 */
#define R_X86_64_PLT32 4
void *memset(void *dst, int value, usize len); void *memset(void *dst, int value, usize len);
void *memcpy(void *dst, const void *src, usize len); void *memcpy(void *dst, const void *src, usize len);
@@ -178,10 +183,14 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) { if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) {
log_error("[DKM] ELF has no section table"); log_error("[DKM] ELF has no section table");
panic_full("DKM-E30 ELF NO SECTION TABLE",
"dkm_scan_symbols: driver ELF has no section table", 0);
return -1; return -1;
} }
if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) { if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) {
log_error("[DKM] ELF section table out of range"); log_error("[DKM] ELF section table out of range");
panic_full("DKM-E31 ELF SECTION TABLE OUT OF RANGE",
"dkm_scan_symbols: section table out of file range", 0);
return -1; return -1;
} }
@@ -202,10 +211,14 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
if (!symtab || !strtab || strtab->type != SHT_STRTAB) { if (!symtab || !strtab || strtab->type != SHT_STRTAB) {
log_error("[DKM] missing symtab/strtab"); log_error("[DKM] missing symtab/strtab");
panic_full("DKM-E32 ELF MISSING SYMTAB",
"dkm_scan_symbols: symtab/strtab missing or invalid", 0);
return -1; return -1;
} }
if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) { if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) {
log_error("[DKM] invalid symtab/strtab range"); log_error("[DKM] invalid symtab/strtab range");
panic_full("DKM-E33 ELF SYMTAB RANGE INVALID",
"dkm_scan_symbols: symtab/strtab out of file range", 0);
return -1; return -1;
} }
@@ -241,6 +254,8 @@ int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out)
return 0; return 0;
} }
log_error("[DKM] required driver symbols missing"); log_error("[DKM] required driver symbols missing");
panic_full("DKM-E34 REQUIRED DRIVER SYMBOLS MISSING",
"dkm_scan_symbols: driver_desc/init/exit symbol missing", 0);
return -1; return -1;
} }
@@ -271,12 +286,16 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
if (total_size == 0) { if (total_size == 0) {
log_error("[DKM] no allocatable sections"); log_error("[DKM] no allocatable sections");
panic_full("DKM-E35 ELF NO ALLOC SECTIONS",
"dkm_load_elf_rel: no allocatable sections", 0);
return -1; return -1;
} }
u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align); u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align);
if (!image) { if (!image) {
log_error("[DKM] failed to allocate module memory"); log_error("[DKM] failed to allocate module memory");
panic_full("DKM-E36 MODULE MEM ALLOC FAILED",
"dkm_load_elf_rel: module memory allocation failed", 0);
return -1; return -1;
} }
memset(image, 0, total_size); memset(image, 0, total_size);
@@ -348,18 +367,20 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
patch = S + A; patch = S + A;
} else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) { } else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) {
patch = (u64)((i64)(i32)(S + A)); patch = (u64)((i64)(i32)(S + A));
} else if (reloc_type == R_X86_64_PC32) { } else if (reloc_type == R_X86_64_PC32 || reloc_type == R_X86_64_PLT32) {
patch = (u64)((i64)(i32)(S + A - P)); patch = (u64)((i64)(i32)(S + A - P));
} else { } else {
log_error("[DKM] unsupported relocation type"); log_error("[DKM] unsupported relocation type");
log_hex64("[DKM] reloc type=", (u64)reloc_type); log_hex64("[DKM] reloc type=", (u64)reloc_type);
panic_full("DKM-E37 UNSUPPORTED RELOCATION",
"dkm_load_elf_rel: unsupported relocation type", 0);
return -1; return -1;
} }
/* Bounds check: use the actual write width, not always 8. */ /* Bounds check: use the actual write width, not always 8. */
u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4; u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4;
if (rela->offset + reloc_width <= target->size) { if (rela->offset + reloc_width <= target->size) {
if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S || reloc_type == R_X86_64_PC32) { if (reloc_type != R_X86_64_64) {
*(u32 *)(target_base + rela->offset) = (u32)patch; *(u32 *)(target_base + rela->offset) = (u32)patch;
} else { } else {
*(u64 *)(target_base + rela->offset) = patch; *(u64 *)(target_base + rela->offset) = patch;
@@ -373,6 +394,8 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value); const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value);
if (desc->magic != DKM_DRIVER_MAGIC) { if (desc->magic != DKM_DRIVER_MAGIC) {
log_error("[DKM] external driver_desc magic mismatch"); log_error("[DKM] external driver_desc magic mismatch");
panic_full("DKM-E38 DRIVER DESC MAGIC MISMATCH",
"dkm_load_elf_rel: external driver_desc magic mismatch", 0);
return -1; return -1;
} }
@@ -390,11 +413,17 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan
handle.load_stage = desc->stage; handle.load_stage = desc->stage;
handle.flags = desc->flags; handle.flags = desc->flags;
dkm_log_set_driver(desc->name); /* D4: 驱动自己的颜色 */
dkm_bus_set_current_provider(&handle); /* D6: 总线 provider 上下文 */
int result = init(dkm_get_kernel_api(), &handle); int result = init(dkm_get_kernel_api(), &handle);
dkm_bus_set_current_provider(0);
dkm_log_set_driver(0);
handle.init_status = result; handle.init_status = result;
if (result != 0) { if (result != 0) {
handle.state = DKM_STATE_FAILED; handle.state = DKM_STATE_FAILED;
log_error("[DKM] external driver init failed"); log_error("[DKM] external driver init failed");
panic_full("DKM-E39 EXTERNAL DRIVER INIT FAILED",
"dkm_load_elf_rel: external driver_init returned non-zero", 0);
return result; return result;
} }
handle.state = DKM_STATE_ACTIVE; handle.state = DKM_STATE_ACTIVE;
@@ -406,6 +435,8 @@ void dkm_scan_boot_modules(void) {
struct limine_module_response *response = g_module_request.response; struct limine_module_response *response = g_module_request.response;
if (!response) { if (!response) {
log_warn("[DKM] no Limine module response"); log_warn("[DKM] no Limine module response");
panic_full("DKM-E40 NO LIMINE MODULE RESPONSE",
"dkm_scan_boot_modules: no Limine module response", 0);
return; return;
} }
@@ -416,6 +447,8 @@ void dkm_scan_boot_modules(void) {
struct limine_file *file = response->modules[i]; struct limine_file *file = response->modules[i];
if (!file) { if (!file) {
log_warn("[DKM] null module entry"); log_warn("[DKM] null module entry");
panic_full("DKM-E41 NULL MODULE ENTRY",
"dkm_scan_boot_modules: null boot module entry", 0);
continue; continue;
} }
@@ -437,11 +470,15 @@ void dkm_scan_boot_modules(void) {
int load_result = dkm_load_elf_rel(file->address, file->size, &symbols); int load_result = dkm_load_elf_rel(file->address, file->size, &symbols);
if (load_result != 0) { if (load_result != 0) {
log_error("[DKM] external driver load failed"); log_error("[DKM] external driver load failed");
panic_full("DKM-E42 EXTERNAL DRIVER LOAD FAILED",
"dkm_scan_boot_modules: external driver load failed", 0);
} }
} }
} else { } else {
log_error("[DKM] module ELF64 rejected"); log_error("[DKM] module ELF64 rejected");
log_hex64("[DKM] elf status=", (u64)(i64)status); log_hex64("[DKM] elf status=", (u64)(i64)status);
panic_full("DKM-E43 MODULE ELF64 REJECTED",
"dkm_scan_boot_modules: boot module ELF64 check rejected", 0);
} }
} }
-13
View File
@@ -1,13 +0,0 @@
dkm/boot_modules.o: dkm\boot_modules.c include\utsm\dkm.h \
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
include\utsm\arena.h dkm\..\arch\x86_64\limine.h
include\utsm\dkm.h:
include\utsm\types.h:
include\utsm\dma.h:
include\utsm\block.h:
include\utsm\net.h:
include\utsm\paging.h:
include\utsm\log.h:
include\utsm\arena.h:
dkm\..\arch\x86_64\limine.h:
Binary file not shown.
+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);
}
-9
View File
@@ -1,9 +0,0 @@
dkm/console_early.o: dkm\console_early.c include\utsm\dkm.h \
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
include\utsm\net.h include\utsm\paging.h
include\utsm\dkm.h:
include\utsm\types.h:
include\utsm\dma.h:
include\utsm\block.h:
include\utsm\net.h:
include\utsm\paging.h:
Binary file not shown.
+12 -1
View File
@@ -1,5 +1,6 @@
#include <utsm/dkm.h> #include <utsm/dkm.h>
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/panic.h>
static int dkm_validate_desc(const dkm_driver_desc *desc) { static int dkm_validate_desc(const dkm_driver_desc *desc) {
if (!desc) { if (!desc) {
@@ -27,18 +28,24 @@ void dkm_init(void) {
log_info("[DKM] init ok"); log_info("[DKM] init ok");
} else { } else {
log_error("[DKM] init failed"); log_error("[DKM] init failed");
panic_full("DKM-E50 BUILTIN INIT FAILED",
"dkm_init: builtin console_early load failed", 0);
} }
} }
int dkm_load_builtin(const dkm_builtin_driver *driver) { int dkm_load_builtin(const dkm_builtin_driver *driver) {
if (!driver || !driver->desc || !driver->init) { if (!driver || !driver->desc || !driver->init) {
log_error("[DKM] invalid builtin driver"); log_error("[DKM] invalid builtin driver");
panic_full("DKM-E51 INVALID BUILTIN DRIVER",
"dkm_load_builtin: builtin driver descriptor invalid", 0);
return -1; return -1;
} }
int status = dkm_validate_desc(driver->desc); int status = dkm_validate_desc(driver->desc);
if (status != 0) { if (status != 0) {
log_error("[DKM] driver_desc rejected"); log_error("[DKM] driver_desc rejected");
panic_full("DKM-E52 DRIVER DESC REJECTED",
"dkm_load_builtin: builtin driver_desc validation failed", 0);
return status; return status;
} }
@@ -51,13 +58,17 @@ int dkm_load_builtin(const dkm_builtin_driver *driver) {
log_info("[DKM] builtin driver init begin"); log_info("[DKM] builtin driver init begin");
log_info(driver->desc->name); log_info(driver->desc->name);
dkm_log_set_driver(driver->desc->name); /* D4: 驱动自己的颜色 */
handle.state = DKM_STATE_INITING; handle.state = DKM_STATE_INITING;
status = driver->init(dkm_get_kernel_api(), &handle); status = driver->init(dkm_get_kernel_api(), &handle);
dkm_log_set_driver(0);
handle.init_status = status; handle.init_status = status;
if (status != 0) { if (status != 0) {
handle.state = DKM_STATE_FAILED; handle.state = DKM_STATE_FAILED;
log_error("[DKM] builtin driver init failed"); log_error("[DKM] builtin driver init failed");
panic_full("DKM-E53 BUILTIN DRIVER INIT FAILED",
"dkm_load_builtin: builtin driver_init returned non-zero", 0);
return status; return status;
} }
-10
View File
@@ -1,10 +0,0 @@
dkm/dkm_core.o: dkm\dkm_core.c include\utsm\dkm.h include\utsm\types.h \
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
include\utsm\paging.h include\utsm\log.h
include\utsm\dkm.h:
include\utsm\types.h:
include\utsm\dma.h:
include\utsm\block.h:
include\utsm\net.h:
include\utsm\paging.h:
include\utsm\log.h:
Binary file not shown.
+115 -5
View File
@@ -3,6 +3,10 @@
#include <utsm/idt.h> #include <utsm/idt.h>
#include <utsm/net.h> #include <utsm/net.h>
#include <utsm/mm.h> #include <utsm/mm.h>
#include <utsm/dma.h>
#include <utsm/drr.h>
#include <utsm/utrw.h>
#include <utsm/panic.h>
static void dkm_api_info(const char *msg) { static void dkm_api_info(const char *msg) {
log_info(msg); log_info(msg);
@@ -17,10 +21,9 @@ static void dkm_api_error(const char *msg) {
} }
static void dkm_api_panic(const char *msg) { static void dkm_api_panic(const char *msg) {
log_error(msg); /* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲)。
for (;;) { * fallback 模式下返回驱动,由驱动调用点走其备用路径。 */
__asm__ volatile("cli; hlt"); panic_full("DKM-PANIC", msg, 0);
}
} }
static const dkm_log_api g_log_api = { static const dkm_log_api g_log_api = {
@@ -30,6 +33,27 @@ static const dkm_log_api g_log_api = {
.panic = dkm_api_panic .panic = dkm_api_panic
}; };
/* ---- D4: DKM 彩色日志——每个驱动有自己的脾气 ----
* 加载器在 driver_init 前调用 dkm_log_set_driver(desc->name):
* 驱动名 FNV-1a hash → 8 色调色板(明亮色系,串口可读),
* 同名驱动颜色确定性可复现。init 返回后由调用方清上下文。 */
void dkm_log_set_driver(const char *name) {
static const char *palette[8] = {
"\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[35m",
"\x1b[96m", "\x1b[92m", "\x1b[93m", "\x1b[95m"
};
if (!name || !*name) {
log_set_color(0);
return;
}
u32 h = 2166136261u;
while (*name) {
h ^= (u8)*name++;
h *= 16777619u;
}
log_set_color(palette[h & 7]);
}
static dkm_kernel_api g_kernel_api = { static dkm_kernel_api g_kernel_api = {
.version = DKM_KERNEL_API_VERSION, .version = DKM_KERNEL_API_VERSION,
.size = sizeof(dkm_kernel_api), .size = sizeof(dkm_kernel_api),
@@ -57,13 +81,59 @@ static dkm_kernel_api g_kernel_api = {
.mmio = 0, .mmio = 0,
.mm_map_mmio = 0, .mm_map_mmio = 0,
.mm_unmap_mmio = 0, .mm_unmap_mmio = 0,
.register_apic_eoi = 0 .register_apic_eoi = 0,
.sched = 0,
.kapi = 0,
.bus = 0
}; };
const dkm_kernel_api *dkm_get_kernel_api(void) { const dkm_kernel_api *dkm_get_kernel_api(void) {
return &g_kernel_api; return &g_kernel_api;
} }
/* ===================================================================
* D3: 按名导出表(kapi)——自由也要有地图
* =================================================================== */
static const dkm_kapi_entry g_kapi_entries[] = {
{ "log.info", (void *)dkm_api_info },
{ "log.warn", (void *)dkm_api_warn },
{ "log.error", (void *)dkm_api_error },
{ "log.panic", (void *)dkm_api_panic },
{ "dma.alloc_pages", (void *)dma_alloc_pages },
{ "irq.register", (void *)irq_register },
{ "irq.vector_alloc", (void *)irq_vector_alloc },
{ "irq.vector_free", (void *)irq_vector_free },
{ "mm.map_mmio", (void *)mm_map_mmio },
{ "mm.unmap_mmio", (void *)mm_unmap_mmio },
{ "apic.register_eoi", (void *)idt_register_apic_eoi },
{ "drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty },
{ "drr.rollback_pages", (void *)drr_rollback_pages },
{ "utrw.debug_dump", (void *)utrw_debug_dump_page },
};
static int kapi_lookup(const char *name, void **out_fn) {
if (!name) return -1;
for (u32 i = 0; i < sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]); i++) {
const dkm_kapi_entry *e = &g_kapi_entries[i];
const char *a = e->name, *b = name;
while (*a && *a == *b) { a++; b++; }
if (*a == 0 && *b == 0) {
if (out_fn) *out_fn = e->fn;
return 0;
}
}
return -1;
}
static const dkm_kapi_table g_kapi_table = {
.magic = DKM_KAPI_MAGIC,
.version = DKM_KAPI_VERSION,
.count = sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]),
.entries = g_kapi_entries,
.lookup = kapi_lookup,
};
#include "../arch/x86_64/limine.h" #include "../arch/x86_64/limine.h"
extern volatile struct limine_rsdp_request g_rsdp_request; extern volatile struct limine_rsdp_request g_rsdp_request;
extern volatile struct limine_framebuffer_request g_fb_request; extern volatile struct limine_framebuffer_request g_fb_request;
@@ -71,10 +141,18 @@ extern volatile struct limine_module_request g_module_request;
extern volatile struct limine_hhdm_request g_hhdm_request; extern volatile struct limine_hhdm_request g_hhdm_request;
void dkm_fill_platform_info(void) { void dkm_fill_platform_info(void) {
/* D6 万物互联:总线先播种再挂载(必须先于一切驱动加载——
* driver_init 里即可 api->bus->register_service)。 */
dkm_bus_init();
g_kernel_api.bus = dkm_bus_get_api();
g_kernel_api.dma = dma_get_api(); g_kernel_api.dma = dma_get_api();
g_kernel_api.net = net_get_api(); g_kernel_api.net = net_get_api();
g_kernel_api.block = block_get_api(); g_kernel_api.block = block_get_api();
g_kernel_api.mmio = paging_get_api(); g_kernel_api.mmio = paging_get_api();
/* M4/Phase7-8: 调度器与恢复根服务表(drr 为原占位字段,此处激活) */
g_kernel_api.sched = utsm_sched_get_api();
g_kernel_api.drr = drr_get_recovery_api();
if (g_rsdp_request.response && g_rsdp_request.response->address) { if (g_rsdp_request.response && g_rsdp_request.response->address) {
g_kernel_api.rsdp_address = g_rsdp_request.response->address; g_kernel_api.rsdp_address = g_rsdp_request.response->address;
} }
@@ -100,4 +178,36 @@ void dkm_fill_platform_info(void) {
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */ /* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
g_kernel_api.irq_vector_alloc = irq_vector_alloc; g_kernel_api.irq_vector_alloc = irq_vector_alloc;
g_kernel_api.irq_vector_free = irq_vector_free; g_kernel_api.irq_vector_free = irq_vector_free;
/* D3: 按名导出表挂入 kernel_api(尾部追加,ABI 兼容) */
g_kernel_api.kapi = &g_kapi_table;
}
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DKM 能力,建立代码骨架,
* 调用即莲花 panic(DKM-E 系列)。当前无调用方(骨架)。
* =================================================================== */
/* D1: DKM 驱动热卸载(引用计数)。driver_desc 已有 NO_UNLOAD 标志位,
* 但卸载路径本身(refcount + driver_exit + 段回收)未实现。 */
void dkm_unload_driver(u32 driver_index) {
(void)driver_index;
panic_full("DKM-E01 DRIVER HOT UNLOAD NOT IMPLEMENTED",
"refcount unload path: designed (D1), not implemented", 0);
}
/* D2: 裸机器码加载器(无 ELF 头,直接映射 .text blob 到可执行页跳转)。
* SAS-R0 单地址空间下合法;加载器未实现。 */
void *dkm_load_bare_code(const void *blob, u64 size) {
(void)blob; (void)size;
panic_full("DKM-E02 BARE CODE LOADER NOT IMPLEMENTED",
"headerless machine-code loader: designed (D2), not implemented", 0);
return 0;
}
/* 多核:SIPI 唤醒 AP(apic.drv 注释预留)。BSP-only 是当前既定边界,
* 一旦被调用即 panic 而非静默单核继续。 */
void dkm_sipi_wake_ap(u32 lapic_id, u64 entry_eip) {
(void)lapic_id; (void)entry_eip;
panic_full("DKM-E03 SIPI AP WAKE NOT IMPLEMENTED",
"AP startup framework: designed, not implemented (BSP-only)", 0);
} }
-14
View File
@@ -1,14 +0,0 @@
dkm/kernel_api.o: dkm\kernel_api.c include\utsm\dkm.h \
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
include\utsm\idt.h include\utsm\mm.h dkm\..\arch\x86_64\limine.h
include\utsm\dkm.h:
include\utsm\types.h:
include\utsm\dma.h:
include\utsm\block.h:
include\utsm\net.h:
include\utsm\paging.h:
include\utsm\log.h:
include\utsm\idt.h:
include\utsm\mm.h:
dkm\..\arch\x86_64\limine.h:
Binary file not shown.
+319 -15
View File
@@ -1,5 +1,6 @@
#include <utsm/dkm.h> #include <utsm/dkm.h>
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/types.h> #include <utsm/types.h>
#include "../arch/x86_64/limine.h" #include "../arch/x86_64/limine.h"
#include "../kernel/ini_parser.h" #include "../kernel/ini_parser.h"
@@ -11,6 +12,7 @@ void dsm_load_by_manifest_ex(const ini_config *cfg);
#define DSM_MAX_STAGES 4 #define DSM_MAX_STAGES 4
#define DSM_MAX_DEPENDS 4 #define DSM_MAX_DEPENDS 4
#define DSM_MAX_PROVIDES 4 #define DSM_MAX_PROVIDES 4
#define DSM_MAX_CAPS 128
typedef struct dsm_driver_entry { typedef struct dsm_driver_entry {
char name[32]; char name[32];
@@ -23,6 +25,9 @@ typedef struct dsm_driver_entry {
u32 provide_count; u32 provide_count;
struct limine_file *boot_module; struct limine_file *boot_module;
int loaded; int loaded;
/* P0(万物互联):终态标志——已加载/被 FUCK 关闭/模块缺失/加载失败,
* 不再是本轮不动点迭代的候选。resolved 但 !loaded 的驱动不登记 provides。 */
int resolved;
} dsm_driver_entry; } dsm_driver_entry;
typedef struct dsm_stage_entry { typedef struct dsm_stage_entry {
@@ -36,6 +41,119 @@ typedef struct dsm_stage_entry {
static dsm_stage_entry g_stages[DSM_MAX_STAGES]; static dsm_stage_entry g_stages[DSM_MAX_STAGES];
static u32 g_stage_count; static u32 g_stage_count;
static int streq_static(const char *a, const char *b);
/* ===================================================================
* P0(万物互联):DSM 依赖图强制
*
* capability 集合 = 已成功加载驱动的 provides token 并集。
* 加载前校验 depends 全满足(miss → DKM-E44);
* 同 stage 内按依赖不动点迭代(环 → DKM-E47)。
* 性能不是目标(多趟扫描),正确性第一——设计定稿见
* docs/RE/万物互联与服务总线设计.md §1.5。
* =================================================================== */
static char g_caps[DSM_MAX_CAPS][32];
static u32 g_cap_count;
static int dsm_capability_satisfied(const char *token) {
for (u32 i = 0; i < g_cap_count; i++) {
if (streq_static(g_caps[i], token)) return 1;
}
return 0;
}
static void dsm_capability_add(const char *token) {
if (dsm_capability_satisfied(token)) return;
if (g_cap_count >= DSM_MAX_CAPS) {
panic_full("DKM-E48 CAPABILITY TABLE FULL",
"P0 dependency graph: g_caps overflow (DSM_MAX_CAPS)", 0);
return;
}
for (u32 i = 0; i + 1 < 32 && token[i]; i++) {
g_caps[g_cap_count][i] = token[i];
}
g_caps[g_cap_count][31] = 0;
g_cap_count++;
}
static void dsm_register_provides(const dsm_driver_entry *drv) {
for (u32 i = 0; i < drv->provide_count; i++) {
dsm_capability_add(drv->provides[i]);
}
}
/* P1(万物互联):stage>=4 的 provides 总线契约校验——driver_init 成功后,
* 每个 provides token T 必须已有 "srv.T" 或 "srv.T.*" 服务注册到总线,
* 否则 DKM-E45(零降级:provides 是对消费者/排序者的承诺,注册不到即违约)。
* stage0-3 老 14 驱动豁免:其 provides 由内核注册表兑现(ahci→block
* registry 等),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目。 */
static void dsm_verify_provides(const dsm_stage_entry *stage, const dsm_driver_entry *drv) {
if (stage->id < 4) return;
for (u32 i = 0; i < drv->provide_count; i++) {
char srv_name[40];
const char *token = drv->provides[i];
srv_name[0] = 's'; srv_name[1] = 'r'; srv_name[2] = 'v'; srv_name[3] = '.';
u32 p = 4;
while (token[p - 4] && p + 1 < sizeof(srv_name)) {
srv_name[p] = token[p - 4];
p++;
}
srv_name[p] = 0;
if (!dkm_bus_prefix_registered(srv_name)) {
log_error("[DSM] provides token not registered on bus: ");
log_info(drv->name);
log_info(token);
panic_full("DKM-E45 PROVIDES NOT REGISTERED",
"stage>=4 driver provides token has no matching srv.* bus service", 0);
}
}
}
/* depends 全满足返回 1;否则返回 0 并(可选)写出第一个未满足 token 的下标 */
static int dsm_deps_satisfied(const dsm_driver_entry *drv, u32 *missing_idx) {
for (u32 i = 0; i < drv->depend_count; i++) {
if (!dsm_capability_satisfied(drv->depends[i])) {
if (missing_idx) *missing_idx = i;
return 0;
}
}
return 1;
}
/* token 是否由本 stage 尚未 resolved 的候选提供(用于区分缺依赖 vs 成环) */
static int dsm_dep_pending_in_stage(const dsm_stage_entry *stage, const char *token) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->provide_count; i++) {
if (streq_static(drv->provides[i], token)) return 1;
}
}
return 0;
}
/* 不动点停滞:先判真缺依赖(DKM-E44),全在剩余候选里则判环(DKM-E47)。
* 零降级:两者都是编程/manifest 错误,莲花屏。 */
static void dsm_report_dependency_stall(const dsm_stage_entry *stage) {
for (u32 di = 0; di < stage->driver_count; di++) {
const dsm_driver_entry *drv = &stage->drivers[di];
if (drv->resolved) continue;
for (u32 i = 0; i < drv->depend_count; i++) {
const char *dep = drv->depends[i];
if (!dsm_capability_satisfied(dep) && !dsm_dep_pending_in_stage(stage, dep)) {
log_error("[DSM] dependency not satisfied: ");
log_info(drv->name);
log_info(dep);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"manifest depends token not provided by any loaded/pending driver", 0);
return;
}
}
}
panic_full("DKM-E47 DEPENDENCY CYCLE",
"manifest stage dependency graph has a cycle (no progress)", 0);
}
extern volatile struct limine_module_request g_module_request; extern volatile struct limine_module_request g_module_request;
static int dsm_skip_ws(const char **p) { static int dsm_skip_ws(const char **p) {
@@ -135,6 +253,7 @@ static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) {
drv->required = 1; drv->required = 1;
drv->boot_module = 0; drv->boot_module = 0;
drv->loaded = 0; drv->loaded = 0;
drv->resolved = 0;
drv->class_id = 0; drv->class_id = 0;
if (dsm_expect(p, '{') != 0) return; if (dsm_expect(p, '{') != 0) return;
@@ -266,7 +385,12 @@ static int dsm_parse_manifest(const char *text, u64 size) {
dsm_expect(&p, ':'); dsm_expect(&p, ':');
if (streq_static(key, "stages")) { if (streq_static(key, "stages")) {
if (dsm_expect(&p, '[') != 0) return -1; if (dsm_expect(&p, '[') != 0) {
/* 严格错误策略插桩:stage 解析失败零降级(DKM-E08) */
panic_full("DKM-E08 MANIFEST STAGES PARSE FAILED",
"dsm_parse_manifest: stages array malformed", 0);
return -1;
}
while (p < end && *p) { while (p < end && *p) {
dsm_skip_ws(&p); dsm_skip_ws(&p);
if (*p == ']') { p++; break; } if (*p == ']') { p++; break; }
@@ -290,15 +414,23 @@ void dsm_load_by_manifest(void) {
dsm_load_by_manifest_ex(NULL); dsm_load_by_manifest_ex(NULL);
} }
void dsm_load_by_manifest_ex(const ini_config *cfg) { static int dsm_ensure_manifest(void) {
struct limine_module_response *rsp = g_module_request.response; struct limine_module_response *rsp;
struct limine_file *manifest_file;
u64 i;
if (g_stage_count) return 0;
rsp = g_module_request.response;
if (!rsp) { if (!rsp) {
log_warn("[DSM] no Limine module response"); log_warn("[DSM] no Limine module response");
return; /* 严格错误策略插桩:manifest 缺失零降级(DKM-E09) */
panic_full("DKM-E09 NO LIMINE MODULE RESPONSE",
"dsm_ensure_manifest: no Limine module response", 0);
return -1;
} }
struct limine_file *manifest_file = 0; manifest_file = 0;
for (u64 i = 0; i < rsp->module_count; i++) { for (i = 0; i < rsp->module_count; i++) {
struct limine_file *f = rsp->modules[i]; struct limine_file *f = rsp->modules[i];
if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) { if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) {
manifest_file = f; manifest_file = f;
@@ -308,70 +440,242 @@ void dsm_load_by_manifest_ex(const ini_config *cfg) {
if (!manifest_file) { if (!manifest_file) {
log_warn("[DSM] manifest not found in boot modules"); log_warn("[DSM] manifest not found in boot modules");
return; /* 严格错误策略插桩:manifest 缺失零降级(DKM-E10) */
panic_full("DKM-E10 MANIFEST NOT FOUND",
"dsm_ensure_manifest: dkm:manifest boot module missing", 0);
return -1;
} }
log_info("[DSM] parsing manifest"); log_info("[DSM] parsing manifest");
dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size); dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size);
log_info("[DSM] manifest parsed"); log_info("[DSM] manifest parsed");
log_hex64("[DSM] stages=", (u64)g_stage_count); log_hex64("[DSM] stages=", (u64)g_stage_count);
return 0;
}
static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max_stage_id) {
if (dsm_ensure_manifest() != 0) return;
for (u32 si = 0; si < g_stage_count; si++) { for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si]; dsm_stage_entry *stage = &g_stages[si];
if (limit_stage && stage->id > max_stage_id) continue;
log_info("[DSM] stage begin"); log_info("[DSM] stage begin");
log_info(stage->name); log_info(stage->name);
log_hex64("[DSM] driver count=", stage->driver_count); log_hex64("[DSM] driver count=", stage->driver_count);
/* P0:同 stage 内按依赖不动点迭代——每趟加载 deps 已满足的候选,
* 直到全部 resolved;停滞即 DKM-E44/E47(见 dsm_report_dependency_stall)。
* 多趟扫描 O(n²·趟数),性能刻意不管,正确性第一。 */
u32 pending = 0;
for (u32 di = 0; di < stage->driver_count; di++) {
if (!stage->drivers[di].resolved) pending++;
}
while (pending > 0) {
u32 pending_before = pending;
for (u32 di = 0; di < stage->driver_count; di++) { for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di]; dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
/* === FUCK [drivers] 驱动开关检查 === */ if (drv->resolved) continue;
/* === FUCK [drivers] 驱动开关检查 ===
* 关闭即退出候选(provides 不登记,依赖它的模块将 DKM-E44)。 */
if (cfg) { if (cfg) {
int enabled = ini_get_bool(cfg, "drivers", drv->name, 1); int enabled = ini_get_bool(cfg, "drivers", drv->name, 1);
if (!enabled) { if (!enabled) {
log_info("[DSM] driver disabled by FUCK: "); log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name); log_info(drv->name);
continue; /* 跳过此驱动 */ drv->resolved = 1;
pending--;
continue;
} }
} }
/* P0:depends 校验——暂不满足先跳过,等本 stage 后续候选登记
* provides 后下一趟重试;整体停滞时统一判罚(零降级)。 */
if (!dsm_deps_satisfied(drv, 0)) continue;
drv->boot_module = dsm_find_boot_module(drv->path); drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) { if (!drv->boot_module) {
log_info("[DSM] module not found"); log_info("[DSM] module not found");
log_info(drv->name); log_info(drv->name);
drv->resolved = 1;
pending--;
if (drv->required && stage->required) { if (drv->required && stage->required) {
log_error("[DSM] required module missing, abort stage"); /* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */
panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING",
"required driver boot module not found in manifest stage", 0);
break; break;
} }
continue; continue;
} }
struct limine_file *f = drv->boot_module; f = drv->boot_module;
log_info("[DSM] loading driver"); log_info("[DSM] loading driver");
log_info(drv->name); log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) { if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed"); log_error("[DSM] ELF check failed");
if (drv->required && stage->required) {
panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID",
"required driver boot module failed ELF64 check", 0);
}
drv->resolved = 1;
pending--;
continue; continue;
} }
struct dkm_symbol_scan symbols;
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) { if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed"); log_error("[DSM] symbol scan failed");
if (drv->required && stage->required) {
panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED",
"required driver symbol scan failed", 0);
}
drv->resolved = 1;
pending--;
continue; continue;
} }
int result = dkm_load_elf_rel(f->address, f->size, &symbols); result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) { if (result != 0) {
log_error("[DSM] load failed"); log_error("[DSM] load failed");
if (drv->required && stage->required) { if (drv->required && stage->required) {
log_error("[DSM] required driver failed, abort stage"); /* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */
break; panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED",
"required driver relocation/load failed", 0);
} }
drv->resolved = 1;
pending--;
} else { } else {
drv->loaded = 1; drv->loaded = 1;
drv->resolved = 1;
pending--;
/* P0:加载成功才登记 provides(disabled/missing/failed 一律不登记) */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
}
}
if (pending > 0 && pending == pending_before) {
/* 一整趟零进展:真缺依赖(DKM-E44)或依赖环(DKM-E47) */
dsm_report_dependency_stall(stage);
/* fallback:停滞的候选全部放弃(不登记 provides),结束本 stage */
for (u32 di = 0; di < stage->driver_count; di++) {
if (stage->drivers[di].resolved) continue;
log_warn("[DSM] fallback: driver skipped (unresolved deps): ");
log_info(stage->drivers[di].name);
stage->drivers[di].resolved = 1;
}
pending = 0;
} }
} }
log_info("[DSM] stage end"); log_info("[DSM] stage end");
} }
} }
void dsm_load_by_manifest_upto(const ini_config *cfg, u32 max_stage_id) {
dsm_load_stage_range(cfg, 1, max_stage_id);
}
/* 按名加载公共实现。strict_deps=1:depends 校验强制(DKM-E44)——供
* kern.dkm.load_by_name(万物互联 P3)等常规路径使用;
* strict_deps=0:顺序豁免版——唯一调用方是 kernel/main.c VMM 路径的
* pci+ahci 预加载(必须先于 apic/stage0,否则 guest 陷入 IDTR=0
* VM-exit 风暴,见 main.c 注释)。设计内顺序豁免,登记于设计文档 §6。 */
static void dsm_load_named_impl(const ini_config *cfg, const char *name, int strict_deps) {
if (!name || dsm_ensure_manifest() != 0) {
/* 严格错误策略插桩:按名加载 manifest 缺失零降级(DKM-E11) */
panic_full("DKM-E11 NAMED LOAD MANIFEST MISSING",
"dsm_load_named: null name or manifest unavailable", 0);
return;
}
for (u32 si = 0; si < g_stage_count; si++) {
dsm_stage_entry *stage = &g_stages[si];
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (!streq_static(drv->name, name)) continue;
if (drv->loaded) return;
if (cfg && !ini_get_bool(cfg, "drivers", drv->name, 1)) {
log_info("[DSM] driver disabled by FUCK: ");
log_info(drv->name);
return;
}
/* P0:按名加载同样强制依赖(此时无同 stage 重试机会,miss 即判罚) */
if (strict_deps && !dsm_deps_satisfied(drv, 0)) {
log_error("[DSM] named driver dependency not satisfied: ");
log_info(drv->name);
panic_full("DKM-E44 DEPENDENCY NOT SATISFIED",
"dsm_load_named: depends token not provided by any loaded driver", 0);
return;
}
drv->boot_module = dsm_find_boot_module(drv->path);
if (!drv->boot_module) {
log_warn("[DSM] named module not found");
log_info(name);
/* 严格错误策略插桩:named 驱动缺模块零降级(DKM-E12) */
panic_full("DKM-E12 NAMED DRIVER MODULE MISSING",
"dsm_load_named: named driver boot module not found", 0);
return;
}
f = drv->boot_module;
log_info("[DSM] loading named driver");
log_info(drv->name);
if (dkm_check_elf64(f->address, f->size) != 0) {
log_error("[DSM] ELF check failed");
/* 严格错误策略插桩:named 驱动 ELF 校验失败零降级(DKM-E13) */
panic_full("DKM-E13 NAMED DRIVER ELF INVALID",
"dsm_load_named: named driver failed ELF64 check", 0);
return;
}
if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) {
log_error("[DSM] symbol scan failed");
/* 严格错误策略插桩:named 驱动符号扫描失败零降级(DKM-E14) */
panic_full("DKM-E14 NAMED DRIVER SYMBOL SCAN FAILED",
"dsm_load_named: named driver symbol scan failed", 0);
return;
}
result = dkm_load_elf_rel(f->address, f->size, &symbols);
if (result != 0) {
log_error("[DSM] load failed");
/* 严格错误策略插桩:named 驱动加载失败零降级(DKM-E15) */
panic_full("DKM-E15 NAMED DRIVER LOAD FAILED",
"dsm_load_named: named driver relocation/load failed", 0);
return;
}
drv->loaded = 1;
drv->resolved = 1;
/* P0:按名加载成功同样登记 provides */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
return;
}
}
log_warn("[DSM] named driver not in manifest");
log_info(name);
/* 严格错误策略插桩:named 驱动不在 manifest 零降级(DKM-E16) */
panic_full("DKM-E16 NAMED DRIVER NOT IN MANIFEST",
"dsm_load_named: named driver not found in manifest", 0);
}
void dsm_load_named(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 1);
}
void dsm_load_named_boot(const ini_config *cfg, const char *name) {
dsm_load_named_impl(cfg, name, 0);
}
void dsm_load_by_manifest_ex(const ini_config *cfg) {
dsm_load_stage_range(cfg, 0, 0);
}
-13
View File
@@ -1,13 +0,0 @@
dkm/manifest.o: dkm\manifest.c include\utsm\dkm.h include\utsm\types.h \
include\utsm\dma.h include\utsm\block.h include\utsm\net.h \
include\utsm\paging.h include\utsm\log.h dkm\..\arch\x86_64\limine.h \
dkm\..\kernel\ini_parser.h
include\utsm\dkm.h:
include\utsm\types.h:
include\utsm\dma.h:
include\utsm\block.h:
include\utsm\net.h:
include\utsm\paging.h:
include\utsm\log.h:
dkm\..\arch\x86_64\limine.h:
dkm\..\kernel\ini_parser.h:
Binary file not shown.
+527 -6
View File
@@ -10,6 +10,15 @@
#include <utsm/drr.h> #include <utsm/drr.h>
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/dma.h>
#include <utsm/segment.h>
#include <utsm/config.h>
#include <utsm/dkm.h>
#include <utsm/crypto.h>
#include <utsm/blake2b.h>
extern void outb(u16 port, u8 value);
/* ---- 内部内存操作(不依赖 libc) ---- */ /* ---- 内部内存操作(不依赖 libc) ---- */
static void drr_memzero(void *dst, u64 n) { static void drr_memzero(void *dst, u64 n) {
@@ -71,14 +80,118 @@ u64 drr_crc64(const void *data, u64 len) {
/* ---- 全局 DRR 状态 ---- */ /* ---- 全局 DRR 状态 ---- */
static drr_state g_drr; static drr_state g_drr;
/* ---- Root key(最终应从安全启动链获取) ---- */ /* ---- Phase 8: 快照区 / 看门狗 / 恢复行为控制 ---- */
static const u64 g_root_key[4] = { #define DRR_SNAP_MAGIC 0x445252534E415021ULL /* "DRRSNAP!" */
#define DRR_SNAP_VERSION 1u
typedef struct {
u64 magic;
u32 version;
u32 capacity; /* 条目容量(page0 空间决定) */
u32 used;
u32 generation;
u64 crc;
} drr_snap_header;
static dkm_dma_buffer g_snap_region;
static int g_snap_ok = 0;
static u32 g_snap_capacity = 0;
static int g_reboot_enabled = 1;
static void (*g_sched_fault_cb)(u32 task_slot) = 0;
typedef struct {
u32 task_slot; /* 0xFFFFFFFF = 空闲 */
u32 timeout_ticks;
u64 last_kick_tick;
} drr_watchdog_entry;
static drr_watchdog_entry g_drr_wd[DRR_WATCHDOG_MAX];
static drr_snap_header *snap_header(void) {
return (drr_snap_header *)g_snap_region.virt;
}
static drr_ckpt_page_entry *snap_entries(void) {
return (drr_ckpt_page_entry *)((u8 *)g_snap_region.virt + 64);
}
static u8 *snap_page_ptr(u32 idx) {
return (u8 *)g_snap_region.virt + 4096ULL * (1ULL + (u64)idx);
}
/* ---- Root key(Phase 9: RDRAND + TSC jitter 混合,boot 生成一次)
* 生成一次后固化:drr_init 可重入(emergency reset),root key 不换,
* 否则旧 segment key 全部失效。持久化(sealed disk region)留待后续。 ---- */
static u64 g_root_key_storage[4];
static int g_root_key_ready = 0;
static const u64 g_root_key_fallback[4] = {
0x4452525554534D31ULL, /* "DRRUTSM1" */ 0x4452525554534D31ULL, /* "DRRUTSM1" */
0xFEEDFACECAFEF00DULL, 0xFEEDFACECAFEF00DULL,
0x0123456789ABCDEFULL, 0x0123456789ABCDEFULL,
0xFEDCBA9876543210ULL 0xFEDCBA9876543210ULL
}; };
static u64 drr_tsc(void) {
u64 v;
__asm__ volatile("rdtsc" : "=A"(v));
return v;
}
static int drr_have_rdrand(void) {
u32 a, b, c, d;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(1u), "c"(0u));
return (int)((c >> 30) & 1u);
}
static int drr_rdrand64(u64 *out) {
u8 ok;
u64 v;
__asm__ volatile("rdrand %0; setc %1" : "=r"(v), "=qm"(ok) :: "cc");
if (!ok) return 0;
*out = v;
return 1;
}
static void drr_root_key_generate(void) {
if (g_root_key_ready) {
g_drr.root_key = g_root_key_storage;
return;
}
int rdrand_ok = 1;
if (drr_have_rdrand()) {
for (int i = 0; i < 4; i++) {
if (!drr_rdrand64(&g_root_key_storage[i])) {
rdrand_ok = 0;
break;
}
}
} else {
rdrand_ok = 0;
}
if (rdrand_ok) {
/* 纵深:混入 TSC jitter,防 RDRAND 单点失效 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] ^= drr_tsc() * 0x9e3779b97f4a7c15ULL;
}
} else {
/* 无 RDRAND:常量基底 + 多轮 TSC jitter 混合 */
for (int i = 0; i < 4; i++) {
g_root_key_storage[i] = g_root_key_fallback[i];
}
for (int r = 0; r < 16; r++) {
u64 j = drr_tsc();
for (int i = 0; i < 4; i++) {
j = (j ^ drr_tsc()) * 0x9e3779b97f4a7c15ULL;
g_root_key_storage[i] ^= j;
for (volatile int d = 0; d < 50; d++) { }
}
}
}
g_root_key_ready = 1;
g_drr.root_key = g_root_key_storage;
log_info("[DRR] root key generated");
log_hex64("[DRR] rdrand=", (u64)rdrand_ok);
}
/* ---- Emergency Pool 内部分配 ---- */ /* ---- Emergency Pool 内部分配 ---- */
static void *pool_alloc_internal(u64 size, u64 alignment) { static void *pool_alloc_internal(u64 size, u64 alignment) {
u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset; u64 current = (u64)&g_drr.pool[0] + g_drr.pool_offset;
@@ -105,25 +218,58 @@ void drr_emergency_reset(void) {
drr_init(); drr_init();
} }
/* ---- R3b: FUCK 配置接线(drr_apply_config 设置,drr_init 消费) ---- */
static u64 g_cfg_pool_bytes; /* 0 = 编译期默认 */
static u64 g_cfg_log_size; /* 0 = 编译期默认 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size) {
g_cfg_pool_bytes = pool_kb ? pool_kb * 1024ULL : 0;
g_cfg_log_size = recovery_log_size;
if (g_cfg_pool_bytes > DRR_EMERGENCY_POOL_SIZE) {
log_warn("[DRR] pool_kb exceeds compile-time pool, clamped");
log_hex64("[DRR] compile-time=", DRR_EMERGENCY_POOL_SIZE);
g_cfg_pool_bytes = DRR_EMERGENCY_POOL_SIZE;
}
}
/* ---- 初始化 ---- */ /* ---- 初始化 ---- */
void drr_init(void) { void drr_init(void) {
log_info("[DRR] init begin"); log_info("[DRR] init begin");
drr_memzero(&g_drr, sizeof(g_drr)); drr_memzero(&g_drr, sizeof(g_drr));
/* R3b: 池上限 = min(编译期池, FUCK 配置);小于最小子缓冲布局时回退默认 */
g_drr.pool_offset = 0; g_drr.pool_offset = 0;
g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE; g_drr.pool_limit = DRR_EMERGENCY_POOL_SIZE;
g_drr.root_key = g_root_key; {
const u64 MIN_LAYOUT =
DRR_EMERGENCY_STACK_SIZE + DRR_RECOVERY_LOG_SIZE +
DRR_CKPT_META_BUF_SIZE + DRR_CRASH_BUF_SIZE + DRR_META_SLAB_SIZE;
if (g_cfg_pool_bytes && g_cfg_pool_bytes >= MIN_LAYOUT) {
g_drr.pool_limit = g_cfg_pool_bytes;
} else if (g_cfg_pool_bytes) {
log_warn("[DRR] pool_kb below minimum sub-buffer layout, default kept");
log_hex64("[DRR] minimum=", MIN_LAYOUT);
}
}
drr_root_key_generate();
g_drr.recovery_generation = 1; g_drr.recovery_generation = 1;
g_drr.heartbeat_interval = 1000; g_drr.heartbeat_interval = 1000;
g_drr.active_slot = 0; /* A */ g_drr.active_slot = 0; /* A */
/* 分配 Emergency Pool 子缓冲区 */ /* 分配 Emergency Pool 子缓冲区(recovery log 大小可由 FUCK 配置) */
g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64); g_drr.stack_base = (u8 *)pool_alloc_internal(DRR_EMERGENCY_STACK_SIZE, 64);
g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE; g_drr.stack_size = DRR_EMERGENCY_STACK_SIZE;
g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */ g_drr.stack_ptr = DRR_EMERGENCY_STACK_SIZE; /* 栈从顶部向下增长 */
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(DRR_RECOVERY_LOG_SIZE, 64); u64 log_size = DRR_RECOVERY_LOG_SIZE;
g_drr.log_capacity = DRR_RECOVERY_LOG_SIZE / sizeof(drr_log_entry); if (g_cfg_log_size && g_cfg_log_size >= sizeof(drr_log_entry) &&
g_cfg_log_size <= g_drr.pool_limit - g_drr.pool_offset) {
log_size = g_cfg_log_size;
} else if (g_cfg_log_size) {
log_warn("[DRR] recovery_log_size out of range, default kept");
}
g_drr.log_base = (drr_log_entry *)pool_alloc_internal(log_size, 64);
g_drr.log_capacity = (u32)(log_size / sizeof(drr_log_entry));
g_drr.log_count = 0; g_drr.log_count = 0;
g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64); g_drr.ckpt_meta_buf = (drr_checkpoint_meta *)pool_alloc_internal(DRR_CKPT_META_BUF_SIZE, 64);
@@ -142,9 +288,102 @@ void drr_init(void) {
g_drr.ckpt_b.version = DRR_CKPT_VERSION; g_drr.ckpt_b.version = DRR_CKPT_VERSION;
g_drr.initialized = 1; g_drr.initialized = 1;
log_hex64("[DRR] pool_limit=", g_drr.pool_limit);
log_hex64("[DRR] log_capacity=", (u64)g_drr.log_capacity);
/* ---- Phase 8: 快照区(dma 页,独立于普通堆;失败降级 metadata-only) ---- */
g_snap_ok = 0;
g_snap_capacity = 0;
g_reboot_enabled = 1;
g_sched_fault_cb = 0;
for (int wdi = 0; wdi < DRR_WATCHDOG_MAX; wdi++) {
g_drr_wd[wdi].task_slot = 0xFFFFFFFFu;
g_drr_wd[wdi].timeout_ticks = 0;
g_drr_wd[wdi].last_kick_tick = 0;
}
{
/* page0 = 头 + 条目数组,其后 128 页快照数据(共 129 页) */
dkm_dma_buffer buf;
if (dma_alloc_pages(1 + 128, 4096, 0xFFFFFFFFULL, &buf) == 0 && buf.virt) {
g_snap_region = buf;
u32 cap = (u32)((4096 - 64) / sizeof(drr_ckpt_page_entry));
drr_snap_header *h = (drr_snap_header *)buf.virt;
drr_memzero(h, 4096);
h->magic = DRR_SNAP_MAGIC;
h->version = DRR_SNAP_VERSION;
h->capacity = cap;
h->used = 0;
g_snap_capacity = cap;
g_snap_ok = 1;
log_info("[DRR] snapshot region ok");
log_hex64("[DRR] pages=", 1 + 128);
} else {
/* 严格错误策略插桩:快照区是 DRR 页回滚的前提,缺失即降级
* metadata-only——按零降级原则直接 panic(DRR-E01)。 */
panic_full("DRR-E01 SNAPSHOT REGION ALLOC FAILED",
"snapshot dma pages unavailable; metadata-only mode is forbidden", 0);
}
}
log_info("[DRR] init ok"); log_info("[DRR] init ok");
} }
/* ===================================================================
* 严格错误策略插桩:设计已定但未实现的 DRR 能力,建立代码骨架,
* 调用即 panic(DRR-E2x 系列)。当前无调用方(骨架)。
* =================================================================== */
/* Root key 持久化(sealed disk region):drr_init 可重入要求 root key
* 跨 emergency reset 固化。当前实现走内存 fallback key(见
* drr_root_key_generate),sealed disk 尚未落盘。 */
void drr_root_key_seal_to_disk(void) {
panic_full("DRR-E02 ROOT KEY SEAL NOT IMPLEMENTED",
"sealed disk region for root key persistence: designed, not implemented", 0);
}
/* task_kill crypto erase:任务被杀时 key_epoch++ 并擦除其密钥材料。
* 当前 task_exit 只回收 slot,不做 crypto erase。 */
void drr_task_kill_crypto_erase(u32 task_slot) {
(void)task_slot;
panic_full("DRR-E03 TASK KILL CRYPTO ERASE NOT IMPLEMENTED",
"key_epoch++ erase on task kill: designed, not implemented", 0);
}
/* segment/system 级回滚入口(R4 遗留):页级回滚已实现,
* segment/system 级接口的调用方尚未接线。 */
void drr_rollback_segment(u32 seg_slot) {
(void)seg_slot;
panic_full("DRR-E04 SEGMENT ROLLBACK NOT IMPLEMENTED",
"segment-level rollback: designed (R4), not verified/implemented", 0);
}
void drr_rollback_system(void) {
panic_full("DRR-E05 SYSTEM ROLLBACK NOT IMPLEMENTED",
"system-level rollback: designed (R4), not verified/implemented", 0);
}
/* DKM 驱动 recovery ops(DKM README 设计:quiesce/reset/reinit/dump_state)。
* 驱动侧从未提供该回调表,DRR 亦未在恢复路径调用。 */
typedef struct {
u32 magic; /* 'D','R','O','P' */
u32 version;
void (*quiesce)(void *drv);
void (*reset)(void *drv);
void (*reinit)(void *drv);
void (*dump_state)(void *drv);
} dkm_driver_recovery_ops;
void drr_driver_recovery_dispatch(const dkm_driver_recovery_ops *ops, void *drv) {
(void)drv;
if (!ops) {
panic_full("DRR-E06 DRIVER RECOVERY OPS MISSING",
"driver did not provide dkm_driver_recovery_ops", 0);
return;
}
panic_full("DRR-E07 DRIVER RECOVERY NOT IMPLEMENTED",
"quiesce/reset/reinit dispatch: designed (DKM README), not implemented", 0);
}
const u64 *drr_get_root_key(void) { const u64 *drr_get_root_key(void) {
return g_drr.root_key; return g_drr.root_key;
} }
@@ -172,6 +411,30 @@ void drr_report_fault(const char *reason) {
g_drr.recovery_generation++; g_drr.recovery_generation++;
} }
/* ---- F1: 崩溃归档(panic_full 调用;crash_buf 写 symbol+msg+rip 简表) ----
* 返回 0=已归档;-1=DRR 未初始化(crash_buf 不可用)。 */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs) {
if (!g_drr.initialized || !g_drr.crash_buf || g_drr.crash_buf_size == 0) {
return -1;
}
u64 i = 0;
u8 *b = g_drr.crash_buf;
u64 cap = g_drr.crash_buf_size - 1;
/* 头部标记 */
const char *magic = "CRASH1";
for (const char *s = magic; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = symbol ? symbol : "UNKNOWN"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i++] = '|';
for (const char *s = msg ? msg : "-"; *s && i < cap; s++) b[i++] = (u8)*s;
b[i] = 0;
/* recovery generation 前进(归档计数) */
g_drr.recovery_generation++;
log_error("[DRR] crash archived");
log_hex64("[DRR] generation=", g_drr.recovery_generation);
return 0;
}
/* ---- Recovery Log ---- */ /* ---- Recovery Log ---- */
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) { void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) {
if (!g_drr.initialized || !g_drr.log_base) return; if (!g_drr.initialized || !g_drr.log_base) return;
@@ -314,3 +577,261 @@ void drr_heartbeat_tick(void) {
u64 drr_heartbeat_get(void) { u64 drr_heartbeat_get(void) {
return g_drr.heartbeat_counter; return g_drr.heartbeat_counter;
} }
/* ===================================================================
* Phase 8: 真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* =================================================================== */
int drr_ckpt_snapshot_dirty(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2; /* metadata-only 模式不支持页快照 */
drr_checkpoint_begin(DRR_CKPT_DIRTY_PAGE);
drr_snap_header *h = snap_header();
drr_ckpt_page_entry *entries = snap_entries();
u32 used = 0;
for (u32 slot = 0; slot < g_utsm_max_segments; slot++) {
utsm_segment_desc *desc = utsm_get_segment(slot);
if (!desc || desc->state != UTSM_SEG_ACTIVE) continue;
desc->state = UTSM_SEG_CHECKPOINTING;
for (u32 s = 0; s < desc->dirty_shard_count && desc->dirty_shards; s++) {
utsm_dirty_shard *shard = &desc->dirty_shards[s];
if (!shard->bitmap) continue;
u64 words = (shard->page_count + 63) / 64;
for (u64 w = 0; w < words && used < g_snap_capacity; w++) {
u64 word = shard->bitmap[w];
while (word && used < g_snap_capacity) {
u64 bit = (u64)__builtin_ctzll(word);
u64 page = w * 64 + bit;
u8 *src = desc->cipher_base + page * UTSM_PAGE_SIZE;
u8 *dst = snap_page_ptr(used);
drr_memcpy(dst, src, UTSM_PAGE_SIZE);
entries[used].seg_slot = slot;
entries[used].page_index = (u32)page;
entries[used].cipher_vaddr = (u64)src;
entries[used].crc = drr_crc64(dst, UTSM_PAGE_SIZE);
entries[used].key_epoch = desc->key_epoch;
entries[used].mac = utsm_page_mac(g_drr.root_key, slot, page,
desc->key_epoch, dst);
entries[used].region_idx = used;
entries[used].used = 1;
used++;
/* 快照成功才清脏位;区满时保留脏位(部分提交,下轮重试) */
shard->bitmap[w] &= ~(1ULL << bit);
if (shard->dirty_count > 0) shard->dirty_count--;
word &= ~(1ULL << bit);
}
}
shard->last_checkpoint_epoch = g_drr.recovery_generation;
}
desc->state = UTSM_SEG_ACTIVE;
desc->checkpoint_generation = g_drr.recovery_generation;
if (used >= g_snap_capacity) break;
}
/* 区域头(crc 不含自身字段) */
h->used = used;
h->generation = (u32)g_drr.recovery_generation;
h->crc = drr_crc64(h, sizeof(drr_snap_header) - sizeof(u64));
/* 元数据 + A/B 原子切换(begin 已选好 inactive slot,同一表达式取回) */
drr_checkpoint_meta *target = (g_drr.active_slot == 0) ? &g_drr.ckpt_b : &g_drr.ckpt_a;
target->dirty_page_count = used;
/* Phase 9: mac_root = keyed BLAKE2b 链(root_key 派生,绑定全部页 MAC) */
{
u8 kb[32];
u8 chain[64];
for (int i = 0; i < 4; i++) utsm_store64_le(kb + 8 * i, g_drr.root_key[i]);
utsm_blake2b_ctx mc;
utsm_blake2b_init(&mc, 64, kb, 32);
for (u32 i = 0; i < used; i++) {
u8 rec[8 + 8 + 8 + 8];
utsm_store64_le(rec + 0, 0x544F4F524D535455ULL); /* "UTSMROOT" LE */
utsm_store64_le(rec + 8, ((u64)entries[i].seg_slot) |
((u64)entries[i].page_index << 32));
utsm_store64_le(rec + 16, entries[i].key_epoch);
utsm_store64_le(rec + 24, entries[i].mac);
utsm_blake2b_update(&mc, rec, sizeof(rec));
}
utsm_blake2b_final(&mc, chain);
target->mac_root = utsm_load64_le(chain);
}
target->segment_count = 0;
drr_checkpoint_finish();
log_info("[DRR] ckpt snapshot ok");
log_hex64("[DRR] pages=", used);
return (int)used;
}
int drr_rollback_pages(void) {
if (!g_drr.initialized) return -1;
if (!g_snap_ok) return -2;
drr_checkpoint_meta meta;
if (drr_checkpoint_recover(&meta) != 0) return -3;
if (meta.type != DRR_CKPT_DIRTY_PAGE) return -4;
drr_snap_header *h = snap_header();
if (h->magic != DRR_SNAP_MAGIC || h->version != DRR_SNAP_VERSION) return -5;
u32 used = h->used;
if (used > h->capacity) used = h->capacity;
drr_ckpt_page_entry *entries = snap_entries();
/* 触碰段先置 RECOVERING */
for (u32 i = 0; i < used; i++) {
utsm_segment_desc *d = utsm_get_segment(entries[i].seg_slot);
if (d && d->state == UTSM_SEG_ACTIVE) d->state = UTSM_SEG_RECOVERING;
}
int failed = 0;
u32 restored = 0;
for (u32 i = 0; i < used; i++) {
drr_ckpt_page_entry *e = &entries[i];
u8 *snap = snap_page_ptr(e->region_idx);
utsm_segment_desc *d = utsm_get_segment(e->seg_slot);
if (drr_crc64(snap, UTSM_PAGE_SIZE) != e->crc) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback crc fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
/* Phase 9: 页 MAC 校验(伪造/跨纪元重放/区域篡改在此被拒) */
u64 mac = utsm_page_mac(g_drr.root_key, e->seg_slot, e->page_index,
e->key_epoch, snap);
if (mac != e->mac) {
failed = 1;
if (d) d->state = UTSM_SEG_POISONED;
log_error("[DRR] rollback mac fail");
log_hex64("[DRR] seg_slot=", e->seg_slot);
log_hex64("[DRR] page=", e->page_index);
continue;
}
drr_memcpy((void *)e->cipher_vaddr, snap, UTSM_PAGE_SIZE);
restored++;
/* 清除该页脏位(P2 之类的回写不再被误判为未 checkpoint) */
if (d && d->dirty_shard_count > 0 && d->dirty_shards && d->dirty_shards[0].bitmap) {
utsm_dirty_shard *sh = &d->dirty_shards[0];
u64 page = e->page_index;
if (page < sh->page_count && (sh->bitmap[page / 64] & (1ULL << (page % 64)))) {
sh->bitmap[page / 64] &= ~(1ULL << (page % 64));
if (sh->dirty_count > 0) sh->dirty_count--;
}
}
if (d && d->state == UTSM_SEG_RECOVERING) d->state = UTSM_SEG_ACTIVE;
}
log_info("[DRR] rollback done");
log_hex64("[DRR] pages=", restored);
return failed ? -2 : (int)restored;
}
void drr_system_rollback(const char *reason) {
log_error("[DRR] SYSTEM ROLLBACK");
if (reason) log_error(reason);
log_hex64("[DRR] generation=", g_drr.recovery_generation);
log_hex64("[DRR] heartbeat=", g_drr.heartbeat_counter);
log_error("[DRR] rebooting (8042 reset)");
for (;;) {
__asm__ volatile("cli");
outb(0x64, 0xFE); /* 8042 复位(A20 gate 已无关紧要) */
for (volatile u32 i = 0; i < 1000000u; i++) { }
__asm__ volatile("hlt");
}
}
void drr_handle_task_fault(u32 task_slot, const char *reason) {
log_error("[DRR] FAULT");
if (reason) log_error(reason);
log_hex64("[DRR] task_slot=", task_slot);
drr_report_fault(reason ? reason : "task fault");
drr_log_write_intent(task_slot, 0, 0);
int pages = drr_rollback_pages();
log_hex64("[DRR] rollback_pages=", (u64)(i64)pages);
if (g_sched_fault_cb) {
g_sched_fault_cb(task_slot); /* 可能不返回(当前任务被杀+切换) */
drr_log_write_commit(task_slot, 0, 0);
return;
}
if (g_reboot_enabled) {
drr_system_rollback(reason ? reason : "task fault (no sched cb)");
}
/* reboot disabled(selftest):仅留日志 */
}
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks) {
if (!g_drr.initialized || timeout_ticks == 0) return -1;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == 0xFFFFFFFFu) {
g_drr_wd[i].task_slot = task_slot;
g_drr_wd[i].timeout_ticks = timeout_ticks;
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return 0;
}
}
return -2;
}
int drr_watchdog_unregister(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].task_slot = 0xFFFFFFFFu;
return 0;
}
}
return -1;
}
void drr_watchdog_kick(u32 task_slot) {
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
if (g_drr_wd[i].task_slot == task_slot) {
g_drr_wd[i].last_kick_tick = g_drr.heartbeat_counter;
return;
}
}
}
void drr_watchdog_check(void) {
if (!g_drr.initialized) return;
for (int i = 0; i < DRR_WATCHDOG_MAX; i++) {
u32 slot = g_drr_wd[i].task_slot;
if (slot == 0xFFFFFFFFu) continue;
u64 now = g_drr.heartbeat_counter;
if (now - g_drr_wd[i].last_kick_tick > g_drr_wd[i].timeout_ticks) {
g_drr_wd[i].last_kick_tick = now; /* 防重复触发 */
drr_handle_task_fault(slot, "watchdog timeout");
}
}
}
void drr_set_reboot_enabled(int enabled) {
g_reboot_enabled = enabled;
}
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot)) {
g_sched_fault_cb = cb;
}
/* kernel_api.drr 服务表(dkm_fill_platform_info 挂入) */
static const drr_recovery_api g_drr_api = {
.report_fault = drr_report_fault,
.heartbeat_get = drr_heartbeat_get,
.emergency_alloc = drr_emergency_alloc,
.ckpt_snapshot_dirty = drr_ckpt_snapshot_dirty,
.rollback_pages = drr_rollback_pages
};
const drr_recovery_api *drr_get_recovery_api(void) {
return &g_drr_api;
}
-6
View File
@@ -1,6 +0,0 @@
drr/drr_stub.o: drr\drr_stub.c include\utsm\drr.h include\utsm\types.h \
include\utsm\config.h include\utsm\log.h
include\utsm\drr.h:
include\utsm\types.h:
include\utsm\config.h:
include\utsm\log.h:
Binary file not shown.
+1
View File
@@ -2,6 +2,7 @@
#define UTSM_ASSERT_H #define UTSM_ASSERT_H
#include <utsm/log.h> #include <utsm/log.h>
#include <utsm/panic.h>
#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0) #define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0)
+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); u64 (*sector_size)(u32 index);
const char *(*device_name)(u32 index); const char *(*device_name)(u32 index);
int (*set_write_fn)(u32 index, dkm_block_write_fn fn); int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
u64 (*sector_count)(u32 index); /* 0x38: tail-append, 老内核该槽为 0 表示未知 */
} dkm_block_api; } dkm_block_api;
void block_init(void); void block_init(void);
+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); u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch);
void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out); void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out);
/* ===================================================================
* Phase 9 扩展:真实加密原语服务(尾部追加,ABI 兼容)
* stream = ChaCha20 block(RFC 8439),hash/KDF/MAC = BLAKE2b(RFC 7693)
* =================================================================== */
/* ChaCha20 block function:64B keystream。
* counter=0 用法下 nonce=tweak 即可保证 (key,nonce) 唯一。 */
void utsm_chacha20_block(const u32 key_words[8], u64 nonce, u64 counter, u8 out[64]);
/* 4KB 密文页 MAC:keyed BLAKE2b(key=root_key, domain||seg_slot||page||epoch||data),
* 截断 64 位。root_key 为 4×u64(DRR root key)。 */
u64 utsm_page_mac(const u64 *root_key, u32 seg_slot, u64 page_index, u64 key_epoch, const void *page);
#endif #endif
+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 */ #define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
/* =================================================================== /* ===================================================================
* L1 — Winux-Kate 语义色值 * L1 — 桌面语义色值(白色调主题,去 winux-Kate 深色风)
* 底色白/浅灰,组件白卡片 + 浅灰描边,文字深灰,强调色深板岩。
* =================================================================== */ * =================================================================== */
#define KS_BG_PRIMARY DP_KATE_BG #define KS_BG_PRIMARY 0xFFF3F4F6u /* 页面底:浅灰 */
#define KS_BG_SECONDARY DP_KATE_PANEL_SOLID #define KS_BG_SECONDARY 0xFFFFFFFFu /* 面板:白 */
#define KS_BG_TERTIARY DP_KATE_BG2 #define KS_BG_TERTIARY 0xFFE9ECF0u /* 三级底:稍深浅灰 */
#define KS_TEXT_PRIMARY DP_KATE_TEXT #define KS_TEXT_PRIMARY 0xFF1A1A1Au /* 主文字:近黑 */
#define KS_TEXT_DIM DP_KATE_TEXT_DIM #define KS_TEXT_DIM 0xFF6B7280u /* 次文字:中灰 */
#define KS_TEXT_INVERT DP_KATE_BG #define KS_TEXT_INVERT 0xFFFFFFFFu /* accent 上的文字:白 */
#define KS_ACCENT DP_KATE_ACCENT #define KS_ACCENT 0xFF111827u /* 强调:深板岩(白底上高对比) */
#define KS_ACCENT2 DP_KATE_ACCENT2 #define KS_ACCENT2 0xFF4B5563u /* 次强调:中板岩 */
#define KS_ACCENT_DIM DP_KATE_ACCENT_DIM_SOLID #define KS_ACCENT_DIM 0xFFE5E7EBu /* 强调淡底:浅灰 */
#define KS_BORDER DP_KATE_PANEL_BORDER_SOLID #define KS_BORDER 0xFFD1D5DBu /* 边框:浅灰 */
#define KS_BORDER_FOCUS DP_KATE_ACCENT #define KS_BORDER_FOCUS 0xFF111827u /* 聚焦边框:深板岩 */
#define KS_BORDER_DIM 0xFF1A2840u /* 暗边框 */ #define KS_BORDER_DIM 0xFFE5E7EBu /* 暗边框:极浅灰 */
#define KS_DANGER DP_KATE_DANGER #define KS_DANGER 0xFFDC2626u /* 红 */
#define KS_WARN DP_KATE_WARN #define KS_WARN 0xFFD97706u /* 琥珀 */
/* =================================================================== /* ===================================================================
* Winux-Kate 布局常量(源自 theme.css / pages.css) * Winux-Kate 布局常量(源自 theme.css / pages.css)
+126 -1
View File
@@ -33,6 +33,76 @@
struct dkm_kernel_api; struct dkm_kernel_api;
struct dkm_driver_handle; struct dkm_driver_handle;
/* ===================================================================
* D3: 内核 API 按名导出表(kapi)
*
* "自由也要有地图。" 固定偏移 struct ABI 之外的按名查找层:
* 驱动/用户态可枚举内核服务并按名取函数指针。
* 通过 kernel_api 尾部追加的 ->kapi 指针访问(ABI 兼容)。
* =================================================================== */
typedef struct {
const char *name; /* 如 "log.info"、"dma.alloc_pages" */
void *fn; /* 服务函数指针(签名见对应服务头) */
} dkm_kapi_entry;
typedef struct {
u32 magic; /* DKM_KAPI_MAGIC */
u32 version; /* DKM_KAPI_VERSION */
u32 count; /* entries 数量 */
const dkm_kapi_entry *entries;
/* 按名查找:命中返回 0 并写 out_fn;未命中 -1 */
int (*lookup)(const char *name, void **out_fn);
} dkm_kapi_table;
#define DKM_KAPI_MAGIC 0x4b415049u /* "KAPI" */
#define DKM_KAPI_VERSION 1u
/* ===================================================================
* D6 万物互联:dkm_bus 双向服务总线
*
* kapi 是内核→模块的单向只读导出表;bus 把它双向化:
* 任何模块在 driver_init 里注册 srv.* 服务,任何模块按名消费。
* 命名空间:kern.* 仅内核可注册,srv.* 仅模块可注册(BUS-E01)。
* 失败策略:lookup miss = BUS-E04 panic(硬依赖未满足=编程错误);
* probe miss = 返回 -1(能力缺席=设计内双路径,白名单登记)。
* 生命周期:driver_init = 注册服务;"运行"也是服务(srv.<mod>.run)。
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#define DKM_BUS_MAGIC 0x53425553u /* "SBUS" */
#define DKM_BUS_VERSION 1u
#define DKM_BUS_MAX_SERVICES 128
#define DKM_BUS_MAX_NAME 48
#define DKM_BUS_F_KERNEL 1u /* 内核注册,不可注销 */
#define DKM_BUS_F_NO_UNLOAD 2u /* provider 声明 NO_UNLOAD,注销即 BUS-E05 */
typedef struct dkm_bus_service {
char name[DKM_BUS_MAX_NAME]; /* NUL 终止;必须带命名空间前缀 */
u32 version; /* 服务契约版本,消费方校验 */
u32 flags; /* DKM_BUS_F_* */
void *fn; /* 服务入口,签名由各服务契约定义 */
const struct dkm_driver_desc *provider; /* NULL = 内核;存 desc 而非
* handle——loader 的 handle 是栈
* 局部变量会悬垂,desc 在驱动
* 镜像内存里持久且每驱动唯一 */
} dkm_bus_service;
typedef struct dkm_bus_api {
u32 magic;
u32 version;
/* 注册:name 必须带命名空间前缀且与调用方身份匹配(BUS-E01)、
* 重名 BUS-E02、表满 BUS-E03——全部 panic,成功返回 0。 */
int (*register_service)(const char *name, u32 version, void *fn);
/* 注销:仅 provider 本人可注销;F_KERNEL/F_NO_UNLOAD 或不存在 → BUS-E05。 */
int (*unregister_service)(const char *name);
/* 必存在查找:未命中 → BUS-E04 panic,绝不返回 NULL 伪装成功。 */
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
/* 可选探测:未命中返回 -1;调用方必须显式处理缺席(白名单登记)。 */
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void); /* 诊断 / /dev/services */
const dkm_bus_service *(*at)(u32 index); /* 按 index 枚举 */
} dkm_bus_api;
typedef struct dkm_driver_desc { typedef struct dkm_driver_desc {
u32 magic; u32 magic;
u16 abi_version; u16 abi_version;
@@ -63,6 +133,24 @@ typedef struct dkm_driver_desc {
typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle); typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle);
typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle); typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle);
/* DRR 恢复根服务表(Phase8;驱动经 kernel_api->drr 使用) */
typedef struct drr_recovery_api {
void (*report_fault)(const char *reason);
u64 (*heartbeat_get)(void);
void *(*emergency_alloc)(u64 size, u64 alignment);
int (*ckpt_snapshot_dirty)(void);
int (*rollback_pages)(void);
} drr_recovery_api;
/* SAS-R0-PCQ 调度器服务表(Phase7;驱动经 kernel_api->sched 使用) */
typedef struct utsm_sched_api {
u64 (*uptime_ns)(void);
void (*yield)(void);
u32 (*current_task_slot)(void);
int (*task_create)(u32 process_slot, u32 priority, void (*entry)(void *), void *arg);
void (*sleep_ms)(u32 ms);
} utsm_sched_api;
typedef struct dkm_log_api { typedef struct dkm_log_api {
void (*info)(const char *msg); void (*info)(const char *msg);
void (*warn)(const char *msg); void (*warn)(const char *msg);
@@ -84,7 +172,7 @@ typedef struct dkm_kernel_api {
const void *vfs; const void *vfs;
const dkm_net_api *net; const dkm_net_api *net;
const void *timer; const void *timer;
const void *drr; const drr_recovery_api *drr; /* 恢复根服务(原占位 void* 改具体类型,布局不变) */
const void *rsdp_address; /* ACPI RSDP physical address */ const void *rsdp_address; /* ACPI RSDP physical address */
const void *fb_address; /* framebuffer base */ const void *fb_address; /* framebuffer base */
@@ -111,6 +199,16 @@ typedef struct dkm_kernel_api {
* 接口已接受向量命名空间,无需新注册 ABI)。 */ * 接口已接受向量命名空间,无需新注册 ABI)。 */
int (*irq_vector_alloc)(void); int (*irq_vector_alloc)(void);
void (*irq_vector_free)(int vector); void (*irq_vector_free)(int vector);
/* M4/Phase7-8: 调度器服务(尾部追加,ABI 兼容;drr 服务见上方原占位字段)。
* 驱动可用:task_create/uptime/sleep/current_task_slot。
* DSK 以 api+0xA8 硬偏移读 block 的约定不受影响(仅尾部追加)。 */
const utsm_sched_api *sched;
/* D3: 按名导出表(尾部追加,ABI 兼容)。驱动/用户态枚举内核服务并
* 按名取函数指针;固定偏移 struct 字段不受影响。 */
const dkm_kapi_table *kapi;
/* D6 万物互联:双向服务总线(尾部追加,ABI 兼容)。kern.* 与 srv.* 按名
* 注册-消费;driver_init = 注册服务的地方。 */
const dkm_bus_api *bus;
} dkm_kernel_api; } dkm_kernel_api;
typedef enum dkm_driver_state { typedef enum dkm_driver_state {
@@ -136,12 +234,39 @@ typedef struct dkm_builtin_driver {
} dkm_builtin_driver; } dkm_builtin_driver;
void dkm_init(void); void dkm_init(void);
/* D4: 设置当前驱动日志颜色上下文(driver_init 前设置,返回后清 0) */
void dkm_log_set_driver(const char *name);
int dkm_load_builtin(const dkm_builtin_driver *driver); int dkm_load_builtin(const dkm_builtin_driver *driver);
void dkm_scan_boot_modules(void); void dkm_scan_boot_modules(void);
void dsm_load_by_manifest(void); void dsm_load_by_manifest(void);
/* P0(万物互联):按名加载。dsm_load_named 严格版(depends 校验强制,供
* kern.dkm.load_by_name 等常规路径);dsm_load_named_boot 顺序豁免版
* (唯一合法调用方 = kernel/main.c VMM 路径 pci+ahci 预加载,先于 apic,
* 否则 guest 陷入 IDTR=0 VM-exit 风暴——设计内豁免,见
* docs/RE/万物互联与服务总线设计.md §6)。
* 签名(ini_config 定义在 kernel/ini_parser.h,此处避免头依赖):
* void dsm_load_named(const ini_config *cfg, const char *name);
* void dsm_load_named_boot(const ini_config *cfg, const char *name);
* 调用方自行前向声明(同 kernel/main.c 既有模式)。 */
void dkm_fill_platform_info(void); void dkm_fill_platform_info(void);
const dkm_kernel_api *dkm_get_kernel_api(void); const dkm_kernel_api *dkm_get_kernel_api(void);
/* 服务表实现(drr_stub.c / sched/sched.c 提供,fill 时挂入 kernel_api) */
const struct drr_recovery_api *drr_get_recovery_api(void);
const struct utsm_sched_api *utsm_sched_get_api(void);
/* D6 万物互联:服务总线(dkm/bus.c 提供)。dkm_bus_init 播种 kern.*
* 并挂入 kernel_api(dkm_fill_platform_info 内调用,先于一切驱动加载);
* loader 在 driver_init 前后 set/clear provider 上下文(仿 D4 颜色上下文),
* kern.* 只能在 provider==NULL 时注册。 */
void dkm_bus_init(void);
const dkm_bus_api *dkm_bus_get_api(void);
void dkm_bus_set_current_provider(struct dkm_driver_handle *h);
/* loader 专用:token T 是否已有 "prefix" 或 "prefix.*" 服务注册
* (stage>=4 provides 校验用,DKM-E45)。命中返回 1。 */
int dkm_bus_prefix_registered(const char *prefix);
/* exposed for manifest parser */ /* exposed for manifest parser */
struct dkm_symbol_scan { struct dkm_symbol_scan {
const void *driver_desc; const void *driver_desc;
+57
View File
@@ -102,9 +102,15 @@ typedef struct {
/* ---- 公共接口 ---- */ /* ---- 公共接口 ---- */
void drr_init(void); void drr_init(void);
/* R3b: FUCK [utsm] 配置接线(须在 drr_init 前调用;0 = 用编译期默认)。
* pool_kb 钳位到编译期静态池上限(Emergency Pool 为编译期数组,运行期只
* 能缩小可用量);recovery_log_size 钳位到池内剩余容量。 */
void drr_apply_config(u64 pool_kb, u64 recovery_log_size);
const u64 *drr_get_root_key(void); const u64 *drr_get_root_key(void);
u64 drr_recovery_generation(void); u64 drr_recovery_generation(void);
void drr_report_fault(const char *reason); void drr_report_fault(const char *reason);
/* F1: 崩溃归档(crash_buf 写入;返回 0=已归档,-1=DRR 未初始化) */
int drr_crash_archive(const char *symbol, const char *msg, const void *regs);
void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len); void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len);
void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len); void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len);
@@ -124,4 +130,55 @@ u64 drr_heartbeat_get(void);
/* CRC64 */ /* CRC64 */
u64 drr_crc64(const void *data, u64 len); u64 drr_crc64(const void *data, u64 len);
/* ===================================================================
* Phase 8 扩展:真实脏页快照 / 页级回滚 / 看门狗 / system rollback
* (尾部追加,不动既有结构与语义)
* =================================================================== */
/* 快照区条目(快照区第 0 页:头(64B) + 条目数组顺序存放) */
typedef struct {
u32 seg_slot; /* utsm_get_segment 的 slot */
u32 page_index; /* 段内页号 */
u64 cipher_vaddr; /* 段内目标页虚拟地址(HHDM,回滚目标) */
u64 crc; /* 快照数据 CRC64(drr_crc64,区域完整性) */
u32 region_idx; /* 快照数据所在页序号(区域第 1+idx 页) */
u32 used;
/* Phase 9: 真实 MAC(keyed BLAKE2b,绑定 seg/page/epoch) */
u64 key_epoch; /* 快照时刻段 key_epoch(MAC 上下文 + 防跨纪元重放) */
u64 mac; /* 页 MAC(utsm_page_mac,截断 64 位) */
} drr_ckpt_page_entry;
/* 真实脏页快照:扫描全部 ACTIVE 段的 dirty shard 位图,逐脏页复制到
* 快照区(先快照后清位;区满则部分提交且不清位),随后走 A/B checkpoint
* 原子切换。O(dirty_shards + dirty_pages)。
* 返回快照页数(>=0);负数失败(未初始化/快照区不可用)。 */
int drr_ckpt_snapshot_dirty(void);
/* 页级回滚:按 active slot 元数据 + 快照区逐页 CRC 校验后恢复密文页
* 并清除对应脏位;触碰段状态 RECOVERING → ACTIVE,CRC 失败段 POISONED。
* 返回恢复页数(>=0);失败 -2(任一页 CRC 不符)。 */
int drr_rollback_pages(void);
/* 系统级回滚:crash 留痕 + 8042 复位(不返回)。
* Emergency Pool 耗尽 / 双槽皆坏 / 无任务可切换时调用。 */
void drr_system_rollback(const char *reason);
/* 统一 fault 入口:report_fault → 页级回滚 → sched fault 回调(可能不
* 返回);回调缺失且 reboot 使能时 → system rollback。 */
void drr_handle_task_fault(u32 task_slot, const char *reason);
/* 看门狗(独立于调度器:由 tick handler 直接调用 check,非调度任务) */
#define DRR_WATCHDOG_MAX 8
int drr_watchdog_register(u32 task_slot, u32 timeout_ticks);
int drr_watchdog_unregister(u32 task_slot);
void drr_watchdog_kick(u32 task_slot);
void drr_watchdog_check(void);
/* 恢复行为控制:关闭后 fault 只留日志不真重启(selftest 用) */
void drr_set_reboot_enabled(int enabled);
/* 调度器 fault 回调(sched enable 时注入;缺失时 fault → system rollback)。
* DRR 独立性:对调度器只依赖这一个函数指针。 */
void drr_set_sched_fault_cb(void (*cb)(u32 task_slot));
#endif /* UTSM_DRR_H */ #endif /* UTSM_DRR_H */
+20
View File
@@ -10,10 +10,30 @@
#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1) #define DSK_BOOT_FLAG_DKM_READY (1ULL << 1)
#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2) #define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2)
#define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3) #define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3)
/* FUCK [panic] mode=fallback:DSK/desktop/login/FirstInit 的 *_fatal 记录后返回,
* 调用点走备用路径(见 utsm/panic.h)。未置位 = strict。 */
#define DSK_BOOT_FLAG_PANIC_FALLBACK (1ULL << 4)
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */ /* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */ #define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
/* ---- 网络租约信息(netman 写入,DSK 持有存储,经 reserved[7] 指针传递) ----
* reserved[0..6] 已被占用(user.conf/login/dev_mode/pe/lxc/probe),
* 仅 reserved[7] 空闲,故以其存放指向本结构的指针。 */
#define NET_LEASE_MAGIC 0x4E45544Cu /* "NETL" */
#define NET_LEASE_F_VALID (1u << 0) /* ip/gateway/dns 有效 */
#define NET_LEASE_F_STATIC (1u << 1) /* 静态配置(非 DHCP) */
typedef struct net_lease_info {
u32 magic; /* NET_LEASE_MAGIC */
u32 flags; /* NET_LEASE_F_* */
u32 ip; /* guest IP(网络序) */
u32 gateway; /* 网关(网络序) */
u32 dns; /* DNS(网络序) */
u32 server; /* DHCP server(网络序,调试) */
u32 reserved[2];
} net_lease_info;
typedef struct probe_info { typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC */ u32 magic; /* PROBE_INFO_MAGIC */
u32 version; /* ABI 版本,当前 1 */ u32 version; /* ABI 版本,当前 1 */
+11 -2
View File
@@ -8,8 +8,9 @@
#define EPT_PAGE_SIZE 4096ULL #define EPT_PAGE_SIZE 4096ULL
#define EPT_ENTRIES 512ULL #define EPT_ENTRIES 512ULL
/* EPTP 构造:bits [5:0]=memory type, bits [7:6]=walk length-1, bits [51:12]=PML4 HPA */ /* EPTP 构造:bits [2:0]=memory type, bits [5:3]=walk length-1, bit 6=AD, bits [51:12]=PML4 HPA
#define EPTP_WALK_LEN_4 (3ULL << 6) /* 4 级页表 */ * P8.4 Fix: walk length was at bits [7:6] (wrong), should be bits [5:3] per Intel SDM. */
#define EPTP_WALK_LEN_4 (3ULL << 3) /* 4 级页表 (3 = 4-1) at bits [5:3] */
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB) #define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */ /* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
@@ -30,6 +31,14 @@ int ept_identity_map(u64 gpa, u64 size, u64 flags);
* Handles 4KB pages and 2MB large pages. */ * Handles 4KB pages and 2MB large pages. */
u64 ept_gpa_to_hpa(u64 gpa); u64 ept_gpa_to_hpa(u64 gpa);
/* Rewrite an existing EPT leaf (4K or 2MB) as RWX+WB+Ignore PAT.
* Returns 0 if a leaf was found and rewritten, -1 if unmapped.
* Used to recover from nested-KVM EPT misconfig (guest PAT vs EPT type). */
int ept_repair_leaf(u64 gpa, u64 flags);
/* Log PML4/PDPT/PD/PT entries for a GPA (misconfig diagnostics). */
void ept_log_walk(u64 gpa);
/* Map a 2MB large page in EPT (PD-level direct mapping). /* Map a 2MB large page in EPT (PD-level direct mapping).
* GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*. * GPA and HPA must be 2MB-aligned. memtype = EPT_MEMORY_TYPE_*.
* Required for real hardware MTRR compliance. */ * Required for real hardware MTRR compliance. */
+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 */
+21
View File
@@ -45,4 +45,25 @@ void idt_register_apic_eoi(void (*eoi_fn)(void));
int irq_vector_alloc(void); int irq_vector_alloc(void);
void irq_vector_free(int vector); void irq_vector_free(int vector);
/* 显式 EOI(Phase7: 调度器 tick 等可能切换任务的中断路径用)。
* 在任何可能换栈的工作之前完成 EOI,防止 EOI 悬挂一个时间片。
* 序列与 idt_handler 默认路径一致: LAPIC 钩子 → 8259 EOI(按向量范围)。
* 参数兼容双命名空间(同 irq_register): 0-15=ISA IRQ, 0x20+=向量。 */
void idt_irq_eoi(u8 vector);
/* CPU 异常钩子(Phase8: DRR 任务级 fault 接管)。
* 在 idt_handler 的 vector<32 分支最前调用: 返回非 0 = 异常已被消费
* (fault 任务被杀/切换,控制权不回到日志+停机路径);返回 0 = 走原
* 全量日志 + cli;hlt 路径(DSK 阶段行为不变)。传 NULL 注销。 */
typedef int (*idt_exception_hook_t)(u64 vector, u64 error_code, u64 rip);
void idt_set_exception_hook(idt_exception_hook_t hook);
/* P1(万物互联): PIC IMR 位操作 —— kern.irq.unmask/mask 总线服务后端。
* 仅 ISA IRQ 0-15(越界 → IRQ-E01 panic 零降级);
* unmask IRQ8-15 自动放开主片 IRQ2 级联。
* apic_route=1 时 PIC 已全屏蔽(IOAPIC 接管),本操作无效果——该路径的
* 解屏蔽随 P6 真 IRQ 输入走 IOAPIC 接线。 */
void pic_irq_unmask(u8 irq);
void pic_irq_mask(u8 irq);
#endif #endif
+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 */
+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 */
+85
View File
@@ -21,6 +21,34 @@
#define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */ #define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */
/* VSCode Phase 3: virtio-gpu scanout 查询结果。
* 镜像 virtio_mmio.h 的 virtio_gpu_scanout_info 字段布局,保持公共 ABI 自洽
* (不强制消费者 include virtio_mmio.h)。必须在 linux_compat_service 之前
* 定义,因为服务表中有指向此类型的函数指针。 */
struct linux_compat_scanout_info {
void *host_vaddr;
u32 width;
u32 height;
u32 stride;
u32 dirty;
u32 enabled;
/* Phase 7: 累积 dirty rect(scanout 坐标系并集,查询随 dirty 清零)。
* dirty=0 时全为 0;desktop 据此只 blit 变化区域(尾部追加,ABI 兼容)。 */
u32 dirty_x;
u32 dirty_y;
u32 dirty_w;
u32 dirty_h;
/* P7.6: hardware cursor state(尾部追加,ABI 兼容)。 */
u32 cursor_visible;
u32 cursor_x;
u32 cursor_y;
u32 cursor_hot_x;
u32 cursor_hot_y;
u32 cursor_w;
u32 cursor_h;
void *cursor_bitmap;
};
/* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */ /* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */
typedef struct linux_compat_service { typedef struct linux_compat_service {
u64 magic; /* LINUX_COMPAT_MAGIC */ u64 magic; /* LINUX_COMPAT_MAGIC */
@@ -93,6 +121,46 @@ typedef struct linux_compat_service {
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */ * 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
int (*file_push)(const char *fat32_name, const char *linux_path, int (*file_push)(const char *fat32_name, const char *linux_path,
u64 *out_size); u64 *out_size);
/* VSCode Phase 2: 输入转发到 Linux guest(virtio-input 键盘+鼠标)。
* 由 desktop 在 IDE attached 时调用,把 Deshab PS/2 事件注入 guest。
* 事件排到 pending ring,由 vmexit poll 路径排空到 eventq + 注入 IRQ。
*
* input_forward_keyboard(code, value)
* code : linux keycode(KEY_A=30 等,见 linux/input-event-codes.h)
* value : 0=release, 1=press, 2=repeat
*
* input_forward_mouse(dx, dy, buttons)
* dx, dy : 相对位移(像素差,可为负)
* buttons : 按钮位图 bit0=左, bit1=右, bit2=中(内部与上次状态比较,
* 仅变化的按钮产生 press/release 事件;末尾自动追加 SYN) */
void (*input_forward_keyboard)(u16 code, u32 value);
void (*input_forward_mouse)(i32 dx, i32 dy, u8 buttons);
/* VSCode Phase 3: 查询 virtio-gpu scanout 状态(desktop IDE blit 用)。
* 在 IDE attached 时由 desktop 周期调用:若 dirty=1,把 host_vaddr 指向的
* scanout 影子缓冲(width×height,stride 字节/行,XRGB8888)blit 到
* framebuffer 的 IDE host 区域,然后重新查询以清零 dirty。
* out->host_vaddr : scanout 影子缓冲 host 虚拟地址(SAS-R0 下可直接解引用)
* out->enabled : 1 = scanout 已绑定 2D resource(X server 已 SET_SCANOUT)
* out->dirty : 1 = 自上次查询有新帧(查询会清零该位)
* 返回 0 成功(enabled=0 时其余字段为零),-1 后端未就绪(无 surface pool)。 */
int (*gpu_get_scanout_info)(struct linux_compat_scanout_info *out);
/* VSCode Phase 5: 异步执行 Linux 程序(daemon fork+setsid+detach)。
* 与 exec 语义相同,但 daemon 在 spawn 成功后立即回 EXEC_EXIT(0),
* 不捕获 stdout、不等待进程退出——用于启动 X server / VSCode 等
* 长生命周期 GUI 进程而不阻塞 UTSM 侧。子进程 stdio 重定向到
* /dev/null,环境自带 LD_LIBRARY_PATH=/usr/lib/deshab 与 DISPLAY=:0。
* 返回 0 = 已成功 spawn(不代表程序后续存活),负值失败
* (-1=服务不可用, -2=IPC 发送失败, -3=linux_resume 失败,
* -4=无响应, -5=daemon 报告 spawn 失败)。 */
int (*exec_async)(const char *path, int argc, const char *const *argv);
/* 不发送 IPC,只跑一段 Linux vCPU(HLT 或 preemption timeslice)。
* KDE/Plasma 全屏循环用这个唤醒 async 子进程,避免再 exec sleep。
* 返回 0 成功,负值同 linux_resume。 */
int (*run_slice)(void);
} linux_compat_service; } linux_compat_service;
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) ===== /* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
@@ -111,4 +179,21 @@ const linux_compat_service *linux_compat_get_service(void);
/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */ /* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */
void linux_compat_init(void); void linux_compat_init(void);
/* 同步 FAT32 lib 目录下所有 .so 文件到 Linux guest。
* guest_lib_path : guest 端库目录路径(如 "/usr/lib/deshab")
* 调用时机:linux_compat_init 成功后。失败不阻断启动。
* 返回 0 成功,负值失败。 */
int lxc_sync_lib_dir(const char *guest_lib_path);
/* VSCode Phase 5: 把 VSCode tarball(Limine boot module,路径含 "vscode")
* 安装到 Linux guest 的 /opt/vscode(经 overlay 持久化在 extra-rootfs 卷)。
* 流程:已安装标记探测(/opt/vscode/bin/code 可执行则跳过)→
* 流式推送 module 字节到 guest /tmp/vscode.tar.gz →
* mkdir -p /opt/vscode → tar 解包(--strip-components=1,失败回退不解层)→
* 清理 /tmp tarball → 验证 /opt/vscode/bin/code。
* 调用时机:linux_compat_init 成功后(kernel main,FUCK [compat] vscode_install=1)。
* 返回 0 成功或已安装;负值失败(-1 服务不可用, -11 无 tarball module,
* -12 推送失败, -13 解包失败, -14 验证失败)。所有失败均不阻断启动。 */
int lxc_vscode_install(void);
#endif /* UTSM_LINUX_COMPAT_H */ #endif /* UTSM_LINUX_COMPAT_H */
+97 -18
View File
@@ -49,6 +49,9 @@ struct linux_setup_header {
/* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */ /* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */
#define BOOT_PARAMS_SIZE 4096 #define BOOT_PARAMS_SIZE 4096
#define SETUP_HEADER_OFFSET 0x1f1 #define SETUP_HEADER_OFFSET 0x1f1
#define BOOT_PARAMS_ACPI_RSDP_ADDR_OFF 0x070
#define BOOT_PARAMS_RSDP_COPY_OFF 0x800
#define BOOT_PARAMS_RSDP_COPY_SIZE 36
/* Key field offsets within bzImage / boot_params */ /* Key field offsets within bzImage / boot_params */
#define BZ_BOOT_FLAG_OFF 0x1fe #define BZ_BOOT_FLAG_OFF 0x1fe
@@ -88,11 +91,10 @@ struct linux_setup_header {
#define E820_TYPE_UNUSABLE 5 #define E820_TYPE_UNUSABLE 5
#define E820_TYPE_PMEM 7 #define E820_TYPE_PMEM 7
/* e820_table entry in boot_params at offset 0x2e0 (actually 0x2d0 in older, /* e820_table is at 0x2d0 in Linux 6.6 boot_params (arch/x86/include/uapi/asm/bootparam.h).
* 0x2e0 in newer). We use 0x2e0 and the e820_entries count at 0x1e8. * e820_entries count is at 0x1e8. Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
* Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
#define E820_ENTRIES_OFFSET 0x1e8 #define E820_ENTRIES_OFFSET 0x1e8
#define E820_TABLE_OFFSET 0x2e0 #define E820_TABLE_OFFSET 0x2d0
#define E820_MAX_ENTRIES 128 #define E820_MAX_ENTRIES 128
#define E820_ENTRY_SIZE 20 #define E820_ENTRY_SIZE 20
@@ -103,7 +105,9 @@ struct linux_setup_header {
* 0x01000000-0x02FFFFFF : Linux kernel image * 0x01000000-0x02FFFFFF : Linux kernel image
* 0x03000000-0x03FFFFFF : Linux initrd * 0x03000000-0x03FFFFFF : Linux initrd
* 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW) * 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW)
* 0x05000000-MAX : Linux general RAM * 0x04100000-0x04FFFFFF : Graphics surface pool (16MB, VSCode integration Phase 1)
* 0x05000000-0x5FFFFFFF : Linux general RAM (~1.4GB, expanded Phase 1 for VSCode)
* 0xF4000000-0xF4007000 : virtio-mmio devices (8 slots, expanded Phase 1)
*/ */
#define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */ #define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */
#define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */ #define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */
@@ -118,8 +122,19 @@ struct linux_setup_header {
#define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */ #define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */
#define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */ #define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
/* General RAM starts after IPC region */ /* VSCode integration Phase 1: Graphics surface pool.
* Separate DMA region EPT-mapped into the Linux guest. Linux user-space mmaps
* /dev/utsm at this GPA to obtain a writable scanout buffer that UTSM reads
* for blitting to the Deshab framebuffer (Phase 3 virtio-gpu shadow buffer).
* 16MB accommodates a 1920×1080×4 framebuffer (~8MB) with headroom for
* cursor plane and dirty rect tracking metadata. */
#define LINUX_GUEST_SURFACE_GPA 0x04100000ULL /* 65 MB (immediately after IPC shm) */
#define LINUX_GUEST_SURFACE_SIZE (16 * 1024 * 1024) /* 16MB */
/* General RAM starts after IPC + surface regions.
* Phase 1 expansion: 1.4GB to fit VSCode + Electron + GUI stack. */
#define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */ #define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */
#define LINUX_GUEST_RAM_SIZE 0x5B000000ULL /* 1.4GB (0x05000000..0x5FFFFFFF) */
/* ===== virtio-mmio device GPA layout ===== /* ===== virtio-mmio device GPA layout =====
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。 * virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
@@ -129,16 +144,33 @@ struct linux_setup_header {
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation, * 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
* 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。 * 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。
* *
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x09000000)。 */ * GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x5FFFFFFF)。
*
* VSCode integration Phase 1 expansion: 槽位从 3 扩到 8,为后续阶段预留:
* slot 0: blk (Phase 1-) IRQ5
* slot 1: net (Phase 1-) IRQ6
* slot 2: rootfs (Phase 1-) IRQ7
* slot 3: keyboard (Phase 2) IRQ8 — virtio-input, evdev /dev/input/event0
* slot 4: mouse (Phase 2) IRQ9 — virtio-input, evdev /dev/input/event1
* slot 5: gpu (Phase 3) IRQ10 — virtio-gpu 2D, /dev/dri/card0
* slot 6: extra-rootfs (Phase 4) IRQ11 — persistent rw volume (/dev/vdc)
* slot 7: reserved — future use
*/
#define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL #define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL
#define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */ #define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */
#define VIRTIO_MMIO_MAX_SLOTS 8 /* Phase 1: 3→8 */
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */ #define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */ #define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */ #define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
/* VSCode integration: Phase 2/3/4 device slots (currently unused, reserved) */
#define VIRTIO_MMIO_KBD_GPA (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4003000 */
#define VIRTIO_MMIO_MOUSE_GPA (VIRTIO_MMIO_GPA_BASE + 4 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4004000 */
#define VIRTIO_MMIO_GPU_GPA (VIRTIO_MMIO_GPA_BASE + 5 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4005000 */
#define VIRTIO_MMIO_EXTRA_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 6 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4006000 */
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */ /* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE) #define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + VIRTIO_MMIO_MAX_SLOTS * VIRTIO_MMIO_GPA_STRIDE)
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END) #define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
/* ===== Linux loader API ===== */ /* ===== Linux loader API ===== */
@@ -156,6 +188,13 @@ struct linux_guest_info {
u64 pgt_gpa; /* GPA of guest page tables */ u64 pgt_gpa; /* GPA of guest page tables */
u64 gdt_gpa; /* GPA of guest GDT */ u64 gdt_gpa; /* GPA of guest GDT */
int loaded; /* 1 = successfully loaded */ int loaded; /* 1 = successfully loaded */
/* VSCode integration Phase 1: graphics surface pool.
* Allocated as a separate DMA buffer and EPT-mapped into the guest at
* surface_gpa. Exposed to Linux user-space via UTSM_HCALL_SURFACE_INFO
* and /dev/utsm mmap (see CODE/linux/patches/utsm_hcall.c). */
u64 surface_hpa; /* Host physical address of surface pool (0 = none) */
u64 surface_gpa; /* GPA visible inside Linux guest */
u64 surface_size; /* Surface pool size in bytes */
}; };
/* Find the Linux bzImage boot module among Limine modules. /* Find the Linux bzImage boot module among Limine modules.
@@ -167,10 +206,24 @@ void *linux_find_bzimage_module(u64 *size_out);
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */ * Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
void *linux_find_rootfs_module(u64 *size_out); void *linux_find_rootfs_module(u64 *size_out);
/* Parse bzImage header and extract key parameters. /* VSCode Phase 4: find the extra rootfs image boot module (path contains
* "extra"). Exposed via writable virtio-blk slot6 as /dev/vdc (overlayfs
* upper / VSCode data volume). Returns NULL when the module is absent. */
void *linux_find_extra_rootfs_module(u64 *size_out);
/* VSCode Phase 5: find the VSCode tarball boot module (path contains
* "vscode", e.g. linux-vscode.tar.gz). The tarball is streamed into the
* guest and extracted to /opt/vscode by lxc_vscode_install().
* Returns NULL when the module is absent (install skipped, non-fatal). */
void *linux_find_vscode_module(u64 *size_out);
/* Parse bzImage header and return the protected-mode image's file offset,
* load length, decompression allocation size, and startup_64 entry offset.
* The protected-mode image starts after setup sectors; setup_header's
* payload_offset instead locates compressed data inside that image.
* Returns 0 on success, negative on error. */ * Returns 0 on success, negative on error. */
int linux_parse_bzimage(const void *bzimage, u64 size, int linux_parse_bzimage(const void *bzimage, u64 size,
u64 *payload_offset_out, u64 *payload_length_out, u64 *kernel_offset_out, u64 *kernel_length_out,
u64 *init_size_out, u64 *entry_offset_out); u64 *init_size_out, u64 *entry_offset_out);
/* Initialize the Linux guest: parse bzImage, allocate guest memory, /* Initialize the Linux guest: parse bzImage, allocate guest memory,
@@ -181,25 +234,51 @@ int linux_loader_init(void);
/* Get the loaded Linux guest info (NULL if not loaded). */ /* Get the loaded Linux guest info (NULL if not loaded). */
const struct linux_guest_info *linux_get_guest_info(void); const struct linux_guest_info *linux_get_guest_info(void);
/* VSCode integration Phase 1: query graphics surface pool.
* Returns 0 and fills outputs if the surface pool was allocated and EPT-mapped
* into the Linux guest; returns -1 if not initialized (e.g. Linux guest not
* loaded). hpa_out receives the host physical address (for UTSM-side blit),
* gpa_out receives the GPA visible inside Linux (for the guest mmap path),
* size_out receives the pool size in bytes. Any output pointer may be NULL. */
int linux_get_surface_info(u64 *hpa_out, u64 *gpa_out, u64 *size_out);
/* VSCode Phase 3: return host virtual address of the graphics surface pool.
* The virtio-gpu 2D backend composites the scanout into this buffer; the
* desktop reads it to blit guest graphics onto the IDE host area.
* Returns NULL if the pool was not allocated (Linux guest not loaded / alloc failed). */
void *linux_get_surface_vaddr(void);
/* Configure VMCS for Linux guest and perform vmlaunch. /* Configure VMCS for Linux guest and perform vmlaunch.
* Must be called after vmm_init() + linux_loader_init(). * Must be called after vmm_init() + linux_loader_init().
* Returns 0 on successful guest termination, negative on error. */ * Returns 0 on successful guest termination, negative on error. */
int linux_launch(void); int linux_launch(void);
/* Default Linux command line (can be overridden). /* Default Linux command line (can be overridden).
* nohlt: prevent kernel idle loop from using HLT — only the exec daemon's * Daemon PARK is a VMCALL. Guest idle may HLT; that stays in the
* explicit ioctl(PARK) HLT should trigger VM-Exit park, so that * guest (wait-for-IRQ). Do not use nohlt/idle=poll — poll idle
* linux_launch() returns only after the daemon is ready. * NULL-derefs current after the first real sleep.
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254), * noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。 * 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
* virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5, * virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5,
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。 */ * net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。
* VSCode Phase 2: kbd@0xF4003000 IRQ8, mouse@0xF4004000 IRQ9 (virtio-input)。
* VSCode Phase 3: gpu@0xF4005000 IRQ10 (virtio-gpu 2D, /dev/dri/card0)。
* VSCode Phase 4: xrootfs@0xF4006000 IRQ11 (可写 virtio-blk, /dev/vdc)。
* 注:无 extra-rootfs boot module 时,后端改映射 ESP 上的
* boot/linux-extra-rootfs.img(AHCI+FAT32),避免 Limine 把 4GiB
* 打进 RAM。文件也没有则不注册,guest 探测超时跳过。 */
#define LINUX_DEFAULT_CMDLINE \ #define LINUX_DEFAULT_CMDLINE \
"console=ttyS0,115200 earlyprintk=serial nokaslr " \ "earlyprintk=serial,ttyS0,115200 console=ttyS0,115200 " \
"no_timer_check loglevel=7 nohlt idle=poll " \ "nokaslr debug loglevel=8 noinvpcid nopcid nopti pti=off nogbpages " \
"noapic nolapic nosmp " \ "tsc_early_khz=2000000 lpj=4000000 i8042.nokbd i8042.noaux " \
"no_timer_check " \
"noapic nolapic nosmp ibt=off user_shstk=off " \
"virtio_mmio.device=4K@0xF4000000:5 " \ "virtio_mmio.device=4K@0xF4000000:5 " \
"virtio_mmio.device=4K@0xF4001000:6 " \ "virtio_mmio.device=4K@0xF4001000:6 " \
"virtio_mmio.device=4K@0xF4002000:7" "virtio_mmio.device=4K@0xF4002000:7 " \
"virtio_mmio.device=4K@0xF4003000:8 " \
"virtio_mmio.device=4K@0xF4004000:9 " \
"virtio_mmio.device=4K@0xF4005000:10 " \
"virtio_mmio.device=4K@0xF4006000:11"
#endif #endif
+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 log_hex64(const char *prefix, u64 value);
void panic(const char *msg); void panic(const char *msg);
/* D4: DKM 彩色日志——驱动 init 期间的颜色上下文。
* log_set_color 传 ANSI CSI(如 "\x1b[36m"),0/"" 恢复默认;
* 颜色只走串口,BOOTLOG 缓冲保持纯文本。log_color_enable 总开关
* (FUCK [debug] color_log)。 */
void log_set_color(const char *ansi);
void log_color_enable(int enable);
/* ---- 内存日志缓冲区(实机启动日志持久化) ----
* 由 disk_log_flush() 写入 FAT32 文件 SYSTEM/DESHAB64/DEV/BOOTLOG.TXT。 */
void log_capture_enable(int enable);
const char *log_get_buffer(void);
u32 log_get_length(void);
#endif #endif
+15
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_scan_result_fn)(void *ctx, u32 n, dkm_net_scan_result *out);
typedef int (*dkm_net_is_wireless_fn)(void *ctx); typedef int (*dkm_net_is_wireless_fn)(void *ctx);
/* 无线关联回调(wlan 驱动填;实施 802.11 认证/关联 + WPA 握手)。
* connect: ssid/password 关联请求,返回 0 = 已关联。
* disconnect: 解除关联。 */
typedef int (*dkm_net_connect_fn)(void *ctx, const char *ssid, const char *password);
typedef int (*dkm_net_disconnect_fn)(void *ctx);
typedef struct dkm_net_device_desc { typedef struct dkm_net_device_desc {
const char *name; const char *name;
u8 mac[6]; u8 mac[6];
@@ -50,6 +56,9 @@ typedef struct dkm_net_device_desc {
dkm_net_scan_count_fn scan_count; dkm_net_scan_count_fn scan_count;
dkm_net_scan_result_fn scan_result; dkm_net_scan_result_fn scan_result;
dkm_net_is_wireless_fn is_wireless; dkm_net_is_wireless_fn is_wireless;
/* 关联扩展(尾部追加,ABI 兼容)。 */
dkm_net_connect_fn connect;
dkm_net_disconnect_fn disconnect;
} dkm_net_device_desc; } dkm_net_device_desc;
typedef struct dkm_net_device_info { typedef struct dkm_net_device_info {
@@ -79,11 +88,17 @@ typedef struct dkm_net_api {
int (*scan_count)(u32 index); int (*scan_count)(u32 index);
int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out); int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out);
int (*is_wireless)(u32 index); int (*is_wireless)(u32 index);
/* 无线关联(dispatch 到 wlan 驱动回调;尾部追加,ABI 兼容)。 */
int (*connect)(u32 index, const char *ssid, const char *password);
int (*disconnect)(u32 index);
/* 网络统计查询(返回 0 成功,-1 索引越界)。 */ /* 网络统计查询(返回 0 成功,-1 索引越界)。 */
int (*device_stats)(u32 index, dkm_net_stats *out); int (*device_stats)(u32 index, dkm_net_stats *out);
} dkm_net_api; } dkm_net_api;
void net_init(void); void net_init(void);
/* attest: 有线/无线设备计数(启动期能力证明用,见 utsm/attest.h) */
void net_attest_counts(u32 *out_total, u32 *out_wireless);
const dkm_net_api *net_get_api(void); const dkm_net_api *net_get_api(void);
#endif #endif
+59
View File
@@ -0,0 +1,59 @@
#ifndef UTSM_PANIC_H
#define UTSM_PANIC_H
#include <utsm/types.h>
/* ===================================================================
* F1: 内核 panic 统一兜底(莲花崩溃屏)
*
* 崩了也得崩出莲花:
* - 帧缓冲直接绘制(不走 console_fb 驱动——崩溃时驱动可能已挂)
* - 淡金莲花 + panic_symbol + 消息 + 寄存器简表
* - DRR 归档(crash_buf)后 halt
*
* 本路径只依赖:Limine framebuffer(Limine 常驻映射)+ serial + DRR
* Emergency Pool(静态区),不依赖堆/驱动/中断。
* =================================================================== */
/* 寄存器简表(可传 0:无异常帧的普通 panic) */
typedef struct panic_regs {
u64 rip;
u64 rsp;
u64 rflags;
u64 cs;
u64 err;
u64 vector;
u64 cr2;
u64 rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11;
} panic_regs;
/* ===================================================================
* panic 模式(FUCK [panic] mode=strict|fallback,默认 strict)
*
* strict :panic_full 画莲花屏 + DRR 归档 + halt,不返回(零降级铁律)。
* fallback:panic_full 只留串口坐标 + 计数后返回,调用点必须紧跟一条
* 可执行的备用路径(返回错误码 / 跳过该能力 / 回退实现)。
* 寄存器帧非空(CPU 异常)时无论模式都 halt。
*
* panic_halt 无视模式恒 halt,仅用于"没有任何可继续的状态"的终点:
* CPU 异常、LA57 激活(4 级页表 walk 会写坏内存)、DSK 交接失败、
* panic_test 演示钩子。
* =================================================================== */
/* 统一 panic 入口:strict 模式不返回;fallback 模式记录后返回 */
void panic_full(const char *symbol, const char *msg, const panic_regs *regs);
/* 恒 halt 的 panic(不受 fallback 模式影响) */
__attribute__((noreturn))
void panic_halt(const char *symbol, const char *msg, const panic_regs *regs);
/* 设置/查询 fallback 模式(kernel/main.c 读 FUCK 后设置一次) */
void panic_set_fallback(int enable);
int panic_fallback_enabled(void);
/* fallback 模式下被吞掉的 panic 次数 */
u32 panic_fallback_hits(void);
/* 兼容旧接口:panic("msg") = panic_full("UTSM-PANIC", msg, 0) */
void panic(const char *msg);
#endif /* UTSM_PANIC_H */

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