- 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>
18 KiB
万物互联与服务总线设计(DKM-BUS)
系列代号:万物互联(Deshab Interconnect) 路线:P0-P6,六阶段全量改造 状态源:STATUS.md;任务板:TASKBOARD.md(D6/D7 条目) 设计定稿:2026-10-10
0. 一句话
没有主内核。utsm.elf 只剩"机制"(中断/内存/调度/恢复/封缄/panic/加载器),其余一切——驱动、文件系统、向导、登录、桌面、shell——都是平等的 DKM 内核模块,彼此只通过按名服务总线(kern.* / srv.*)互联。
这是 library OS / 万物互联形态:程序即内核,模块间没有 syscall 边界,没有第二次内核交接。性能不是本设计的目标——按名查找、间接调用、注册-消费模式到处用,是刻意的(自由 > 快)。
Limine → utsm.elf(唯一内核:IDT/IRQ、DMA、分页、arena、UTSM/UTRW、DRR、
PCQ 调度器、panic 莲花屏、DKM loader、服务总线宿主)
│ manifest.json 4+1 stage,全部 .drv(ET_REL),加载后常驻、永不被覆盖
stage0 platform: timer apic acpi pci
stage1 boot: console_fb ahci nvme ehci xhci bootfs
stage2 fs: vfs fat32(实装 R/W) ext4(新) devfs
stage3 optional: e1000 virtio_net ath9k wlan ps2kbd
stage4 session: ps2mouse session(协调器) netman firstinit login shell desktop cmd
↑ driver_init=注册服务;运行= srv.*.run 被调用 / 调度任务
│ 模块间唯一通信方式:服务总线(kern.* / srv.* 按名注册-消费)
终态:PCQ 调度器常驻(bootstrap 变 idle),desktop 等长期模块为任务,
输入走真 IRQ(IRQ1/12),无 DSK、无第二次内核交接、无 IF=0 时代
0.1 与 RTOS / 微内核的区别(定界)
- 这不是 RTOS:不承诺调度延迟上界(O(1) 调度器结构上接近,但那是副产品)。
- 这也不是微内核:模块不在用户态,没有 IPC 权限检查,一切 Ring0——SAS-R0 约束不变。
- 最近亲缘:exokernel/library OS(程序即内核)+ multikernel 的"OS 即分布式系统"世界观(去中心服务表)。
1. 服务总线(dkm_bus)
1.1 从 kapi 到 bus
现有 dkm_kapi_table(CODE/UTSM/dkm/kernel_api.c:95-136)是静态只读表:只有内核能导出(14 项),驱动不能注册。总线把它双向化:
- kern.*:仅 UTSM 注册(内核本体 + 内核托管注册表的前端)。
- srv.*:任何 .drv 模块在 driver_init 里注册,任何模块消费。
- 旧 14 条无前缀 kapi 名(
log.info等)冻结不动(兼容层);总线播种时同时注册带kern.前缀的新名。终态(P6 收尾)kapi 静态表退役,总线成为唯一注册源。
1.2 数据结构与 API
新增 CODE/UTSM/dkm/bus.c;include/utsm/dkm.h 尾部追加(不改任何既有字段,kernel_api+0xA8 block 槽偏移不受影响):
#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 /* 模块声明:注销即非法 */
typedef struct dkm_bus_service {
char name[DKM_BUS_MAX_NAME]; /* NUL 终止,必须带命名空间前缀 */
u32 version; /* 服务契约版本,消费方校验 */
u32 flags;
void *fn; /* 服务入口,签名由各服务契约定义 */
const struct dkm_driver_handle *provider; /* NULL = 内核 */
} dkm_bus_service;
typedef struct dkm_bus_api {
u32 magic, 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 dkm_bus_service *(*at)(u32 index);
} dkm_bus_api;
/* dkm_kernel_api 尾部追加(kapi 之后):const dkm_bus_api *bus; */
/* 内核直连(kernel 不经函数指针调自己):
* const dkm_bus_api *dkm_bus_get_api(void);
* void dkm_bus_set_current_provider(struct dkm_driver_handle *h);
* loader 在 driver_init 前后设/清 current provider 上下文
* (仿 D4 dkm_log_set_driver 的位置与模式)。 */
1.3 失败策略(铁律映射)
| 操作 | miss / 违规时 | 语义 |
|---|---|---|
register_service 名字非法(无命名空间前缀 / 非 srv.* 却是模块) |
BUS-E01 panic |
编程错误 |
register_service 重名 |
BUS-E02 panic |
编程错误 |
register_service 表满 |
BUS-E03 panic |
容量规划错误 |
lookup 未命中 |
BUS-E04 <名字> panic |
硬依赖未满足 = 编程错误,绝不返回 NULL 伪装成功 |
probe 未命中 |
返回 -1 | 能力缺席 = 设计内双路径,调用方必须显式处理缺席,且该回退须登记 CLAUDE.md 白名单 |
unregister 非 provider 本人 / F_NO_UNLOAD |
panic | 生命周期违规 |
两级语义正好映射插桩铁律:该响的地方响(lookup),该静的地方显式静(probe + 白名单)。
1.4 命名空间
| 前缀 | 谁能注册 | 已规划服务 |
|---|---|---|
kern.* |
仅 UTSM | kern.log.info/warn/error/panic、kern.dma.alloc_pages、kern.irq.register、kern.irq.vector_alloc/free、kern.irq.unmask/mask、kern.mm.map_mmio/unmap_mmio、kern.apic.register_eoi、kern.drr.snapshot_dirty/rollback_pages、kern.utrw.debug_dump、kern.block.read/write、kern.config.get、kern.pe.service、kern.lxc.service、kern.probe.info、kern.dkm.load_by_name、kern.bootlog.milestone |
srv.* |
任何 .drv | srv.input.mouse.poll、srv.input.kbd.poll、srv.fs.fat32.read_path/write_file/bootlog、srv.fs.ext4.*、srv.net.lease、srv.user.config、srv.auth.result、srv.session.dev_mode、srv.firstinit.run、srv.login.run、srv.desktop.run、srv.netman.run、srv.shell.run |
规则:模块注册不带命名空间前缀或带 kern. → BUS-E01。名字长度 ≥ DKM_BUS_MAX_NAME → BUS-E01。
1.5 与 manifest depends/provides 的两层机制
manifest 的 depends/provides 今天是纯元数据(dsm_load_stage_range 只按数组顺序加载,不做校验)。总线设计引入两层强制:
第一层:加载时序保证(P0)
- DSM loader 维护 capability 集合(已加载驱动的 provides token 并集)。
- 每个驱动加载前校验其 depends 全部满足,miss →
DKM-E44 DEPENDENCY NOT SATISFIEDpanic。 - 同 stage 内按依赖不动点迭代排序(多趟扫描直到无进展;环/死锁 → panic)。性能无所谓,正确性第一。
- driver_init 返回 0 后,登记该驱动 desc->provides 的 token。
第二层:服务可发现性校验(P1)
- loader 在 driver_init 成功返回后校验:stage≥4 模块的每个 provides token
T必须对应至少一个已注册的srv.T或srv.T.*服务,否则DKM-E45 PROVIDES NOT REGISTEREDpanic。 - stage0-3 老 14 驱动豁免:它们的 provides 由内核侧注册表兑现(如 ahci → block registry),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目,写死在
dsm_verify_provides的注释里。
为什么 provides 不自动注册:loader 拿不到服务 fn 指针(注册哪些函数、什么签名是模块的自由)。所以是"模块主动注册 + loader 事后校验契约"——自由也要有地图,但地图由填图人自己画,loader 只查漏。
1.6 生命周期模型("程序变内核任务")
load(ET_REL 重定位,kmem_alloc 永久内存——镜像覆盖问题就此灭绝)
→ driver_init(handle, api):只做 注册服务 + (长期模块)api->sched->task_create
(快、非交互;此时抢占未启用,满足 task_create 前置条件)
→ 控制权回到 DSM loader → stage4 全部完成 → 回 kernel_main
→ 排序者(P4 期间=残留 DSK,P5 起=session.drv)bus 调 srv.*.run 依次拉起
(一次性模块 call-and-return;交互式模块 run 内阻塞交互——与今天 DSK 阻塞在
login 上的行为一致,无回归)
→ P6 终态:utsm_sched_enable 常驻 + bootstrap park 成 idle;desktop 的 run 即
它创建的任务入口,由 PCQ 驱动;输入 IRQ 解屏蔽(IRQ1/12)
关键设计:"运行"也是总线服务(srv.<mod>.run)。这回答了"desktop 什么时候 driver_init"——在 UTSM 阶段、任何 UI 出现之前;init 只注册不运行;运行由排序者按总线调用发起。
loader 单线程不变式:session.drv 的 kern.dkm.load_by_name 在 stage 循环外被调(顺序执行 + loaded 标志防重入),总线无锁。任何未来并发加载都是契约违规。
2. 跨模块状态迁移表
现状真正跨模块耦合只有 dsk_boot_context(desktop.h 的 extern 全局是 desktop 单模块内多 TU 链接,ld -r 后原样工作)。逐槽消灭:
| 现状耦合通道 | 总线化后 | 提供方 |
|---|---|---|
desktop *(u64*)(api+0xA8) 裸偏移读 block |
api->block 字段直读(driver_init 拿到真 api 指针) |
UTSM |
reserved[4] pe_service |
kern.pe.service |
UTSM pe.c |
reserved[5] lxc service |
kern.lxc.service |
UTSM linux_compat.c |
reserved[6] probe_info |
kern.probe.info |
UTSM probe.c |
reserved[7] net_lease |
srv.net.lease(fn 返回 const net_lease_info*) |
netman.drv |
reserved[0/1] userconf 缓冲 |
srv.user.config(ptr/size/状态打包结构) |
firstinit.drv |
reserved[2] login 成功标志 |
srv.auth.result |
login.drv |
reserved[3] dev_mode |
srv.session.dev_mode |
session.drv |
g_mouse_state / mouse_poll_state() |
srv.input.mouse.poll(沿用 mouse_state_t magic/version 契约) |
ps2mouse.drv |
| DSK 自带 FAT32 只读解析 + 写路径 | srv.fs.fat32.read_path / srv.fs.fat32.write_file / srv.fs.fat32.bootlog |
fat32.drv(实装) |
| DSK 自带 ext4.c | srv.fs.ext4.* |
ext4.drv(新) |
| DSK PIE ELF loader | (消灭)app 全变 .drv,统一走 UTSM DKM ET_REL loader | UTSM |
| dsk_fatal 莲花屏 | api->log->panic(内核 F1 莲花屏,视觉本就同源) |
UTSM |
| dsk_boot_context 整体 | (消灭)P5 删除 dsk.h 中的定义 | — |
3. DSK 的命运:溶解
终态完全消灭 deshab.elf。不保留"会话协调器特权模块"——会话排序逻辑移植为 session.drv,一个普通的 stage4 .drv,无任何特殊待遇。
迁移期(P2-P4)DSK 先降格为纯总线客户端(调度调用从"自带 FAT32 + PIE loader"改成 bus 调用),P5 删除二进制、dsk_loader.c 交接代码与 dsk_boot_context。
职责归位地图:
| DSK 职责 | 归宿 |
|---|---|
| FAT32 只读解析 + 写路径 + bootlog | fat32.drv 实装(母本:dsk_loader.c 的 fat32_read_path/write_file_in_dir 全套) |
| ext4.c 读写 | ext4.drv(整文件移植,dsk_fatal → g_log->panic) |
| PIE ELF loader + 8MB ibuf | 消灭(app 全变 .drv ET_REL;DKM-E70 ibuf 雷区整体绕开) |
| 静态 Logo / splash | session.drv |
| FUCK INI 读取 | 单一事实源 = Limine boot module(cmdline="fuck:config")+ session.drv 自带 INI 解析副本 |
| firstInit.txt 检测 | session.drv(经 srv.fs.fat32.read_path) |
| 模块调度(mouseInit/FirstInit/netman/login/shell/desktop/cmd) | 各自变 stage4 .drv;session.drv 用 kern.dkm.load_by_name 按需拉起 + bus 调 srv.*.run |
| BOOT01-07.TXT 里程碑 | kern.bootlog.milestone / srv.fs.fat32.bootlog |
| 会话选择(native/kde/cmd)+ 登录强制 | session.drv(登录失败 → SESSION-E02 panic,沿用 FS-E06 语义) |
| dsk_boot_context | 消灭 |
P2-P5 之间的双 FAT32 实现是受控临时债:deshab.elf 自身装载仍走内核侧 FAT32(dsk_load_from_block_fat32),直到 P5 删除。窗口期内禁止"顺手"修两边分叉逻辑。
4. 落地路线(P0-P6)
每 phase QEMU 可启动(build.bat -Variant dev → run_qemu.bat,ahci0 SATA + TCG + 串口 stdio),证据日志落 .build_tmp/。
P0 — DSM 依赖图强制
dkm/manifest.c:capability 集合 + 不动点排序 + depends 校验(DKM-E44)。
验收:正常启动串口与基线逐行一致;负向测试(假 depends 条目)→ DKM-E44 莲花屏。
P1 — 服务总线落地
dkm/bus.c(新)+ dkm.h 尾部追加 + provider 上下文 + dsm_verify_provides(DKM-E45)+ attest 翻绿 + selftest 用例(TST-E25..E28)+ devfs /dev/services。
验收:[BUS] ready services=N;[TST] bus selftest PASS;老 14 驱动不变。
P2 — 文件系统归位
fat32.drv 实装(srv.fs.fat32.)+ ext4.drv(srv.fs.ext4.)+ DSK/UTSM 消费侧切总线。
验收:[fat32] srv.fs.fat32.read_path registered、[ext4] selftest PASS、首次启动全流程不回归。
P3 — 加载器 FS 化 + stage4 + ps2mouse.drv 试点
DSM_MAX_STAGES 4→6 / PER_STAGE 8→12;dsm_read_module_fs(boot module 优先 / FS 回退 / DKM-E46);kern.dkm.load_by_name 接通;ps2mouse.drv(srv.input.mouse.poll);manifest 新增 stage4 "session";DSK 删 mouseInit 调度块。
验收:[DSM] stage 4 session begin → [ps2mouse] driver ready。
P4 — 应用模块 DKM 化(4a-4f)
统一配方:① dsk_entry(ctx) → driver_init(api, handle) + driver_desc(stage=4、NO_UNLOAD);② 原 entry 主体改 static <mod>_run(void) 注册 srv.<mod>.run;③ ctx 消费点按 §2 迁移表替换;④ 自带 fatal → api->log->panic;⑤ 构建:PIE linker.ld 弃用,clang -c + lld -r 产出到 SYSTEM/driver/session/,每模块必跑 llvm-readobj --relocations 核对 ∈ {R_X86_64_64/32/32S/PC32/PLT32};⑥ DSK 同 commit 删对应调度块改 bus 调 run。
子步:4a netman → 4b shell → 4c firstInit(user.conf 写盘收编进模块,reserved[0/1] 往返删除)→ 4d login → 4e desktop(~20 TU,最大风险点;host SDL 构建 #ifdef 隔离)→ 4f cmd + 收尾。
P5 — DSK 退役 + session.drv
session.drv(排序骨架移植)+ 删 dsk_loader.c / dsk_boot_context / CODE/dsk/ + main.c 交接改 dsm_load_by_manifest_upto(cfg, 4)(quiesce 保留——运行时行为与 DSK 时代逐字节对齐,无回归)+ CLAUDE.md 全面更新。
验收:串口无 [DSK] boot;[session] stage4 sequencing begin → 全流程正常;dev/release 双过。
P6 — 常驻调度 + 事件循环 + 真 IRQ 输入
sched_enable 常驻 + bootstrap park;desktop 任务化(task_create + 每帧 srv.input.*.poll + sleep_ms);ps2kbd 事件环形缓冲 + srv.input.kbd.poll;ps2mouse IRQ12 解屏蔽(kern.irq.unmask);firstInit/login 键盘轮询 → kbd.poll(强制:IRQ 驱动后 handler 读走 0x60,轮询端口会饿死)。
验收:[SCHED] enable 常驻;QEMU QMP sendkey/鼠标事件到达 desktop;[desktop] task slot=N。
收尾(并入 P6)
kapi 静态表退役(总线唯一注册源);block/net 注册表前端化(消费者切 kern.block.*);STUB grep 基线不抬升;desktop 任务 DRR 看门狗 register/kick(可选)。
5. 错误码登记
| 码 | 含义 | 触发点 |
|---|---|---|
| BUS-E01 | 服务名非法(无命名空间 / 模块注册 kern.* / 超长) | register_service |
| BUS-E02 | 服务重名 | register_service |
| BUS-E03 | 服务表满(DKM_BUS_MAX_SERVICES) | register_service |
| BUS-E04 | lookup 未命中(硬依赖未满足) | lookup |
| BUS-E05 | 注销违规(非 provider / F_NO_UNLOAD) | unregister_service |
| DKM-E44 | manifest depends 未满足 | dsm_load_stage_range / dsm_load_named |
| DKM-E45 | stage≥4 provides 未注册到总线 | dsm_verify_provides |
| DKM-E46 | 模块 boot module 与 FS 双源皆无 | dsm_load_stage_range |
| DKM-E47 | 依赖图成环 | 不动点排序 |
| DKM-E48 | capability 表满(DSM_MAX_CAPS) | dsm_capability_add |
| SESSION-E01 | session.drv 初始化失败(FUCK 缺失等) | session.drv |
| SESSION-E02 | 登录强制失败(沿用 FS-E06 语义) | session.drv |
| SESSION-E03 | 会话类型非法/加载失败 | session.drv |
| TST-E34..E38 | 总线 selftest 用例(注册/命中/probe/重名/注销违规;TST-E01..E33 已被既有用例占用) | utsm_selftest.c |
全部零降级:panic 莲花屏。
6. 设计内白名单(probe 型缺席 + 顺序豁免,CLAUDE.md 同步登记)
- 无块设备时
srv.fs.fat32.*缺席 → PE/会话路径 warn 跳过(环境能力缺失,与既有"块设备缺失回 boot module"白名单同源)。 - FUCK
[drivers]关掉的模块 → 其 srv.* 服务缺席,消费方必须用 probe 并显式处理。 - QEMU 无 ath9k / 无 wlan → 既有白名单不变。
- P0 顺序豁免:
dsm_load_named_boot(cfg, "pci"/"ahci")(kernel/main.c VMM 路径)跳过 depends 校验——pci+ahci 必须先于 apic/stage0 加载,否则 Linux guest 陷入 IDTR=0 VM-exit 风暴(历史验证的行为边界)。这是顺序豁免不是降级:模块照常 ELF 校验/driver_init/provides 登记,只是不卡依赖时序。kern.dkm.load_by_name(P3)一律走严格版dsm_load_named。
7. 风险点
- desktop 多 TU 重定位集合(最大技术风险):lld -r 产物可能带出新 relocation 类型 → DKM-E37 开机即炸(至少响亮)。每模块迁移必跑 readobj 并在 phase 记录贴清单。
- DSK-E70(ibuf 尾部写零,未解):DKM 化整体绕开 8MB ibuf,但若根因是 AHCI DMA PRDT 越界,会跟到 fat32.drv 缓冲。fat32/ext4 保留写后回读校验,持续观察 STATUS.md 该条目。
- 交互式模块阻塞 bootstrap:与今天行为等价无回归;DRR 看门狗默认关(FUCK),接 P6 时给 desktop 任务显式 register/kick。
- 加载器重入:loader 单线程不变式(§1.6),违反即契约违规。
- attest/FUCK 双面性:app 模块进 manifest 后受
[drivers]开关与require_*影响;build/configs/{dev,release}/FUCK 的 attest 豁免行同步增删(release 要求 desktop 必在,QEMU 无 ath9k 等继续豁免)。 - DSM/manifest 容量:stage4 一期约 8 条 → P3 把上限提到 12/6;name[32]/path[64]/depends 4/provides 4,desktop 的 provides token 控制在 4 内。
"没有主内核,每个模块都是内核——总线就是万物的神经。疯但互联。" 🪷