diff --git a/CLAUDE.md b/CLAUDE.md index ea8be32..45f5186 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,8 +85,21 @@ SYSTEM/ SYSTEM/driver/manifest.json 内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。 +SYSTEM/STARTMENU.CNF + M3 开始菜单注册表(FAT32 根目录,desktop 启动时解析)。 + 格式 NAME|显示名|path|args;path 支持 builtin: / program/xxx.exe(GUI PE, + g++ 编译,run_windowed 嵌入桌面)/ bin/xxx.elf(ELF 工具,全屏加载)/ + /guest/path(Linux guest 应用)。显示名可 UTF-8 中文;文件缺失时回退全部已注册应用。 + 纯 CLI exe(bin/pciscan.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行, + 输出走串口。源码在 CODE/tools/pe-tools/(build.ps1 用 mingw-w64 g++ 编译), + ELF 工具源码在 CODE/tools/diskinfo/(→ SYSTEM/bin/diskinfo.elf)。 + +SYSTEM/program/ + SYSTEM/bin/ + SYSTEM/lib/ + PEAPPS.CNF + LINUXAPP.CNF + STARTMENU.CNF + 桌面 PE/工具链全部经 DSK FAT32 只读 ESP——pack_system_image.sh 会把这几项 + 额外拷到 FAT32 ESP(p1)。漏拷则开始菜单/PE 应用在 GPT 镜像里静默失效。 + SYSTEM/system/deshab64/FUCK - 内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为。 + 内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为、panic 策略([panic] mode=strict|fallback)。 UTSM 通过 Limine boot module 预加载读取(cmdline="fuck:config")。 DSK 通过 FAT32 子目录遍历读取(路径 "SYSTEM /DESHAB64 /FUCK ")。 缺失或为空时使用硬编码默认值,行为与无配置文件时一致。 @@ -100,14 +113,18 @@ CODE/DKM/README.md CODE/UTSM/README.md UTSM 模块设计说明。 -RE/UTSM_算法记录.md +docs/RE/UTSM_算法记录.md UTSM 核心算法记录。 -RE/UTSM_设计架构.md +docs/RE/UTSM_设计架构.md UTSM 架构记录。 -RE/驱动模块ABI设计.md - DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。 +docs/RE/驱动模块ABI设计.md + DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机、boot module 方案和服务总线 ABI(§8.5)。 + +docs/RE/万物互联与服务总线设计.md + 万物互联架构定稿:dkm_bus 按名服务总线(kern.*/srv.*)、dsk_boot_context 逐槽迁移表、 + DSK 溶解地图、P0-P6 落地路线与错误码登记(BUS-E/DKM-E44..47/SESSION-E)。 ISO/deshab.img build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。 @@ -136,11 +153,13 @@ SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。 CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf build.bat: 调用 build.ps1,透传参数(如 -Variant dev)。 build.ps1: 编译 UTSM/DSK/工具/驱动,把整个 SYSTEM/ 打成 GPT(FAT32 ESP + ext4) 镜像: - - ISO/deshab-dev.img 开发者模式(dev_mode=1,自动跑 shell.elf 测试,调试全开,QEMU 验证选项) - - ISO/deshab-release.img 发布版(dev_mode=0,进桌面+登录,调试零开销,真机安全选项) - 参数 -Variant dev|release|both(默认 both)。both 模式构建后恢复 SYSTEM 为 dev 配置。 - 配置模板在 build/configs/{dev,release}/(FUCK + firstInit.txt),打包前注入到 SYSTEM。 -ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab-dev.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 下失败的问题。 ``` @@ -176,11 +195,12 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp 当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。 -下一阶段优先路线(活跃路线 = `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`): +下一阶段优先路线(活跃路线 = **万物互联 P0-P6**(D6/D7,设计定稿 `docs/RE/万物互联与服务总线设计.md`)+ `docs/ROADMAP_INSANE.md` 五期填补计划,任务编号 F/U/D/R/E;状态唯一来源 = `docs/STATUS.md`): +0. **万物互联(D6/D7,当前最高优先)**: P0 manifest 依赖图强制 → P1 dkm_bus 双向服务总线(kern.*/srv.*)→ P2 fat32/ext4.drv 归位 → P3 加载器 FS 化 + stage4 + ps2mouse.drv → P4 六 app 模块 DKM 化(netman/shell/firstInit/login/desktop/cmd)→ P5 DSK 退役 + session.drv → P6 常驻调度 + 真 IRQ 输入。每 phase QEMU 验证后提交,进度见 STATUS.md 万物互联表。 1. **一期 F 系列收尾**: F2 统一控制台后端(fbcon.h 抽象已落,panic 已用)。(F1 莲花 panic / F3 跨平台 Makefile / F4 boot 分级自检 / F5 skip_firstinit 已完成) 2. **二期 U 系列**: ✅ 已完成(U1 UUID 表 / U2 Slow Path 真分支 / U3 @sealed 可选标记+linker .sealed section / U4 密文 dump / U5 RFC 8439 KAT)。chacha20 KAT 三方对拍通过(pycryptodome+官方向量+C),"间歇 FAIL" 确认为从未合并的幽灵缺陷。 -3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本) -4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS) +3. **三期 D 系列**: DKM 热卸载(引用计数)、裸机器码加载器、按名 API 导出表升级为服务总线(并入万物互联 P1)。(~~彩色日志~~ D4 已完成:驱动名 hash → 8 色 ANSI,FUCK color_log 开关,BOOTLOG 保持纯文本) +4. **四期 R 系列收尾**: segment/system 级回滚验证、PCQ per-cpu 多核(多核在 P6 常驻调度之后排队)。(~~drr_emergency_pool_kb/drr_recovery_log_size 接线~~ R3b 已完成,运行期钳位验证 PASS) 5. **五期 E 系列**: 启动莲花动画、假想时光键彩蛋、提交信息规范、风味 ISO。 6. **工程债(Phase 存档遗留)**: vector allocator/MSI-X、NVMe 高位 BAR、virtio-net virtqueue、DKM 零 warning。 @@ -304,6 +324,11 @@ kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bp - 每次只处理 **一个文件** 的 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_=1`(默认)而能力未就绪 → 交接 DSK 前 panic(莲花屏,错误码=该能力登记码)。`require_=0` 豁免行是设计内白名单,仅限两类:① 该构建环境不可能具备(QEMU 无 wlan/嵌套 VMX);② STATUS.md ⬜/🔶 未落地项(实现后逐项删除豁免行)。当前豁免清单见 build/configs/*/FUCK [attest]。 ### 4. 自检命令(验收只认这个输出,不认自然语言保证) @@ -319,4 +344,4 @@ powershell -Command "Get-ChildItem CODE\UTSM,CODE\DKM,CODE\dsk -Recurse -Include ``` [STUB] <阶段>/<模块>/<文件>::<函数> | 改动行= | 剩余STUB= | “<一句疯洁标语>” | 已提交= ``` -- 例:`[STUB] P1/UTSM/vmm/hypercall.c::hcall_utrw_read | 改动行=176-181 | 剩余STUB=112 | “封缄之下,未成之物当自爆”` +- 例:`[STUB] P1/UTSM/vmm/hypercall.c::hcall_utrw_read | 改动行=176-181 | 剩余STUB=112 | “封缄之下,未成之物当自爆”` diff --git a/CODE/DKM/devfs/devfs.c b/CODE/DKM/devfs/devfs.c index 7c60244..291ea5f 100644 --- a/CODE/DKM/devfs/devfs.c +++ b/CODE/DKM/devfs/devfs.c @@ -26,6 +26,26 @@ struct dkm_log_api { void (*panic)(const char *msg); }; +/* D6 万物互联:dkm_bus 镜像(布局对齐 UTSM include/utsm/dkm.h) */ +struct dkm_bus_service { + char name[48]; + u32 version; + u32 flags; + void *fn; + const void *provider; +}; + +struct dkm_bus_api { + u32 magic; + u32 version; + int (*register_service)(const char *name, u32 version, void *fn); + int (*unregister_service)(const char *name); + int (*lookup)(const char *name, void **out_fn, u32 *out_version); + int (*probe)(const char *name, void **out_fn, u32 *out_version); + u32 (*count)(void); + const struct dkm_bus_service *(*at)(u32 index); +}; + struct dkm_kernel_api { u32 version; u32 size; @@ -47,6 +67,19 @@ struct dkm_kernel_api { u64 fb_pitch; u16 fb_bpp; const void *boot_modules_response; + /* —— 尾部追加字段镜像(DKM_ABI v1;到 bus 为止) —— */ + void *irq_register; /* int (*)(u8, void*) */ + u64 hhdm_offset; + const void *block; + const void *mmio; + void *mm_map_mmio; + void *mm_unmap_mmio; + void *register_apic_eoi; + void *irq_vector_alloc; + void *irq_vector_free; + const void *sched; + const void *kapi; + const struct dkm_bus_api *bus; }; struct dkm_driver_handle; @@ -185,6 +218,31 @@ int driver_init(const struct dkm_kernel_api *api, devfs_file("fb0", info); } + /* /dev/services — D6 万物互联:总线已注册服务清单 */ + if (api->bus && api->bus->magic == 0x53425553u /* "SBUS" */) { + char cnt[24]; + cnt[0] = '0'; cnt[1] = 'x'; + u64 v = api->bus->count(); + u32 p = 2; + if (v == 0) cnt[p++] = '0'; + else { + char r[18]; u32 q = 0; + while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; } + while (q) cnt[p++] = r[--q]; + } + cnt[p] = 0; + devfs_file("services", cnt); + + g_log->info("[devfs] /dev/services/"); + for (u32 i = 0; i < api->bus->count(); i++) { + const struct dkm_bus_service *s = api->bus->at(i); + if (s && s->name[0]) { + g_log->info("[devfs] "); + g_log->info(s->name); + } + } + } + /* /dev/modules — count from boot_modules */ const struct limine_module_response *rsp = (const struct limine_module_response *)api->boot_modules_response; diff --git a/CODE/DKM/e1000/e1000.c b/CODE/DKM/e1000/e1000.c index c975c6d..9160b12 100644 --- a/CODE/DKM/e1000/e1000.c +++ b/CODE/DKM/e1000/e1000.c @@ -741,6 +741,9 @@ int driver_init(const struct dkm_kernel_api *api, (mac[0] & 1)) { raw_log("[e1000] MAC read failed (zero/multicast)\n"); g_log->panic("E1K-E01 MAC READ FAILED"); + /* fallback:本地管理单播地址(bit1=LAA,bit0=0),"DESHB" */ + mac[0]=0x02; mac[1]=0x44; mac[2]=0x45; mac[3]=0x53; mac[4]=0x48; mac[5]=0x42; + raw_log("[e1000] fallback: using locally administered MAC\n"); } raw_mac("[e1000] MAC=", mac); diff --git a/CODE/DKM/ehci/ehci.c b/CODE/DKM/ehci/ehci.c index 0dedb96..058bcf6 100644 --- a/CODE/DKM/ehci/ehci.c +++ b/CODE/DKM/ehci/ehci.c @@ -1098,10 +1098,12 @@ int driver_init(const struct dkm_kernel_api *api, dkm_delay_ms(20); } } - dkm_delay_ms(100); /* 端口稳定 */ + dkm_delay_ms(1500); /* 控制器复位后 U 盘重新连接 */ - /* 扫描端口,枚举 MSC */ + /* 扫描端口,枚举 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); @@ -1138,6 +1140,7 @@ int driver_init(const struct dkm_kernel_api *api, log_hex("[ehci] MSC device block_size=", blk); break; } + } /* 注册 block provider */ if (g_msc.ready && api->block && api->block->register_device) { diff --git a/CODE/DKM/mt7920/mt7920.c b/CODE/DKM/mt7920/mt7920.c new file mode 100644 index 0000000..f4548c0 --- /dev/null +++ b/CODE/DKM/mt7920/mt7920.c @@ -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;im[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()>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;imodule_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)<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=lenctrl=((2048u&0x3fff)<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=lendma->alloc_pages(pages,4096,0xFFFFFFFFull,&g_rx_bufs)!=0) return -1; + memset_(g_rx_bufs.virt,0,pages*4096); + for(u32 i=0;ibuf0=(u32)bp; + d->buf1=0; + d->info=0; + d->ctrl=((2048u&0x3fff)<desc.n_region); + rhex("[mt7920] patch regions=",nreg); + + int ret=0; + for(u32 i=0;itype)&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(fwszn_region; + rhex("[mt7920] ram regions=",nreg); + u32 offset=0, override=0, option=0; + for(u8 i=0;ilen); + 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; } diff --git a/CODE/DKM/virtio_net/virtio_net.c b/CODE/DKM/virtio_net/virtio_net.c index 24ccf77..a6f3c07 100644 --- a/CODE/DKM/virtio_net/virtio_net.c +++ b/CODE/DKM/virtio_net/virtio_net.c @@ -784,7 +784,7 @@ static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) { (void)ctx; (void)packet; log_hex("[virtio_net] tx stub hit, len=", length); g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)"); - return 0; + return -1; } static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) { diff --git a/CODE/DKM/wlan/wlan.c b/CODE/DKM/wlan/wlan.c index 26cdc52..2e1b7cd 100644 --- a/CODE/DKM/wlan/wlan.c +++ b/CODE/DKM/wlan/wlan.c @@ -64,6 +64,8 @@ static const struct wlan_device_id g_wlan_ids[] = { {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"}, @@ -122,10 +124,10 @@ static void zero_bytes(void *p, u64 n) { /* ---- 严格错误策略骨架:原生/网守后端未落地,调用即 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; - for (;;) __asm__ volatile("cli; hlt"); } static int wlan_scan_start(void *ctx) { @@ -192,6 +194,10 @@ int driver_init(const struct dkm_kernel_api *api, 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) { diff --git a/CODE/DKM/xhci/xhci.c b/CODE/DKM/xhci/xhci.c index aa17e24..027784e 100644 --- a/CODE/DKM/xhci/xhci.c +++ b/CODE/DKM/xhci/xhci.c @@ -562,7 +562,8 @@ static u32 xhci_pci_diag_code(void) { return 0x13; } -static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { +static int xhci_find_nth(int nth, u8 *out_bus, u8 *out_dev, u8 *out_func) { + int seen = 0; for (u16 bus = 0; bus < 256; bus++) { for (u8 dev = 0; dev < 32; dev++) { u32 vd = dkm_pci_read((u8)bus, dev, 0, PCI_VENDOR_ID); @@ -582,10 +583,13 @@ static int xhci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { if (class_code == XHCI_CLASS_SERIAL && subclass == XHCI_SUBCLASS_USB && prog_if == XHCI_PROGIF_XHCI) { - *out_bus = (u8)bus; - *out_dev = dev; - *out_func = func; - return 0; + if (seen == nth) { + *out_bus = (u8)bus; + *out_dev = dev; + *out_func = func; + return 0; + } + seen++; } } } @@ -1104,8 +1108,12 @@ static int xhci_reset_port(u32 port_idx) { /* Already enabled? */ if (portsc & PORTSC_PED) return 0; - /* Clear any change bits (W1C), then assert port reset */ - mmio_write32(port_off, portsc | PORTSC_PR); + /* 只置 PR,并清掉 RW1C 位。把读到的 PORTSC 原样或上 PR 会把 + * PED=0 和已经为 1 的变化位写回去,实机上复位经常起不来。 */ + u32 cmd = portsc & ~((1u << 1) | PORTSC_PR | 0x00FE0000u); + cmd |= PORTSC_PR; + if (portsc & (1u << 9)) cmd |= (1u << 9); + mmio_write32(port_off, cmd); /* Wait for PED (Port Enabled) with 500ms timeout */ u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500; @@ -1920,6 +1928,8 @@ static int xhci_enum_msc_device(u32 port_idx) { * driver_init — main entry point * ================================================================ */ +#define XHCI_TRY_NEXT do { xhci_nth++; if (xhci_nth < 4) goto again_ctrl; return 0; } while (0) + __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { @@ -1935,7 +1945,10 @@ int driver_init(const struct dkm_kernel_api *api, /* ---- Phase 1: PCI discovery ---- */ 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"); /* BUG-FIX: 不 halt。xhci 是可选驱动(manifest required=false), * 无 xHCI 的平台(Intel 6 系/纯 EHCI)必须继续引导到 EHCI/DSK, @@ -1955,6 +1968,18 @@ int driver_init(const struct dkm_kernel_api *api, log_hex("[xhci] vendor=", vd & 0xffff); log_hex("[xhci] device=", vd >> 16); + /* Intel PCH 默认把 USB2 口留在 EHCI。不把路由切到 xHCI, + * U 盘在 xHCI 上永远没有 CCS,块设备就不会注册。 */ + if ((vd & 0xffff) == 0x8086) { + u32 usb2_mask = dkm_pci_read(bus, dev, func, 0xD4); + u32 usb3_mask = dkm_pci_read(bus, dev, func, 0xDC); + if (usb2_mask) dkm_pci_write(bus, dev, func, 0xD0, usb2_mask); + if (usb3_mask) dkm_pci_write(bus, dev, func, 0xD8, usb3_mask); + log_hex("[xhci] intel usb2 route=", usb2_mask); + log_hex("[xhci] intel usb3 route=", usb3_mask); + dkm_delay_ms(200); + } + /* Enable PCI MEM + BUSM, disable INTx (polling mode) */ u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10); @@ -1968,7 +1993,7 @@ int driver_init(const struct dkm_kernel_api *api, if (bar0_lo & 1u) { g_log->warn("[xhci] BAR0 is IO space; unsupported"); FB_FATAL(api, 0x02); /* 红: BAR0 是 IO 空间 */ - return 0; + XHCI_TRY_NEXT; } if ((bar0_lo & 0x6u) == 0x4u) { bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u); @@ -1984,7 +2009,7 @@ int driver_init(const struct dkm_kernel_api *api, if (!api->hhdm_offset || !bar_phys) { g_log->warn("[xhci] missing HHDM or BAR0; skip"); FB_FATAL(api, 0x02); /* 红: 缺 HHDM/BAR */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 1: MMIO mapping ---- */ @@ -1992,14 +2017,14 @@ int driver_init(const struct dkm_kernel_api *api, if (!g_mmio) { g_log->error("[xhci] BAR MMIO unavailable; driver inactive"); FB_FATAL(api, 0x03); /* 红: BAR MMIO 映射失败 */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 1: Parse capabilities ---- */ if (xhci_parse_caps() != 0) { g_log->error("[xhci] capability parse failed"); FB_FATAL(api, 0x04); /* 红: capability 解析失败 */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 1: BIOS Handoff ---- */ @@ -2009,27 +2034,27 @@ int driver_init(const struct dkm_kernel_api *api, if (xhci_controller_reset() != 0) { g_log->error("[xhci] controller reset failed"); FB_FATAL(api, 0x05); /* 红: 控制器 reset 失败 */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 2: Setup Rings + DCBAA + Scratchpad ---- */ if (!api->dma) { g_log->warn("[xhci] DMA API unavailable"); FB_FATAL(api, 0x06); /* 红: DMA API 缺失 */ - return 0; + XHCI_TRY_NEXT; } if (xhci_setup_rings() != 0) { g_log->error("[xhci] ring setup failed"); FB_FATAL(api, 0x06); /* 红: ring 初始化失败 */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 2: Start Controller ---- */ if (xhci_controller_start() != 0) { g_log->error("[xhci] controller start failed"); FB_FATAL(api, 0x07); /* 红: 控制器启动失败 */ - return 0; + XHCI_TRY_NEXT; } /* ---- Phase 3: Port Scan + Device Enumeration ---- */ @@ -2039,53 +2064,56 @@ int driver_init(const struct dkm_kernel_api *api, * attachment and update port status. Without this delay, PORTSC.CCS * may read 0 even though a device is physically connected. */ - g_log->info("[xhci] waiting 100ms for port settle..."); - dkm_delay_ms(100); - - g_msc.ready = 0; + /* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。 + * 20ms 就读 CCS 时,实机上的 U 盘还没出现,于是不会注册块设备。 */ for (u32 port = 0; port < g_max_ports; port++) { u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC; u32 portsc = mmio_read32(port_off); + if (g_ppc && !(portsc & (1u << 9))) { + mmio_write32(port_off, portsc | (1u << 9)); + } + } + g_log->info("[xhci] waiting 1500ms for port settle..."); + dkm_delay_ms(1500); - /* Always log PORTSC for debugging, even if no device is connected */ - log_hex("[xhci] port=", port); - log_hex("[xhci] PORTSC=", portsc); + g_msc.ready = 0; + int saw_ccs = 0; + int saw_reset_fail = 0; + for (int pass = 0; pass < 2 && !g_msc.ready; pass++) { + if (pass == 1) { + g_log->info("[xhci] second port scan"); + dkm_delay_ms(1500); + } + for (u32 port = 0; port < g_max_ports; port++) { + u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC; + u32 portsc = mmio_read32(port_off); - if (!(portsc & PORTSC_CCS)) { - /* - * 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); + log_hex("[xhci] port=", port); + log_hex("[xhci] PORTSC=", portsc); + + if (!(portsc & PORTSC_CCS)) continue; + + saw_ccs = 1; + g_log->info("[xhci] device connected"); + + if (xhci_reset_port(port) != 0) { + g_log->warn("[xhci] port reset failed"); + saw_reset_fail = 1; + continue; } - if (!(portsc & PORTSC_CCS)) continue; /* Still no device */ + g_log->info("[xhci] port reset OK"); + + if (xhci_enum_msc_device(port) == 0) { + g_log->info("[xhci] MSC enumeration successful"); + break; + } + + g_log->warn("[xhci] MSC enumeration failed; trying next port"); } - - g_log->info("[xhci] device connected"); - - /* Port Reset */ - if (xhci_reset_port(port) != 0) { - /* 严格错误策略插桩:端口复位失败 = 设备不可用,零降级 panic */ - g_log->panic("XHC-E01 PORT RESET FAILED"); - continue; - } - g_log->info("[xhci] port reset OK"); - - /* Try to enumerate as MSC device */ - if (xhci_enum_msc_device(port) == 0) { - g_log->info("[xhci] MSC enumeration successful"); - break; /* First MSC device is enough for now */ - } - - 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 ---- */ @@ -2105,12 +2133,18 @@ int driver_init(const struct dkm_kernel_api *api, } } else { g_log->info("[xhci] no MSC device found; block provider not registered"); + if (xhci_nth < 3) { + xhci_nth++; + g_log->info("[xhci] trying next controller"); + goto again_ctrl; + } FB_FATAL(api, 0x08); /* 黄: 所有端口无设备/枚举全部失败 */ } g_log->info("[xhci] driver ready"); return 0; } +#undef XHCI_TRY_NEXT __attribute__((visibility("default"))) int driver_exit(struct dkm_driver_handle *handle) { diff --git a/CODE/UTSM/arch/x86_64/idt.c b/CODE/UTSM/arch/x86_64/idt.c index 8b0afc7..650a4dc 100644 --- a/CODE/UTSM/arch/x86_64/idt.c +++ b/CODE/UTSM/arch/x86_64/idt.c @@ -406,6 +406,43 @@ static void pic_remap(void) { outb(PIC2_DATA, 0xFF); /* mask all slave */ } +/* P1(万物互联): kern.irq.unmask/mask 总线服务后端 —— PIC IMR 位操作。 + * 仅 ISA IRQ 0-15(越界 = 编程错误,IRQ-E01 零降级)。 + * unmask IRQ8-15 时自动放开主片 IRQ2 级联。 + * 注意 apic_route=1 时 PIC 已全屏蔽、由 IOAPIC 接管(B7);该路径下 + * 的解屏蔽随 P6 真 IRQ 输入走 IOAPIC 重定向表接线。 */ +void pic_irq_unmask(u8 irq) { + if (irq > 15) { + panic_full("IRQ-E01 IRQ OUT OF RANGE", + "pic_irq_unmask: ISA IRQ range is 0-15", 0); + return; + } + if (irq < 8) { + u8 m = inb(PIC1_DATA); + outb(PIC1_DATA, m & (u8)~(1u << irq)); + } else { + u8 m = inb(PIC2_DATA); + outb(PIC2_DATA, m & (u8)~(1u << (irq - 8))); + u8 m1 = inb(PIC1_DATA); + outb(PIC1_DATA, m1 & (u8)~(1u << 2)); /* 主片 IRQ2 级联 */ + } +} + +void pic_irq_mask(u8 irq) { + if (irq > 15) { + panic_full("IRQ-E01 IRQ OUT OF RANGE", + "pic_irq_mask: ISA IRQ range is 0-15", 0); + return; + } + if (irq < 8) { + u8 m = inb(PIC1_DATA); + outb(PIC1_DATA, m | (u8)(1u << irq)); + } else { + u8 m = inb(PIC2_DATA); + outb(PIC2_DATA, m | (u8)(1u << (irq - 8))); + } +} + /* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。 * 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设 * OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位 @@ -664,8 +701,8 @@ void idt_handler(isr_frame *frame) { sym[8] = (char)(hi < 10 ? '0' + hi : 'A' + hi - 10); sym[9] = (char)(lo < 10 ? '0' + lo : 'A' + lo - 10); sym[10] = 0; - panic_full(sym, "cpu exception", &pr); - for (;;) { __asm__ volatile ("cli; hlt"); } /* 兜底(panic_full 不返回) */ + /* 异常帧上无法安全恢复执行:两种 panic 模式都 halt */ + panic_halt(sym, "cpu exception", &pr); } /* PIC IRQ: vectors 0x20-0x2F (32-47) diff --git a/CODE/UTSM/core/sealed.c b/CODE/UTSM/core/sealed.c index 0785a8c..c218eba 100644 --- a/CODE/UTSM/core/sealed.c +++ b/CODE/UTSM/core/sealed.c @@ -35,22 +35,25 @@ __attribute__((used, section(".sealed"))) static const char g_sealed_brand[] = "DESHAB-SEALED-MEMORY-V1-DEAICUP"; /* 极早期封缄初始化:任何不就绪都在这里 panic,绝不带病前进 */ -void utsm_sealed_early_init(void) { +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 时的封缄比对 */ @@ -58,6 +61,7 @@ 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]; diff --git a/CODE/UTSM/dkm/boot_modules.c b/CODE/UTSM/dkm/boot_modules.c index b0cc89a..52aa02c 100644 --- a/CODE/UTSM/dkm/boot_modules.c +++ b/CODE/UTSM/dkm/boot_modules.c @@ -414,7 +414,9 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan handle.flags = desc->flags; dkm_log_set_driver(desc->name); /* D4: 驱动自己的颜色 */ + dkm_bus_set_current_provider(&handle); /* D6: 总线 provider 上下文 */ int result = init(dkm_get_kernel_api(), &handle); + dkm_bus_set_current_provider(0); dkm_log_set_driver(0); handle.init_status = result; if (result != 0) { diff --git a/CODE/UTSM/dkm/bus.c b/CODE/UTSM/dkm/bus.c new file mode 100644 index 0000000..9a7be83 --- /dev/null +++ b/CODE/UTSM/dkm/bus.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/CODE/UTSM/dkm/kernel_api.c b/CODE/UTSM/dkm/kernel_api.c index af9bf9b..3dead09 100644 --- a/CODE/UTSM/dkm/kernel_api.c +++ b/CODE/UTSM/dkm/kernel_api.c @@ -21,11 +21,9 @@ static void dkm_api_error(const char *msg) { } static void dkm_api_panic(const char *msg) { - /* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲) */ + /* F1: 驱动 panic 统一走莲花崩溃屏(串口 dump + DRR 归档 + 帧缓冲)。 + * fallback 模式下返回驱动,由驱动调用点走其备用路径。 */ panic_full("DKM-PANIC", msg, 0); - for (;;) { - __asm__ volatile("cli; hlt"); - } } static const dkm_log_api g_log_api = { @@ -85,7 +83,8 @@ static dkm_kernel_api g_kernel_api = { .mm_unmap_mmio = 0, .register_apic_eoi = 0, .sched = 0, - .kapi = 0 + .kapi = 0, + .bus = 0 }; const dkm_kernel_api *dkm_get_kernel_api(void) { @@ -142,6 +141,11 @@ extern volatile struct limine_module_request g_module_request; extern volatile struct limine_hhdm_request g_hhdm_request; void dkm_fill_platform_info(void) { + /* D6 万物互联:总线先播种再挂载(必须先于一切驱动加载—— + * driver_init 里即可 api->bus->register_service)。 */ + dkm_bus_init(); + g_kernel_api.bus = dkm_bus_get_api(); + g_kernel_api.dma = dma_get_api(); g_kernel_api.net = net_get_api(); g_kernel_api.block = block_get_api(); @@ -206,4 +210,4 @@ 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); -} +} diff --git a/CODE/UTSM/dkm/manifest.c b/CODE/UTSM/dkm/manifest.c index f6e2b44..a59bfc9 100644 --- a/CODE/UTSM/dkm/manifest.c +++ b/CODE/UTSM/dkm/manifest.c @@ -12,6 +12,7 @@ void dsm_load_by_manifest_ex(const ini_config *cfg); #define DSM_MAX_STAGES 4 #define DSM_MAX_DEPENDS 4 #define DSM_MAX_PROVIDES 4 +#define DSM_MAX_CAPS 128 typedef struct dsm_driver_entry { char name[32]; @@ -24,6 +25,9 @@ typedef struct dsm_driver_entry { u32 provide_count; struct limine_file *boot_module; int loaded; + /* P0(万物互联):终态标志——已加载/被 FUCK 关闭/模块缺失/加载失败, + * 不再是本轮不动点迭代的候选。resolved 但 !loaded 的驱动不登记 provides。 */ + int resolved; } dsm_driver_entry; typedef struct dsm_stage_entry { @@ -37,6 +41,119 @@ typedef struct dsm_stage_entry { static dsm_stage_entry g_stages[DSM_MAX_STAGES]; static u32 g_stage_count; +static int streq_static(const char *a, const char *b); + +/* =================================================================== + * P0(万物互联):DSM 依赖图强制 + * + * capability 集合 = 已成功加载驱动的 provides token 并集。 + * 加载前校验 depends 全满足(miss → DKM-E44); + * 同 stage 内按依赖不动点迭代(环 → DKM-E47)。 + * 性能不是目标(多趟扫描),正确性第一——设计定稿见 + * docs/RE/万物互联与服务总线设计.md §1.5。 + * =================================================================== */ +static char g_caps[DSM_MAX_CAPS][32]; +static u32 g_cap_count; + +static int dsm_capability_satisfied(const char *token) { + for (u32 i = 0; i < g_cap_count; i++) { + if (streq_static(g_caps[i], token)) return 1; + } + return 0; +} + +static void dsm_capability_add(const char *token) { + if (dsm_capability_satisfied(token)) return; + if (g_cap_count >= DSM_MAX_CAPS) { + panic_full("DKM-E48 CAPABILITY TABLE FULL", + "P0 dependency graph: g_caps overflow (DSM_MAX_CAPS)", 0); + return; + } + for (u32 i = 0; i + 1 < 32 && token[i]; i++) { + g_caps[g_cap_count][i] = token[i]; + } + g_caps[g_cap_count][31] = 0; + g_cap_count++; +} + +static void dsm_register_provides(const dsm_driver_entry *drv) { + for (u32 i = 0; i < drv->provide_count; i++) { + dsm_capability_add(drv->provides[i]); + } +} + +/* P1(万物互联):stage>=4 的 provides 总线契约校验——driver_init 成功后, + * 每个 provides token T 必须已有 "srv.T" 或 "srv.T.*" 服务注册到总线, + * 否则 DKM-E45(零降级:provides 是对消费者/排序者的承诺,注册不到即违约)。 + * stage0-3 老 14 驱动豁免:其 provides 由内核注册表兑现(ahci→block + * registry 等),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目。 */ +static void dsm_verify_provides(const dsm_stage_entry *stage, const dsm_driver_entry *drv) { + if (stage->id < 4) return; + for (u32 i = 0; i < drv->provide_count; i++) { + char srv_name[40]; + const char *token = drv->provides[i]; + srv_name[0] = 's'; srv_name[1] = 'r'; srv_name[2] = 'v'; srv_name[3] = '.'; + u32 p = 4; + while (token[p - 4] && p + 1 < sizeof(srv_name)) { + srv_name[p] = token[p - 4]; + p++; + } + srv_name[p] = 0; + if (!dkm_bus_prefix_registered(srv_name)) { + log_error("[DSM] provides token not registered on bus: "); + log_info(drv->name); + log_info(token); + panic_full("DKM-E45 PROVIDES NOT REGISTERED", + "stage>=4 driver provides token has no matching srv.* bus service", 0); + } + } +} + +/* depends 全满足返回 1;否则返回 0 并(可选)写出第一个未满足 token 的下标 */ +static int dsm_deps_satisfied(const dsm_driver_entry *drv, u32 *missing_idx) { + for (u32 i = 0; i < drv->depend_count; i++) { + if (!dsm_capability_satisfied(drv->depends[i])) { + if (missing_idx) *missing_idx = i; + return 0; + } + } + return 1; +} + +/* token 是否由本 stage 尚未 resolved 的候选提供(用于区分缺依赖 vs 成环) */ +static int dsm_dep_pending_in_stage(const dsm_stage_entry *stage, const char *token) { + for (u32 di = 0; di < stage->driver_count; di++) { + const dsm_driver_entry *drv = &stage->drivers[di]; + if (drv->resolved) continue; + for (u32 i = 0; i < drv->provide_count; i++) { + if (streq_static(drv->provides[i], token)) return 1; + } + } + return 0; +} + +/* 不动点停滞:先判真缺依赖(DKM-E44),全在剩余候选里则判环(DKM-E47)。 + * 零降级:两者都是编程/manifest 错误,莲花屏。 */ +static void dsm_report_dependency_stall(const dsm_stage_entry *stage) { + for (u32 di = 0; di < stage->driver_count; di++) { + const dsm_driver_entry *drv = &stage->drivers[di]; + if (drv->resolved) continue; + for (u32 i = 0; i < drv->depend_count; i++) { + const char *dep = drv->depends[i]; + if (!dsm_capability_satisfied(dep) && !dsm_dep_pending_in_stage(stage, dep)) { + log_error("[DSM] dependency not satisfied: "); + log_info(drv->name); + log_info(dep); + panic_full("DKM-E44 DEPENDENCY NOT SATISFIED", + "manifest depends token not provided by any loaded/pending driver", 0); + return; + } + } + } + panic_full("DKM-E47 DEPENDENCY CYCLE", + "manifest stage dependency graph has a cycle (no progress)", 0); +} + extern volatile struct limine_module_request g_module_request; static int dsm_skip_ws(const char **p) { @@ -136,6 +253,7 @@ static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) { drv->required = 1; drv->boot_module = 0; drv->loaded = 0; + drv->resolved = 0; drv->class_id = 0; if (dsm_expect(p, '{') != 0) return; @@ -345,70 +463,115 @@ static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max log_info(stage->name); log_hex64("[DSM] driver count=", stage->driver_count); + /* P0:同 stage 内按依赖不动点迭代——每趟加载 deps 已满足的候选, + * 直到全部 resolved;停滞即 DKM-E44/E47(见 dsm_report_dependency_stall)。 + * 多趟扫描 O(n²·趟数),性能刻意不管,正确性第一。 */ + u32 pending = 0; for (u32 di = 0; di < stage->driver_count; di++) { - dsm_driver_entry *drv = &stage->drivers[di]; - struct limine_file *f; - struct dkm_symbol_scan symbols; - int result; + if (!stage->drivers[di].resolved) pending++; + } - if (drv->loaded) continue; + while (pending > 0) { + u32 pending_before = pending; - /* === FUCK [drivers] 驱动开关检查 === */ - if (cfg) { - int enabled = ini_get_bool(cfg, "drivers", drv->name, 1); - if (!enabled) { - log_info("[DSM] driver disabled by FUCK: "); + for (u32 di = 0; di < stage->driver_count; di++) { + dsm_driver_entry *drv = &stage->drivers[di]; + struct limine_file *f; + struct dkm_symbol_scan symbols; + int result; + + if (drv->resolved) continue; + + /* === FUCK [drivers] 驱动开关检查 === + * 关闭即退出候选(provides 不登记,依赖它的模块将 DKM-E44)。 */ + if (cfg) { + int enabled = ini_get_bool(cfg, "drivers", drv->name, 1); + if (!enabled) { + log_info("[DSM] driver disabled by FUCK: "); + log_info(drv->name); + drv->resolved = 1; + pending--; + continue; + } + } + + /* P0:depends 校验——暂不满足先跳过,等本 stage 后续候选登记 + * provides 后下一趟重试;整体停滞时统一判罚(零降级)。 */ + if (!dsm_deps_satisfied(drv, 0)) continue; + + drv->boot_module = dsm_find_boot_module(drv->path); + if (!drv->boot_module) { + log_info("[DSM] module not found"); log_info(drv->name); + drv->resolved = 1; + pending--; + if (drv->required && stage->required) { + /* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */ + panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING", + "required driver boot module not found in manifest stage", 0); + break; + } continue; } - } - drv->boot_module = dsm_find_boot_module(drv->path); - if (!drv->boot_module) { - log_info("[DSM] module not found"); + f = drv->boot_module; + log_info("[DSM] loading driver"); log_info(drv->name); - if (drv->required && stage->required) { - /* 严格错误策略插桩:required 驱动缺模块零降级(DKM-E04) */ - panic_full("DKM-E04 REQUIRED DRIVER MODULE MISSING", - "required driver boot module not found in manifest stage", 0); - break; + + if (dkm_check_elf64(f->address, f->size) != 0) { + log_error("[DSM] ELF check failed"); + if (drv->required && stage->required) { + panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID", + "required driver boot module failed ELF64 check", 0); + } + drv->resolved = 1; + pending--; + continue; + } + + if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) { + log_error("[DSM] symbol scan failed"); + if (drv->required && stage->required) { + panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED", + "required driver symbol scan failed", 0); + } + drv->resolved = 1; + pending--; + continue; + } + + result = dkm_load_elf_rel(f->address, f->size, &symbols); + if (result != 0) { + log_error("[DSM] load failed"); + if (drv->required && stage->required) { + /* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */ + panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED", + "required driver relocation/load failed", 0); + } + drv->resolved = 1; + pending--; + } else { + drv->loaded = 1; + drv->resolved = 1; + pending--; + /* P0:加载成功才登记 provides(disabled/missing/failed 一律不登记) */ + dsm_register_provides(drv); + /* P1:stage>=4 provides 总线契约校验(DKM-E45) */ + dsm_verify_provides(stage, drv); } - continue; } - f = drv->boot_module; - log_info("[DSM] loading driver"); - log_info(drv->name); - - if (dkm_check_elf64(f->address, f->size) != 0) { - log_error("[DSM] ELF check failed"); - if (drv->required && stage->required) { - panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID", - "required driver boot module failed ELF64 check", 0); + 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; } - continue; - } - - if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) { - log_error("[DSM] symbol scan failed"); - if (drv->required && stage->required) { - panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED", - "required driver symbol scan failed", 0); - } - continue; - } - - result = dkm_load_elf_rel(f->address, f->size, &symbols); - if (result != 0) { - log_error("[DSM] load failed"); - if (drv->required && stage->required) { - /* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */ - panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED", - "required driver relocation/load failed", 0); - break; - } - } else { - drv->loaded = 1; + pending = 0; } } log_info("[DSM] stage end"); @@ -419,7 +582,12 @@ void dsm_load_by_manifest_upto(const ini_config *cfg, u32 max_stage_id) { dsm_load_stage_range(cfg, 1, max_stage_id); } -void dsm_load_named(const ini_config *cfg, const char *name) { +/* 按名加载公共实现。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", @@ -442,6 +610,14 @@ void dsm_load_named(const ini_config *cfg, const char *name) { 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"); @@ -477,6 +653,11 @@ void dsm_load_named(const ini_config *cfg, const char *name) { 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; } } @@ -487,6 +668,14 @@ void dsm_load_named(const ini_config *cfg, const char *name) { "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); } diff --git a/CODE/UTSM/drr/drr_stub.c b/CODE/UTSM/drr/drr_stub.c index 8199176..df47917 100644 --- a/CODE/UTSM/drr/drr_stub.c +++ b/CODE/UTSM/drr/drr_stub.c @@ -378,6 +378,7 @@ void drr_driver_recovery_dispatch(const dkm_driver_recovery_ops *ops, 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); diff --git a/CODE/UTSM/include/utsm/dkm.h b/CODE/UTSM/include/utsm/dkm.h index 08fbff9..393014b 100644 --- a/CODE/UTSM/include/utsm/dkm.h +++ b/CODE/UTSM/include/utsm/dkm.h @@ -57,6 +57,52 @@ typedef struct { #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..run)。 + * 设计定稿:docs/RE/万物互联与服务总线设计.md §1。 + * =================================================================== */ +#define DKM_BUS_MAGIC 0x53425553u /* "SBUS" */ +#define DKM_BUS_VERSION 1u +#define DKM_BUS_MAX_SERVICES 128 +#define DKM_BUS_MAX_NAME 48 + +#define DKM_BUS_F_KERNEL 1u /* 内核注册,不可注销 */ +#define DKM_BUS_F_NO_UNLOAD 2u /* provider 声明 NO_UNLOAD,注销即 BUS-E05 */ + +typedef struct dkm_bus_service { + char name[DKM_BUS_MAX_NAME]; /* NUL 终止;必须带命名空间前缀 */ + u32 version; /* 服务契约版本,消费方校验 */ + u32 flags; /* DKM_BUS_F_* */ + void *fn; /* 服务入口,签名由各服务契约定义 */ + const struct dkm_driver_desc *provider; /* NULL = 内核;存 desc 而非 + * handle——loader 的 handle 是栈 + * 局部变量会悬垂,desc 在驱动 + * 镜像内存里持久且每驱动唯一 */ +} dkm_bus_service; + +typedef struct dkm_bus_api { + u32 magic; + u32 version; + /* 注册:name 必须带命名空间前缀且与调用方身份匹配(BUS-E01)、 + * 重名 BUS-E02、表满 BUS-E03——全部 panic,成功返回 0。 */ + int (*register_service)(const char *name, u32 version, void *fn); + /* 注销:仅 provider 本人可注销;F_KERNEL/F_NO_UNLOAD 或不存在 → BUS-E05。 */ + int (*unregister_service)(const char *name); + /* 必存在查找:未命中 → BUS-E04 panic,绝不返回 NULL 伪装成功。 */ + int (*lookup)(const char *name, void **out_fn, u32 *out_version); + /* 可选探测:未命中返回 -1;调用方必须显式处理缺席(白名单登记)。 */ + int (*probe)(const char *name, void **out_fn, u32 *out_version); + u32 (*count)(void); /* 诊断 / /dev/services */ + const dkm_bus_service *(*at)(u32 index); /* 按 index 枚举 */ +} dkm_bus_api; + typedef struct dkm_driver_desc { u32 magic; u16 abi_version; @@ -160,6 +206,9 @@ typedef struct dkm_kernel_api { /* D3: 按名导出表(尾部追加,ABI 兼容)。驱动/用户态枚举内核服务并 * 按名取函数指针;固定偏移 struct 字段不受影响。 */ const dkm_kapi_table *kapi; + /* D6 万物互联:双向服务总线(尾部追加,ABI 兼容)。kern.* 与 srv.* 按名 + * 注册-消费;driver_init = 注册服务的地方。 */ + const dkm_bus_api *bus; } dkm_kernel_api; typedef enum dkm_driver_state { @@ -191,6 +240,15 @@ void dkm_log_set_driver(const char *name); int dkm_load_builtin(const dkm_builtin_driver *driver); void dkm_scan_boot_modules(void); void dsm_load_by_manifest(void); +/* P0(万物互联):按名加载。dsm_load_named 严格版(depends 校验强制,供 + * kern.dkm.load_by_name 等常规路径);dsm_load_named_boot 顺序豁免版 + * (唯一合法调用方 = kernel/main.c VMM 路径 pci+ahci 预加载,先于 apic, + * 否则 guest 陷入 IDTR=0 VM-exit 风暴——设计内豁免,见 + * docs/RE/万物互联与服务总线设计.md §6)。 + * 签名(ini_config 定义在 kernel/ini_parser.h,此处避免头依赖): + * void dsm_load_named(const ini_config *cfg, const char *name); + * void dsm_load_named_boot(const ini_config *cfg, const char *name); + * 调用方自行前向声明(同 kernel/main.c 既有模式)。 */ void dkm_fill_platform_info(void); const dkm_kernel_api *dkm_get_kernel_api(void); @@ -198,6 +256,17 @@ const dkm_kernel_api *dkm_get_kernel_api(void); const struct drr_recovery_api *drr_get_recovery_api(void); const struct utsm_sched_api *utsm_sched_get_api(void); +/* D6 万物互联:服务总线(dkm/bus.c 提供)。dkm_bus_init 播种 kern.* + * 并挂入 kernel_api(dkm_fill_platform_info 内调用,先于一切驱动加载); + * loader 在 driver_init 前后 set/clear provider 上下文(仿 D4 颜色上下文), + * kern.* 只能在 provider==NULL 时注册。 */ +void dkm_bus_init(void); +const dkm_bus_api *dkm_bus_get_api(void); +void dkm_bus_set_current_provider(struct dkm_driver_handle *h); +/* loader 专用:token T 是否已有 "prefix" 或 "prefix.*" 服务注册 + * (stage>=4 provides 校验用,DKM-E45)。命中返回 1。 */ +int dkm_bus_prefix_registered(const char *prefix); + /* exposed for manifest parser */ struct dkm_symbol_scan { const void *driver_desc; diff --git a/CODE/UTSM/include/utsm/dsk.h b/CODE/UTSM/include/utsm/dsk.h index fa111db..b4ae9c7 100644 --- a/CODE/UTSM/include/utsm/dsk.h +++ b/CODE/UTSM/include/utsm/dsk.h @@ -10,6 +10,9 @@ #define DSK_BOOT_FLAG_DKM_READY (1ULL << 1) #define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2) #define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3) +/* FUCK [panic] mode=fallback:DSK/desktop/login/FirstInit 的 *_fatal 记录后返回, + * 调用点走备用路径(见 utsm/panic.h)。未置位 = strict。 */ +#define DSK_BOOT_FLAG_PANIC_FALLBACK (1ULL << 4) /* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */ #define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */ diff --git a/CODE/UTSM/include/utsm/fluent_ui.h b/CODE/UTSM/include/utsm/fluent_ui.h index 9fd52fb..4999d15 100644 --- a/CODE/UTSM/include/utsm/fluent_ui.h +++ b/CODE/UTSM/include/utsm/fluent_ui.h @@ -7,7 +7,8 @@ * 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 组件 按钮/图标钮/开关/滑块/列表项/卡片/文本框/滚动条/提示 + * L3 组件 按钮/图标钮/开关/滑块/列表项/卡片/文本框/滚动条/提示 + + * 复选框/进度条/标签页/分隔线/应用瓦片/菜单行/徽标(M3 开始菜单批次) * L4 图标 du_wf_icon 矢量线性图标(24 网格设计,2px 笔触) * * 约定(与 deshab_ui.h 相同):包含本头前,编译单元须已定义 g_ascii @@ -315,6 +316,92 @@ static inline void du_wf_tooltip(du_context *ctx, du_i64 x, du_i64 y, 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 风格) * @@ -343,6 +430,18 @@ typedef enum { 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, @@ -466,6 +565,80 @@ static inline void du_wf_icon(du_context *ctx, wf_icon_id id, 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; diff --git a/CODE/UTSM/include/utsm/idt.h b/CODE/UTSM/include/utsm/idt.h index e76846f..0acc380 100644 --- a/CODE/UTSM/include/utsm/idt.h +++ b/CODE/UTSM/include/utsm/idt.h @@ -58,4 +58,12 @@ void idt_irq_eoi(u8 vector); 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 diff --git a/CODE/UTSM/include/utsm/panic.h b/CODE/UTSM/include/utsm/panic.h index 25d5886..b82b8f2 100644 --- a/CODE/UTSM/include/utsm/panic.h +++ b/CODE/UTSM/include/utsm/panic.h @@ -27,9 +27,32 @@ typedef struct panic_regs { u64 rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11; } panic_regs; -/* 统一 panic 入口:画莲花崩溃屏 + 串口日志 + DRR 归档 + halt(不返回) */ +/* =================================================================== + * 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); diff --git a/CODE/UTSM/kernel/attest.c b/CODE/UTSM/kernel/attest.c index 12a9711..5a9e9c2 100644 --- a/CODE/UTSM/kernel/attest.c +++ b/CODE/UTSM/kernel/attest.c @@ -116,6 +116,8 @@ void attest_check(const void *cfg) { if (first_required_missing) { panic_full(first_required_missing->errcode, "required capability not ready at boot (attestation)", 0); + log_warn("[ATTEST] fallback: booting with required capabilities missing"); + return; } /* 全部缺失项均被 FUCK 显式豁免(设计内白名单),放行 */ diff --git a/CODE/UTSM/kernel/boot_selftest.c b/CODE/UTSM/kernel/boot_selftest.c index 764cd0a..5f9a3d0 100644 --- a/CODE/UTSM/kernel/boot_selftest.c +++ b/CODE/UTSM/kernel/boot_selftest.c @@ -131,6 +131,6 @@ void boot_panic_test(const ini_config *cfg) { extern int ini_get_bool(const ini_config *cfg, const char *section, const char *key, int defval); if (ini_get_bool(cfg, "boot", "panic_test", 0)) { log_warn("[boot] panic_test=1 -> triggering lotus panic screen"); - panic_full("UTSM-E42", "lotus panic selftest", 0); + panic_halt("UTSM-E42", "lotus panic selftest", 0); } } diff --git a/CODE/UTSM/kernel/dsk_loader.c b/CODE/UTSM/kernel/dsk_loader.c index 38a41a2..b3d24c3 100644 --- a/CODE/UTSM/kernel/dsk_loader.c +++ b/CODE/UTSM/kernel/dsk_loader.c @@ -1026,6 +1026,7 @@ static void dsk_fill_boot_context(dsk_boot_context *ctx) { ctx->abi_version = DSK_BOOT_ABI_VERSION; ctx->size = sizeof(*ctx); ctx->flags = DSK_BOOT_FLAG_FROM_UTSM | DSK_BOOT_FLAG_DKM_READY | DSK_BOOT_FLAG_FAT32_PATH; + if (panic_fallback_enabled()) ctx->flags |= DSK_BOOT_FLAG_PANIC_FALLBACK; if (g_hhdm_request.response) { ctx->hhdm_offset = g_hhdm_request.response->offset; diff --git a/CODE/UTSM/kernel/main.c b/CODE/UTSM/kernel/main.c index 5e0e371..9c91981 100644 --- a/CODE/UTSM/kernel/main.c +++ b/CODE/UTSM/kernel/main.c @@ -25,6 +25,8 @@ extern volatile struct limine_module_request g_module_request; /* dkm/manifest.c: 带 FUCK [drivers] 过滤的 manifest 加载入口 */ void dsm_load_by_manifest_ex(const ini_config *cfg); void dsm_load_named(const ini_config *cfg, const char *name); +/* P0 顺序豁免版:唯一调用方就是下方 VMM 路径(pci+ahci 必须先于 apic) */ +void dsm_load_named_boot(const ini_config *cfg, const char *name); /* dsk_loader.c: 按路径读取 FAT32 文件到 g_dsk_fat32_filedata 静态缓冲。 * Phase 1 已支持 LFN 长文件名 + 子目录遍历。 */ @@ -51,6 +53,17 @@ int g_sched_enabled_cfg = 1; /* FUCK [sched] enable /* 将 FUCK [compat] 区的配置应用到兼容层全局变量。 * 必须在 utsm_apply_config 之后调用。 */ +/* FUCK [panic] mode=strict|fallback(默认 strict)。其余取值按 strict 处理。 */ +static void utsm_apply_panic_config(const ini_config *cfg) { + const char *mode = ini_get(cfg, "panic", "mode"); + const char *want = "fallback"; + int fallback = mode ? 1 : 0; + for (int i = 0; fallback && (mode[i] || want[i]); i++) { + if (mode[i] != want[i]) fallback = 0; + } + panic_set_fallback(fallback); +} + static void utsm_apply_compat_config(const ini_config *cfg) { if (!cfg) return; const char *lib_path = ini_get(cfg, "compat", "lib_path"); @@ -128,6 +141,12 @@ static void utsm_apply_config(const ini_config *cfg) { /* ---- 全局启动配置(从 FUCK 文件读取) ---- */ static ini_config g_boot_cfg; static int g_boot_cfg_loaded = 0; + +/* D6 万物互联:FUCK 启动配置只读访问(kern.config.get 总线服务后端, + * bus.c 经前向声明调用;未加载时返回 NULL,消费方自行处理缺席)。 */ +const ini_config *utsm_boot_cfg_get(void) { + return g_boot_cfg_loaded ? &g_boot_cfg : 0; +} static int g_vmm_init_ok = 0; /* attest: vmm_vmx 能力打点 */ /* 从 Limine boot module 读取 FUCK 配置。 @@ -263,9 +282,9 @@ void kernel_main(void) { if (cr4 & (1ULL << 12)) { log_error("[UTSM] LA57 is ACTIVE — this kernel requires 4-level paging"); log_error("[UTSM] Refusing to boot. Disable LA57 in firmware settings."); - panic_full("VMM-E11 LA57 ACTIVE REFUSE BOOT", + /* 无备用路径:paging.c/mmio.c 的 4 级 walk 会写坏 5 级页表 */ + panic_halt("VMM-E11 LA57 ACTIVE REFUSE BOOT", "5-level paging active; kernel requires 4-level paging", 0); - arch_halt_forever(); } } @@ -311,6 +330,8 @@ void kernel_main(void) { * 颜色只走串口,BOOTLOG.TXT 保持纯文本) */ log_color_enable(ini_get_bool(cfg, "debug", "color_log", 1)); + utsm_apply_panic_config(cfg); + /* ---- 插桩: FUCK 配置加载完成 ---- */ INSTR_PROBE(BOOT, (u64)g_boot_cfg_loaded, 0, 0, 0); @@ -372,9 +393,8 @@ void kernel_main(void) { /* === 封缄内存极早期初始化(UTSM-E01..E04) === * .sealed 区为空 / root key 缺失 → 此处极早期 panic,绝不带病前进。 */ { - extern void utsm_sealed_early_init(void); - utsm_sealed_early_init(); - attest_report_ready("sealed_mem", "UTSM-E05"); + extern int utsm_sealed_early_init(void); + attest_report("sealed_mem", "UTSM-E05", utsm_sealed_early_init() == 0); } /* === SAS-R0-PCQ 调度器:bootstrap 任务绑定(Phase 7) === @@ -395,8 +415,11 @@ void kernel_main(void) { * 调度器没有真正立起来,零降级 panic(SAS-E01) */ panic_full("SAS-E01 SCHED BOOTSTRAP FAILED", "SAS-R0-PCQ scheduler bootstrap failed (was: non-fatal)", 0); + /* fallback:不开 demo 窗口,sched 能力不报就绪 */ + g_sched_enabled_cfg = 0; + } else { + attest_report_ready("sched", "SAS-E02"); } - attest_report_ready("sched", "SAS-E02"); } INSTR_TS_DECL(ts_dkm); @@ -430,31 +453,38 @@ void kernel_main(void) { /* Only pci+ahci before linux_launch. Loading apic/stage0 first * left the guest in an IDTR=0 VM-exit storm. Remaining DKM - * still loads after the guest parks. */ - dsm_load_named(cfg, "pci"); - dsm_load_named(cfg, "ahci"); + * still loads after the guest parks. + * P0 依赖图:此处用顺序豁免版(pci depends irq/apic 未加载), + * 豁免登记见 docs/RE/万物互联与服务总线设计.md §6。 */ + dsm_load_named_boot(cfg, "pci"); + dsm_load_named_boot(cfg, "ahci"); /* === Linux guest — 可通过 FUCK [boot] linux_guest=0 跳过 === * 严格错误策略:linux_guest=1 即"请求了 guest"——模块缺失、 * 启动失败、IPC 失败一律 panic(VMM-E/LNX-E),零降级。 */ int run_linux = ini_get_bool(cfg, "boot", "linux_guest", 1); - if (run_linux) { - if (linux_loader_init() != 0) { - panic_full("VMM-E01 LINUX BZIMAGE MISSING", - "linux_guest=1 but bzImage boot module not found", 0); - } + int linux_ready = run_linux; + if (run_linux && linux_loader_init() != 0) { + panic_full("VMM-E01 LINUX BZIMAGE MISSING", + "linux_guest=1 but bzImage boot module not found", 0); + linux_ready = 0; /* fallback:跳过 Linux guest */ + } + if (linux_ready) { INSTR_TS_DECL(ts_linux); INSTR_TS_BEGIN(ts_linux); log_info("[UTSM] Linux loader init ok"); + int lin_st = -1; + int launched = 0; if (ipc_shm_init() == 0) { log_info("[UTSM] IPC shm init ok"); + lin_st = linux_launch(); + launched = 1; } else { panic_full("LNX-E03 IPC SHM INIT FAILED", "IPC shared memory init failed (was: non-fatal)", 0); + log_warn("[UTSM] fallback: Linux guest launch skipped (no IPC)"); } - - int lin_st = linux_launch(); INSTR_TS_END(ts_linux, "linux_launch"); if (lin_st == 0) { log_info("[UTSM] Linux guest parked (daemon ready)"); @@ -480,14 +510,14 @@ void kernel_main(void) { "vscode tarball install into guest failed", 0); } } - } else { + } else if (launched) { /* 严格错误策略插桩:guest 启动失败零降级(VMM-E02) */ log_error("[UTSM] Linux launch failed"); log_hex64("[UTSM] Linux st=", (u64)(i64)lin_st); panic_full("VMM-E02 LINUX LAUNCH FAILED", "linux_guest=1 requested but VMX launch failed", 0); } - } else { + } else if (!run_linux) { log_warn("[UTSM] Linux guest disabled by FUCK (linux_guest=0)"); } } else { @@ -503,11 +533,11 @@ void kernel_main(void) { * 严格错误策略:xj380_guest=1 即"请求了 guest"——模块缺失或 * 启动失败一律 panic(VMM-E03/E04),零降级。 */ int run_xj380 = ini_get_bool(cfg, "boot", "xj380_guest", 1); - if (run_xj380) { - if (xj380_loader_init() != 0) { - panic_full("VMM-E03 XJ380 KERNEL.KRL MISSING", - "xj380_guest=1 but kernel.krl boot module not found", 0); - } + if (run_xj380 && xj380_loader_init() != 0) { + panic_full("VMM-E03 XJ380 KERNEL.KRL MISSING", + "xj380_guest=1 but kernel.krl boot module not found", 0); + log_warn("[UTSM] fallback: XJ380 guest skipped"); + } else if (run_xj380) { INSTR_TS_DECL(ts_xj380); INSTR_TS_BEGIN(ts_xj380); log_info("[UTSM] XJ380 loader init ok"); @@ -554,7 +584,9 @@ void kernel_main(void) { attest_report("pkg", "PKG-E10", 0); attest_report("brand_anim", "BRAND-E10", 0); attest_report("dkm_hot_unload", "DKM-E60", 0); - attest_report("dkm_api_export", "DKM-E61", 0); + /* D6 万物互联:dkm_bus 落地,按名服务导出能力翻绿 + * (kapi 只读表 + bus 双向注册-消费)。 */ + attest_report("dkm_api_export", "DKM-E61", 1); attest_report("nvme_rw", "NVME-E30", 0); attest_report("virtio_net", "VNET-E30", 0); } @@ -578,7 +610,6 @@ void kernel_main(void) { log_error("[UTSM] SELFTEST FAIL"); panic_full("VMM-E14 SELFTEST FAIL", "utsm_selftest_run returned non-zero", 0); - arch_halt_forever(); } } } @@ -620,7 +651,8 @@ void kernel_main(void) { if (dsk_load_and_jump() != 0) { log_error("[UTSM] DSK jump failed"); - panic_full("VMM-E15 DSK JUMP FAILED", + /* 启动链终点,无可继续的路径:两种模式都上莲花屏 */ + panic_halt("VMM-E15 DSK JUMP FAILED", "dsk_load_and_jump returned non-zero", 0); } diff --git a/CODE/UTSM/kernel/net.c b/CODE/UTSM/kernel/net.c index fe1e0de..df9d171 100644 --- a/CODE/UTSM/kernel/net.c +++ b/CODE/UTSM/kernel/net.c @@ -106,18 +106,22 @@ static int net_connect_impl(u32 index, const char *ssid, const char *password) { if (index >= g_net_device_count) return -2; const dkm_net_device_desc *dev = &g_net_devices[index]; /* 严格错误策略:无线设备未提供关联回调 = 驱动后端未实现,零降级 panic */ - if (!dev->connect) + if (!dev->connect) { panic_full("NET-E20 WLAN CONNECT NOT SUPPORTED", "wireless netdev has no connect backend (native chip driver pending)", 0); + return -3; + } return dev->connect(dev->ctx, ssid, password); } static int net_disconnect_impl(u32 index) { if (index >= g_net_device_count) return -2; const dkm_net_device_desc *dev = &g_net_devices[index]; - if (!dev->disconnect) + if (!dev->disconnect) { panic_full("NET-E21 WLAN DISCONNECT NOT SUPPORTED", "wireless netdev has no disconnect backend (native chip driver pending)", 0); + return -3; + } return dev->disconnect(dev->ctx); } diff --git a/CODE/UTSM/kernel/panic.c b/CODE/UTSM/kernel/panic.c index 4c8e7c9..bd2a4c0 100644 --- a/CODE/UTSM/kernel/panic.c +++ b/CODE/UTSM/kernel/panic.c @@ -43,9 +43,59 @@ static void pregline(fbc_ctx *p, const char *name, u64 v, i64 x, i64 y, u32 fg) phex64(p, v, x + 5 * g_ascii_w, y, fg); } -/* ---- DRR 归档(drr.h 声明,drr_stub.c 提供;未初始化时返回 -1) ---- */ +/* 按屏宽折行居中。返回下一行 y。超出 y_limit 的行不画。 */ +static i64 pwrap(fbc_ctx *p, const char *s, i64 y, i64 y_limit, u32 fg) { + i64 cols; + if (!s) return y; + cols = (i64)p->w / g_ascii_w - 2; + if (cols < 8) cols = 8; + if (cols > 120) cols = 120; + while (*s && y + g_ascii_h <= y_limit) { + char line[121]; + i64 n = 0; + while (s[n] && n < cols) { line[n] = s[n]; n++; } + line[n] = 0; + s += n; + fbc_string_center(p, line, y, fg); + y += g_ascii_h + 4; + } + return y; +} + +/* ---- panic 模式(panic.h 头注释) ---- */ + +static int g_panic_fallback; +static u32 g_panic_fallback_hits; + +void panic_set_fallback(int enable) { + g_panic_fallback = enable ? 1 : 0; + log_info(g_panic_fallback ? "[PANIC] mode=fallback (panic points take backup paths)" + : "[PANIC] mode=strict (zero degradation)"); +} + +int panic_fallback_enabled(void) { + return g_panic_fallback; +} + +u32 panic_fallback_hits(void) { + return g_panic_fallback_hits; +} void panic_full(const char *symbol, const char *msg, const panic_regs *regs) { + if (g_panic_fallback && !regs) { + g_panic_fallback_hits++; + log_warn("[PANIC:FALLBACK] panic point hit, taking backup path"); + if (symbol) log_warn(symbol); + if (msg) log_warn(msg); + log_hex64("[PANIC:FALLBACK] hits=", g_panic_fallback_hits); + return; + } + panic_halt(symbol, msg, regs); +} + +/* ---- DRR 归档(drr.h 声明,drr_stub.c 提供;未初始化时返回 -1) ---- */ + +void panic_halt(const char *symbol, const char *msg, const panic_regs *regs) { log_error("[PANIC] =========================================="); log_error("[PANIC] panic_full: 莲花崩溃屏"); if (symbol) { log_error("[PANIC] symbol:"); log_error(symbol); } @@ -75,43 +125,60 @@ void panic_full(const char *symbol, const char *msg, const panic_regs *regs) { /* 背景色 = Logo 图片背景色(四角平均,gen_panic_logo.py 生成) */ fbc_fill_bg(p, g_panic_logo_bg); - /* DEAICUP 莲花 Logo 居中,文字随 logo 下缘排布 */ - i64 ly = ((i64)p->h - (i64)g_panic_logo_h) / 2 - 40; - if (ly < 8) ly = 8; - fbc_draw_rgba(p, g_panic_logo_rgba, - (i64)g_panic_logo_w, (i64)g_panic_logo_h, - ((i64)p->w - (i64)g_panic_logo_w) / 2, ly); - + /* 底部留给错误码:莲花按整数步进缩小,保证 symbol/msg 落在屏内。 + * 长串按屏宽折行,避免居中后错误码被裁出左右边缘。 */ const u32 fg = 0xFFF0E8D8, dim = 0xFFB0A8C0, acc = 0xFFF0D080; - i64 y = ly + (i64)g_panic_logo_h + 8; - fbc_string_center(p, "DESHAB PANIC", y, acc); - y += g_ascii_h + 8; - if (symbol) { fbc_string_center(p, symbol, y, fg); y += g_ascii_h + 8; } - if (msg) { - /* 单行截断 60 字符 */ - char line[61]; - u32 i = 0; - while (msg[i] && i < 60) { line[i] = msg[i]; i++; } - line[i] = 0; - fbc_string_center(p, line, y, fg); - y += g_ascii_h + 8; - } - if (regs) { - if (y + g_ascii_h * 6 < (i64)p->h) { - pregline(p, "RIP ", regs->rip, (i64)p->w / 2 - 14 * g_ascii_w / 2, y, dim); y += g_ascii_h + 2; - pregline(p, "RSP ", regs->rsp, (i64)p->w / 2 - 14 * g_ascii_w / 2, y, dim); y += g_ascii_h + 2; - pregline(p, "FLG ", regs->rflags, (i64)p->w / 2 - 14 * g_ascii_w / 2, y, dim); y += g_ascii_h + 2; - if (regs->vector == 14) { - pregline(p, "CR2 ", regs->cr2, (i64)p->w / 2 - 14 * g_ascii_w / 2, y, dim); + i64 text_need = (g_ascii_h + 6) * (regs ? 8 : 5); + i64 step = 1; + while (step < 8 && + (i64)g_panic_logo_h / step + text_need + 24 > (i64)p->h) + step++; + i64 dw = (i64)g_panic_logo_w / step; + i64 dh = (i64)g_panic_logo_h / step; + i64 ly = ((i64)p->h - dh - text_need) / 2; + if (ly < 4) ly = 4; + { + i64 lx = ((i64)p->w - dw) / 2; + for (i64 r = 0; r < dh; r++) { + for (i64 c = 0; c < dw; c++) { + const unsigned char *px4 = + g_panic_logo_rgba + (((u64)r * (u64)step) * (u64)g_panic_logo_w + + (u64)c * (u64)step) * 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(p, lx + c, ly + r), col, a); + fbc_pixel(p, lx + c, ly + r, col); } } } - fbc_string_center(p, archived == 0 ? "crash archived to DRR" : "DRR archive unavailable", - (i64)p->h - g_ascii_h - 24, dim); + + i64 y_limit = (i64)p->h - 4; + i64 y = ly + dh + 8; + if (y + text_need > y_limit) y = y_limit - text_need; + if (y < 4) y = 4; + y = pwrap(p, "DESHAB PANIC", y, y_limit, acc); + y += 4; + y = pwrap(p, symbol, y, y_limit, fg); + y = pwrap(p, msg, y, y_limit, fg); + if (regs && y + g_ascii_h * 3 < y_limit) { + i64 rx = (i64)p->w / 2 - 12 * g_ascii_w; + if (rx < 4) rx = 4; + pregline(p, "RIP ", regs->rip, rx, y, dim); y += g_ascii_h + 2; + if (y + g_ascii_h < y_limit) { pregline(p, "RSP ", regs->rsp, rx, y, dim); y += g_ascii_h + 2; } + if (y + g_ascii_h < y_limit) { pregline(p, "FLG ", regs->rflags, rx, y, dim); y += g_ascii_h + 2; } + if (regs->vector == 14 && y + g_ascii_h < y_limit) + pregline(p, "CR2 ", regs->cr2, rx, y, dim); + } + if ((i64)p->h > g_ascii_h + 8) { + fbc_string_center(p, archived == 0 ? "crash archived to DRR" : "DRR archive unavailable", + (i64)p->h - g_ascii_h - 8, dim); + } } log_error("[PANIC] halt"); arch_halt_forever(); + for (;;) __asm__ volatile("cli; hlt"); } void panic(const char *msg) { diff --git a/CODE/UTSM/test/utsm_selftest.c b/CODE/UTSM/test/utsm_selftest.c index c54ec03..5b0ed59 100644 --- a/CODE/UTSM/test/utsm_selftest.c +++ b/CODE/UTSM/test/utsm_selftest.c @@ -284,6 +284,52 @@ int utsm_selftest_run(void) { log_info("[UTSM] selftest kapi ok"); } + /* ==== D6 万物互联:dkm_bus 双向服务总线 ==== + * 覆盖:表存在/magic、kern.* lookup 命中、probe 命中与缺席、 + * 内核侧非法注册(BUS-E01/E02 走 panic——selftest 只能验证合法路径, + * 违规路径由负向测试专用构建验证)。 */ + { + const dkm_kernel_api *api = dkm_get_kernel_api(); + if (!api->bus || api->bus->magic != DKM_BUS_MAGIC) { + log_error("[UTSM] selftest bus table missing"); + panic_full("TST-E34 BUS TABLE MISSING", + "utsm_selftest: bus api missing or bad magic", 0); + return UTSM_ERR_INVALID; + } + /* kern.* 播种命中:lookup(必存在语义) */ + void *fn = 0; + u32 ver = 0; + if (api->bus->lookup("kern.log.info", &fn, &ver) != 0 || !fn) { + log_error("[UTSM] selftest bus kern.log.info lookup failed"); + panic_full("TST-E35 BUS KERN LOOKUP FAILED", + "utsm_selftest: bus lookup kern.log.info failed", 0); + return UTSM_ERR_INVALID; + } + /* probe 命中(可选语义正例) */ + if (api->bus->probe("kern.block.read", &fn, &ver) != 0 || !fn) { + log_error("[UTSM] selftest bus kern.block.read probe failed"); + panic_full("TST-E36 BUS KERN PROBE FAILED", + "utsm_selftest: bus probe kern.block.read failed", 0); + return UTSM_ERR_INVALID; + } + /* probe 缺席(可选语义负例:返回 -1,不 panic) */ + if (api->bus->probe("srv.no.such.thing", &fn, &ver) == 0) { + log_error("[UTSM] selftest bus probe negative failed"); + panic_full("TST-E37 BUS PROBE NEGATIVE FAILED", + "utsm_selftest: probe of absent service unexpectedly hit", 0); + return UTSM_ERR_INVALID; + } + /* 服务数合理(P1 播种 22 条;后续 phase 只增不减) */ + if (api->bus->count() < 22) { + log_error("[UTSM] selftest bus seed count too low"); + panic_full("TST-E38 BUS SEED COUNT LOW", + "utsm_selftest: bus seeded service count below minimum", 0); + return UTSM_ERR_INVALID; + } + log_hex64("[UTSM] selftest bus services=", api->bus->count()); + log_info("[UTSM] selftest bus ok"); + } + /* ==== SCHED-1: O(1) runqueue 语义(合成 TCB,协作式,无 tick) ==== */ /* ==== SCHED-1 + SCHED-2: runqueue / block-wake(tick 竞态保护) ==== */ { diff --git a/CODE/desktop/MAKEFILE b/CODE/desktop/MAKEFILE index 5f6ca9d..bb43576 100644 --- a/CODE/desktop/MAKEFILE +++ b/CODE/desktop/MAKEFILE @@ -9,7 +9,7 @@ LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld C_SOURCES := main.c input.c wm.c compositor.c apps_builtin.c appreg.c \ launcher.c lxc.c shell.c pages.c f32io.c ascii_font.c zhfont.c \ - wallpaper.c taskbar.c textures.c + wallpaper.c taskbar.c textures.c prank.c files.c startmenu.c OBJECTS := $(C_SOURCES:.c=.o) .PHONY: all clean diff --git a/CODE/desktop/Makefile.linux b/CODE/desktop/Makefile.linux index ef8f15a..e94c876 100644 --- a/CODE/desktop/Makefile.linux +++ b/CODE/desktop/Makefile.linux @@ -26,7 +26,7 @@ LDFLAGS := $(SDL_LIBS) -lm TARGET := desktop_host SRCS := main.c input.c wm.c compositor.c apps_builtin.c appreg.c \ launcher.c lxc.c shell.c pages.c f32io.c ascii_font.c \ - zhfont.c host_main.c + zhfont.c host_main.c prank.c files.c OBJS := $(SRCS:.c=.o) .PHONY: all run clean diff --git a/CODE/desktop/appreg.c b/CODE/desktop/appreg.c index 4f3e4a8..9a2c331 100644 --- a/CODE/desktop/appreg.c +++ b/CODE/desktop/appreg.c @@ -1,8 +1,9 @@ /* appreg.c — 应用注册表 + 桌面图标(M0 自 main.c 平移) * - * 注册来源:内置 / LINUXAPP.CNF / PEAPPS.CNF / user/desktop/*.lnk + * 注册来源:内置 / LINUXAPP.CNF / PEAPPS.CNF / user/desktop/*.lnk / STARTMENU.CNF */ #include "desktop.h" +#include "../UTSM/include/utsm/fluent_ui.h" /* M3: desktop_app_icon 图标映射 */ /* ---- 全局状态 ---- */ app_descriptor g_apps[MAX_APPS]; @@ -87,14 +88,38 @@ static void draw_icon_browser(du_context *ctx, int cx, int cy) { } void draw_app_icon(du_context *ctx, int app_id, int cx, int cy) { - switch (app_id) { - case 0: draw_icon_terminal(ctx, cx, cy); break; - case 1: draw_icon_editor(ctx, cx, cy); break; - case 2: draw_icon_folder(ctx, cx, cy); break; - case 3: draw_icon_calc(ctx, cx, cy); break; - case 4: draw_icon_browser(ctx, cx, cy); break; - default: draw_icon_terminal(ctx, cx, cy); break; + const char *n = (app_id >= 0 && app_id < g_app_count) ? g_apps[app_id].name : 0; + if (!n) { draw_icon_terminal(ctx, cx, cy); return; } + if (n[0]=='e' && n[1]=='d') { draw_icon_editor(ctx, cx, cy); return; } + if (n[0]=='p' && n[1]=='r') { draw_icon_editor(ctx, cx, cy); return; } + if ((n[0]=='f' && n[1]=='i') || (n[0]=='f' && n[1]=='o')) { + draw_icon_folder(ctx, cx, cy); return; } + if (n[0]=='c' && n[1]=='a') { draw_icon_calc(ctx, cx, cy); return; } + if (n[0]=='b' && n[1]=='r') { draw_icon_browser(ctx, cx, cy); return; } + draw_icon_terminal(ctx, cx, cy); +} + +/* M3: app_id → Fluent 线性图标(开始菜单瓦片 / 任务栏共用映射)。 + * 返回值即 wf_icon_id(int 传递避免 desktop.h 依赖 fluent_ui.h)。 */ +int desktop_app_icon(int app_id) { + const char *n = (app_id >= 0 && app_id < g_app_count) ? g_apps[app_id].name : 0; + if (!n) return WF_ICON_TERMINAL; + if (n[0]=='s' && n[1]=='h') return WF_ICON_TERMINAL; /* shell */ + if (n[0]=='c' && n[1]=='m' && n[2]=='d') return WF_ICON_TERMINAL; + if (n[0]=='e' && n[1]=='d') return WF_ICON_EDITOR; /* editor */ + if (n[0]=='p' && n[1]=='r') return WF_ICON_EDITOR; /* proedit */ + if (n[0]=='f' && n[1]=='i') return WF_ICON_FOLDER; /* fileman */ + if (n[0]=='c' && n[1]=='a' && n[2]=='l') return WF_ICON_CALC; + if (n[0]=='b' && n[1]=='r') return WF_ICON_BROWSER; /* browser */ + if (n[0]=='s' && n[1]=='e') return WF_ICON_SETTINGS; /* settings */ + if (n[0]=='v' || n[0]=='k') return WF_ICON_BROWSER; /* vscode/kde */ + if (n[0]=='s' && n[1]=='y' && n[2]=='s') return WF_ICON_INFO; /* sysinfo */ + if (n[0]=='c' && n[1]=='l') return WF_ICON_CLOCK; /* clock */ + if (n[0]=='p' && n[1]=='a') return WF_ICON_PICTURE; /* paint */ + if (n[0]=='n' && n[1]=='o') return WF_ICON_EDITOR; /* notes */ + if (n[0]=='d' && n[1]=='i') return WF_ICON_DISK; /* diskinfo */ + return WF_ICON_APPS; } /* ============================================================ @@ -122,11 +147,14 @@ static int lapp_field_copy(char *dst, int dst_cap, const char *src, int n) { #define UDESK_MAX 12 static char g_ud_name[UDESK_MAX][12]; -static char g_ud_disp[UDESK_MAX][16]; +static char g_ud_disp[UDESK_MAX][48]; static char g_ud_path[UDESK_MAX][96]; static char g_ud_elf[UDESK_MAX][12]; static char g_ud_files[UDESK_MAX][64]; static int g_ud_nfiles; +/* 桌面图标按 user/desktop 目录枚举顺序排,不按内置表下标。 */ +static int g_desk_order[UDESK_MAX]; +static int g_desk_n; static int udesk_name_taken(const char *n) { int i; @@ -156,6 +184,66 @@ static int udesk_ascii_ok(const char *s) { return 1; } +static int kind_word(const char *kind, const char *word) { + int i = 0; + while (word[i]) { + char c = kind[i]; + char w = word[i]; + if (c >= 'a' && c <= 'z') c = (char)(c - 32); + if (w >= 'a' && w <= 'z') w = (char)(w - 32); + if (c != w) return 0; + i++; + } + return kind[i] == 0 || kind[i] == ' '; +} + +static int find_app_by_name(const char *name) { + int a, i; + if (!name || !name[0]) return -1; + for (a = 0; a < g_app_count; a++) { + const char *nm = g_apps[a].name; + if (!nm) continue; + i = 0; + while (name[i] && nm[i] && name[i] == nm[i]) i++; + if (name[i] == 0 && nm[i] == 0) return a; + } + return -1; +} + +static void desk_place(int app_id) { + int i; + if (app_id < 0 || app_id >= g_app_count) return; + for (i = 0; i < g_desk_n; i++) { + if (g_desk_order[i] == app_id) return; + } + if (g_desk_n >= UDESK_MAX) return; + g_apps[app_id].user_desktop = 1; + g_desk_order[g_desk_n++] = app_id; +} + +/* 快捷方式种类的第一个词对应内置应用。路径以 '/' 开头不能改写种类。 */ +static const char *kind_builtin_name(const char *kind) { + if (kind_word(kind, "FILEMAN")) return "fileman"; + if (kind_word(kind, "EDITOR")) return "editor"; + if (kind_word(kind, "BROWSER")) return "browser"; + if (kind_word(kind, "SHELL")) return "shell"; + if (kind_word(kind, "CALC")) return "calc"; + if (kind_word(kind, "CMD")) return "cmd"; + if (kind_word(kind, "PROEDIT")) return "proedit"; + if (kind_word(kind, "SETTINGS")) return "settings"; + return 0; +} + +static int bind_desktop_builtin(int slot, const char *name) { + int a = find_app_by_name(name); + if (a < 0) return 0; + g_apps[a].display_name = g_ud_disp[slot]; + desk_place(a); + slog("desktop shortcut -> builtin"); + slog(g_ud_disp[slot]); + return 1; +} + static void udesk_parse_lnk(int slot, const u8 *data, u32 size) { const char *lines[4]; int nlen[4]; @@ -181,19 +269,24 @@ static void udesk_parse_lnk(int slot, const u8 *data, u32 size) { g_ud_name[slot][0] = 'a' + (char)slot; g_ud_name[slot][1] = 0; } - if (nlen[0] > 0 && udesk_ascii_ok(lines[0])) - lapp_field_copy(g_ud_disp[slot], 16, lines[0], nlen[0]); + /* 第一行是桌面标签,允许 UTF-8(文件管理 / 终端 等) */ + if (nlen[0] > 0) + lapp_field_copy(g_ud_disp[slot], 48, lines[0], nlen[0]); else - kstrcpy(g_ud_disp[slot], g_ud_name[slot], 16); + kstrcpy(g_ud_disp[slot], g_ud_name[slot], 48); lapp_field_copy(kind, 16, lines[1], nlen[1]); g_ud_path[slot][0] = 0; g_ud_elf[slot][0] = 0; - if (fat32_lfn_streq_ci(kind, "LINUX") || (n >= 3 && lines[2][0] == '/')) { + /* 种类优先。第三行路径经常以 '/' 开头,不能因此把 ELF / NATIVE 当成 Linux。 */ + if (kind_word(kind, "LINUX")) { if (n >= 3) lapp_field_copy(g_ud_path[slot], 96, lines[2], nlen[2]); - } else if (fat32_lfn_streq_ci(kind, "NATIVE")) { - return; + } else if (kind_word(kind, "NATIVE")) { + if (bind_desktop_builtin(slot, g_ud_name[slot])) return; } else { + const char *bn = kind_builtin_name(kind); + if (bn && bind_desktop_builtin(slot, bn)) return; + if (bind_desktop_builtin(slot, g_ud_name[slot])) return; if (nlen[1] >= 11) lapp_field_copy(g_ud_elf[slot], 12, lines[1], 11); else @@ -201,16 +294,20 @@ static void udesk_parse_lnk(int slot, const u8 *data, u32 size) { g_ud_elf[slot][11] = 0; } if (udesk_name_taken(g_ud_name[slot])) return; - if (!g_ud_path[slot][0] && !g_ud_elf[slot][0]) return; + if (!kind_word(kind, "NATIVE") && !g_ud_path[slot][0] && !g_ud_elf[slot][0]) return; if (g_app_count >= MAX_APPS) return; - g_apps[g_app_count] = (app_descriptor){ - g_ud_name[slot], g_ud_disp[slot], 720, 480, - 0, 0, 0, 0, - g_ud_elf[slot][0] ? g_ud_elf[slot] : 0, - g_ud_path[slot][0] ? g_ud_path[slot] : 0, 0, 0, 0, - 1 /* user_desktop:桌面只显示 .lnk */ - }; - g_app_count++; + { + int id = g_app_count; + g_apps[id] = (app_descriptor){ + g_ud_name[slot], g_ud_disp[slot], 720, 480, + 0, 0, 0, 0, + g_ud_elf[slot][0] ? g_ud_elf[slot] : 0, + g_ud_path[slot][0] ? g_ud_path[slot] : 0, 0, 0, 0, + 0 + }; + g_app_count++; + desk_place(id); + } slog("user/desktop icon:"); slog(g_ud_disp[slot]); (void)i; @@ -220,6 +317,7 @@ static void register_user_desktop_icons(void) { u32 dclus = 0; int i; g_ud_nfiles = 0; + g_desk_n = 0; if (f32_find_path_dir_lfn_w("user/desktop", &dclus) != 0) { slog("user/desktop missing, skip shared icons"); return; @@ -369,6 +467,203 @@ static void register_pe_apps(void) { } } +/* ============================================================ + * M3: STARTMENU.CNF 开始菜单注册表 + * FAT32 根目录 STARTMENU.CNF(8.3: STARTMENUCNF),每行一个菜单项: + * NAME|显示名|path|args(args 可省略) + * path 四种: + * builtin: → 已注册内置应用(editor/calc/fileman/settings/...) + * program/xxx.exe → GUI PE 应用(run_windowed 桌面嵌入,g++ 编译) + * bin/xxx.exe → PE 应用(同上;CLI exe 请从 cmd 运行,不入菜单) + * bin/xxx.elf → ELF 工具(按路径全屏加载) + * /guest/path → Linux guest 应用(exec_async) + * 显示名允许 UTF-8 中文(瓦片标签);NAME 为 ASCII(窗口标题)。 + * 缺失/为空时回退:全部已注册应用进开始菜单。 + * ============================================================ */ + +int g_sm_ids[SM_MAX_APPS]; +char g_sm_labels[SM_MAX_APPS][48]; +int g_sm_count = 0; + +static char g_smn_name[SM_MAX_APPS][12]; +static char g_smn_path[SM_MAX_APPS][96]; +static char g_smn_args[SM_MAX_APPS][96]; + +static void sm_add(int app_id, const char *label) { + if (g_sm_count >= SM_MAX_APPS) return; + if (app_id < 0 || app_id >= g_app_count) return; + g_sm_ids[g_sm_count] = app_id; + if (label && label[0]) + lapp_field_copy(g_sm_labels[g_sm_count], 48, label, kstrlen(label)); + else + kstrcpy(g_sm_labels[g_sm_count], g_apps[app_id].display_name, 48); + g_sm_count++; +} + +static int str_eq(const char *a, const char *b) { + int i = 0; + while (a[i] && b[i] && a[i] == b[i]) i++; + return a[i] == 0 && b[i] == 0; +} + +static int str_ends_with_ci(const char *s, const char *suffix) { + int sl = kstrlen(s), fl = kstrlen(suffix); + if (sl < fl) return 0; + for (int i = 0; i < fl; i++) { + char c = s[sl - fl + i], d = suffix[i]; + if (c >= 'a' && c <= 'z') c = (char)(c - 32); + if (d >= 'a' && d <= 'z') d = (char)(d - 32); + if (c != d) return 0; + } + return 1; +} + +/* 已有同 pe_file / elf_path / linux_path / name 的注册项 → 复用 id */ +static int sm_find_existing(const char *name, const char *path) { + for (int i = 0; i < g_app_count; i++) { + const app_descriptor *a = &g_apps[i]; + if (a->pe_file && str_eq(a->pe_file, path)) return i; + if (a->elf_path && str_eq(a->elf_path, path)) return i; + if (a->linux_path && str_eq(a->linux_path, path)) return i; + } + return find_app_by_name(name); +} + +static void sm_register_entry(int slot) { + const char *name = g_smn_name[slot]; + const char *path = g_smn_path[slot]; + const char *args = g_smn_args[slot]; + const char *label = g_sm_labels[slot]; + if (!name[0] || !path[0]) return; + + /* builtin: → 绑定已注册应用 */ + if (path[0]=='b' && path[1]=='u' && path[2]=='i' && path[3]=='l' && + path[4]=='t' && path[5]=='i' && path[6]=='n' && path[7]==':') { + int a = find_app_by_name(path + 8); + if (a >= 0) { + sm_add(a, label); + } else { + slog("[startmenu] builtin not found:"); + slog(path + 8); + } + return; + } + + /* /guest → Linux guest 应用 */ + if (path[0] == '/') { + int a = sm_find_existing(name, path); + if (a >= 0) { sm_add(a, label); return; } + if (g_app_count >= MAX_APPS) return; + int id = g_app_count; + g_apps[id] = (app_descriptor){ + name, name, 1024, 768, + 0, 0, 0, 0, 0, + path, args[0] ? args : 0 + }; + g_app_count++; + sm_add(id, label); + return; + } + + /* *.exe → PE 应用(GUI,run_windowed) */ + if (str_ends_with_ci(path, ".exe")) { + int a = sm_find_existing(name, path); + if (a >= 0) { sm_add(a, label); return; } + if (g_app_count >= MAX_APPS) return; + int id = g_app_count; + g_apps[id] = (app_descriptor){ + name, name, 760, 560, + 0, 0, pe_win_on_draw, 0, 0, 0, 0, + path, args[0] ? args : 0 + }; + g_app_count++; + sm_add(id, label); + return; + } + + /* *.elf → 按路径加载的外部 ELF 工具(bin/xxx.elf) */ + if (str_ends_with_ci(path, ".elf")) { + int a = sm_find_existing(name, path); + if (a >= 0) { sm_add(a, label); return; } + if (g_app_count >= MAX_APPS) return; + int id = g_app_count; + g_apps[id] = (app_descriptor){ + name, name, 800, 560, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + g_apps[id].elf_path = path; + g_app_count++; + sm_add(id, label); + return; + } + + /* 兜底:按 NAME 找已注册应用 */ + { + int a = find_app_by_name(name); + if (a >= 0) sm_add(a, label); + } +} + +static void register_startmenu_apps(void) { + u8 *data = 0; + u32 size = 0; + char n11[11]; + + g_sm_count = 0; + if (f32_name_to_83_w("STARTMENU.CNF", n11) == 0 && + f32_read_root_file_w(n11, &data, &size) == 0 && data && size) { + int slot = 0; + u32 pos = 0; + while (pos < size && slot < SM_MAX_APPS) { + u32 eol = pos; + while (eol < size && data[eol] != '\n' && data[eol] != '\r') eol++; + u32 line_len = eol - pos; + u32 next = eol; + while (next < size && (data[next] == '\n' || data[next] == '\r')) next++; + + if (line_len == 0 || data[pos] == '#') { pos = next; continue; } + + /* 按 '|' 切 4 段:NAME|DISPLAY|PATH|ARGS */ + const char *line = (const char *)data + pos; + int f0 = -1, f1 = -1, f2 = -1; + for (u32 i = 0; i < line_len; i++) { + if (line[i] == '|') { + if (f0 < 0) f0 = (int)i; + else if (f1 < 0) f1 = (int)i; + else if (f2 < 0) { f2 = (int)i; break; } + } + } + if (f0 > 0 && f1 > f0) { + int path_beg = f1 + 1; + int path_end = (f2 > path_beg) ? f2 : (int)line_len; + int args_beg = (f2 > path_beg) ? f2 + 1 : (int)line_len; + + lapp_field_copy(g_smn_name[slot], 12, line, f0); + lapp_field_copy(g_sm_labels[slot], 48, line + f0 + 1, f1 - f0 - 1); + lapp_field_copy(g_smn_path[slot], 96, line + path_beg, path_end - path_beg); + if (args_beg < (int)line_len) + lapp_field_copy(g_smn_args[slot], 96, line + args_beg, (int)line_len - args_beg); + else + g_smn_args[slot][0] = 0; + slot++; + } + pos = next; + } + for (int i = 0; i < slot; i++) sm_register_entry(i); + slog_num("[startmenu] config entries=", g_sm_count); + return; + } + + /* 无配置文件:全部已注册应用入菜单 */ + for (int i = 0; i < g_app_count && g_sm_count < SM_MAX_APPS; i++) + sm_add(i, g_apps[i].display_name); + slog("[startmenu] no config, fallback to all apps"); +} + +/* ============================================================ + * 应用注册 + * ============================================================ */ + void register_apps(void) { /* 命令行1:shell.elf(原生命令行 + 可运行 Linux 程序)*/ g_apps[0] = (app_descriptor){ @@ -388,8 +683,8 @@ void register_apps(void) { }; g_app_count++; g_apps[3] = (app_descriptor){ - "fileman", "Files", 500, 400, - 0, 0, 0, 0, "FILEMAN ELF" + "fileman", "Files", 880, 560, + fileman_on_create, fileman_on_event, fileman_on_draw, fileman_on_destroy, 0 }; g_app_count++; g_apps[4] = (app_descriptor){ @@ -419,16 +714,40 @@ void register_apps(void) { /* P5d: Windows PE 应用,来自 PEAPPS.CNF */ register_pe_apps(); register_user_desktop_icons(); + /* M3: 开始菜单注册(STARTMENU.CNF;须在全部来源注册后跑以支持 builtin:/path 复用) */ + register_startmenu_apps(); } /* M2: 桌面图标 = user/desktop/*.lnk 用户快捷方式(user_desktop 标记)。 * 内置应用(editor/calc/settings…)不落桌面——经任务栏/开始菜单启动。 * 布局沿用 canvas padding 24px,列宽 104px,gap 18px,左→右换行。 */ +static void place_desktop_icon(int app_id, int *x, int *y, int pad, int max_x) { + if (g_icon_count >= MAX_ICONS) return; + if (app_id < 0 || app_id >= g_app_count) return; + if (*x > pad && *x + KATE_ICON_W > max_x) { + *x = pad; + *y += KATE_ICON_H + KATE_ICON_GAP; + } + g_icons[g_icon_count].app_id = app_id; + g_icons[g_icon_count].x = *x; + g_icons[g_icon_count].y = *y; + g_icons[g_icon_count].label = g_apps[app_id].display_name; + g_icons[g_icon_count].selected = 0; + g_icon_count++; + *x += KATE_ICON_W + KATE_ICON_GAP; +} + void setup_desktop_icons(void) { int pad = KATE_ICON_PAD; /* 24 */ int x = pad; int y = g_fluent ? (pad + 8) : (KATE_TOPBAR_H + KATE_PAGE_PAD + pad); int max_x = (int)g_fb_w - pad; /* canvas 右缘 */ + int k; + if (g_desk_n > 0) { + for (k = 0; k < g_desk_n; k++) + place_desktop_icon(g_desk_order[k], &x, &y, pad, max_x); + return; + } for (int i = 0; i < g_app_count; i++) { if (g_icon_count >= MAX_ICONS) break; if (!g_apps[i].user_desktop) continue; /* M2: 只显示 .lnk 快捷方式 */ diff --git a/CODE/desktop/apps_builtin.c b/CODE/desktop/apps_builtin.c index 6171361..a763dc5 100644 --- a/CODE/desktop/apps_builtin.c +++ b/CODE/desktop/apps_builtin.c @@ -390,6 +390,68 @@ void editor_on_draw(void *state, app_ctx *ctx) { } void editor_on_destroy(void *state) { (void)state; } +/* 文件资源管理器把文本文件送进窗口编辑器(单实例 ADDR_EDWIN)。 */ +void editor_open_text(const char *disp, const u8 *data, u32 size) { + int id = -1; + int found = 0; + int ascii = 1; + editor_state *s; + u32 cap = EDITOR_BUF_SIZE - 1; + if (!disp) disp = ""; + if (!data) { data = (const u8 *)""; size = 0; } + for (int i = 0; i < g_app_count; i++) { + const char *n = g_apps[i].name; + if (!n || !g_apps[i].on_create) continue; + if (n[0]=='e' && n[1]=='d' && n[2]=='i' && n[3]=='t' && + n[4]=='o' && n[5]=='r' && n[6]==0) { id = i; break; } + } + if (id < 0) { + slog("fileman: editor missing"); + return; + } + launch_app(id); + for (int i = 0; i < g_win_count; i++) { + if (g_windows[i].app_id == id && g_windows[i].visible) { found = 1; break; } + } + if (!found) return; + + s = (editor_state *)ADDR_EDWIN; + if (size > cap) size = cap; + s->text_len = 0; + for (u32 k = 0; k < size && s->text_len < (int)cap; k++) { + char c = (char)data[k]; + if (c == '\r') continue; + if ((unsigned char)c >= 32 || c == '\n' || c == '\t') + s->text[s->text_len++] = c; + } + s->text[s->text_len] = 0; + s->cursor_pos = 0; + s->cursor_line = 0; + s->cursor_col = 0; + s->scroll_y = 0; + s->modified = 0; + s->has_file = 0; + s->disp[0] = 0; + + for (int k = 0; disp[k]; k++) { + if ((unsigned char)disp[k] < 32 || (unsigned char)disp[k] > 126) ascii = 0; + } + for (int i = 0; i < g_win_count; i++) { + desktop_window *w = &g_windows[i]; + int p = 0; + const char *prefix = "Editor"; + if (w->app_id != id || !w->visible) continue; + while (prefix[p] && p < 46) { w->title[p] = prefix[p]; p++; } + if (ascii && disp[0]) { + const char *sep = " - "; + while (*sep && p < 46) w->title[p++] = *sep++; + for (int k = 0; disp[k] && p < 47; k++) w->title[p++] = disp[k]; + } + w->title[p] = 0; + w->dirty = 1; + } +} + /* ============================================================ * 计算器 (Calculator) * ============================================================ */ diff --git a/CODE/desktop/compositor.c b/CODE/desktop/compositor.c index f55ee19..c605cae 100644 --- a/CODE/desktop/compositor.c +++ b/CODE/desktop/compositor.c @@ -171,6 +171,9 @@ void redraw_all(void) { /* 亮度调整 */ apply_brightness(); + /* 整蛊叠加层(在光标之前,覆盖页面与任务栏) */ + prank_draw(&g_fb); + /* 鼠标光标 */ cursor_save_bg(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y); @@ -216,6 +219,9 @@ static void redraw_all_fluent(void) { /* 亮度调整 */ apply_brightness(); + /* 整蛊叠加层(在光标之前,覆盖页面与任务栏) */ + prank_draw(&g_fb); + /* 鼠标光标 */ cursor_save_bg(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y); diff --git a/CODE/desktop/desktop.h b/CODE/desktop/desktop.h index 7729979..c14df6e 100644 --- a/CODE/desktop/desktop.h +++ b/CODE/desktop/desktop.h @@ -15,6 +15,8 @@ * ascii_font.c ASCII 位图字体单副本 * zhfont.c 中文位图字库(.dbf)加载与渲染(M1) * wallpaper.c 壁纸背景层缓存(M1) + * files.c 窗口化文件资源管理器(目录遍历 + 打开文本) + * startmenu.c M3 Win11 开始菜单(搜索 + 应用瓦片 + 电源行) * * 依赖单向:main → shell → (pages/appreg/launcher/lxc) → wm/compositor → input。 * 全局状态按"最 owning 模块定义、此处 extern"原则组织。 @@ -100,7 +102,7 @@ static __inline__ u64 rdtsc(void) { #define MAX_CPAGES 4 /* 自定义页上限 */ #define MAX_FILES 24 /* FILES 面板条目上限 */ #define MAX_ICONS 24 -#define MAX_APPS 24 +#define MAX_APPS 32 /* M3: 开始菜单注册 + 内置/LINUXAPP/PEAPPS/lnk 并存 */ /* 应用固定状态地址(单实例策略 → 每类应用唯一实例) */ #define ADDR_TERM1 0x4000000ULL @@ -109,6 +111,7 @@ static __inline__ u64 rdtsc(void) { #define ADDR_EDDASH 0x5000000ULL #define ADDR_EDWIN 0x5100000ULL #define ADDR_CALC 0x6000000ULL +#define ADDR_FILEMAN 0x6800000ULL /* 文件资源管理器状态(calc 与 sprite 之间) */ /* 双缓冲:渲染到 sprite buffer,完成后一次性 flip 到真实 framebuffer */ #define SPRITE_BUF_ADDR 0x7000000ULL /* 112MB,在 identity-mapped 区域 */ @@ -187,6 +190,9 @@ typedef struct { const char *pe_args; /* M2: 1 = 来自 user/desktop/*.lnk 的用户快捷方式(桌面只显示这些) */ int user_desktop; + /* M3: 按路径启动的外部 ELF(STARTMENU.CNF 的 bin/xxx.elf 条目)。 + * 与 elf_name(FAT32 根 8.3 名)互斥,launch_app 优先走 elf_path。 */ + const char *elf_path; } app_descriptor; /* 桌面图标 */ @@ -326,6 +332,12 @@ extern desktop_icon g_icons[MAX_ICONS]; extern int g_icon_count; extern int g_vscode_app_id; /* 第一个名为 "vscode" 的注册项 */ +/* ---- appreg.c:M3 开始菜单注册表(STARTMENU.CNF,缺失时回退全量) ---- */ +#define SM_MAX_APPS 24 /* 开始菜单条目上限 */ +extern int g_sm_ids[SM_MAX_APPS]; /* → g_apps 下标 */ +extern char g_sm_labels[SM_MAX_APPS][48];/* 瓦片中文标签(UTF-8) */ +extern int g_sm_count; + /* ---- launcher.c:PE 运行时 ---- */ extern int g_pe_running; extern int g_pe_win_id; @@ -463,15 +475,25 @@ void *calc_on_create(app_ctx *ctx); void calc_on_event(void *state, app_ctx *ctx, desktop_event *ev); void calc_on_draw(void *state, app_ctx *ctx); void calc_on_destroy(void *state); +void editor_open_text(const char *disp, const u8 *data, u32 size); + +/* ---- files.c:窗口化文件资源管理器 ---- */ +void *fileman_on_create(app_ctx *ctx); +void fileman_on_event(void *state, app_ctx *ctx, desktop_event *ev); +void fileman_on_draw(void *state, app_ctx *ctx); +void fileman_on_destroy(void *state); /* ---- appreg.c ---- */ void register_apps(void); void setup_desktop_icons(void); void draw_app_icon(du_context *ctx, int app_id, int cx, int cy); void draw_circle_outline(du_context *ctx, int cx, int cy, int r, u32 color); +/* M3: app_id → Fluent 图标(返回 wf_icon_id,int 传递避免头依赖) */ +int desktop_app_icon(int app_id); /* ---- launcher.c ---- */ void launch_external_elf(const char *name11); +void launch_external_elf_path(const char *path); void fill_app_ctx(app_ctx *ctx, desktop_window *win); void launch_app(int app_id); void launch_pe_app(int app_id); @@ -510,9 +532,21 @@ void taskbar_draw(void); int taskbar_click(int mx, int my); int taskbar_init(int (*read_to)(const char *, unsigned char *, unsigned int, unsigned int *)); + +/* ---- startmenu.c(M3 开始菜单,taskbar 开始按钮弹出) ---- */ +void startmenu_open(void); +void startmenu_close(void); +void startmenu_toggle(void); +void startmenu_draw(void); /* taskbar_draw 末尾叠加 */ +int startmenu_click(int mx, int my); /* 1=已消费(面板内) */ +int startmenu_key(u8 sc, int shift); /* 1=已消费(模态输入) */ +extern int g_sm_open; + /* ---- taskbar.c WiFi 快捷卡片 ---- */ extern int g_wifi_open; /* 卡片是否展开 */ int wifi_key(u8 sc, int shift); /* shell.c handle_key 路由;1=已消费 */ +/* ---- taskbar.c 快速设置卡片(音量/亮度) ---- */ +extern int g_qs_open; /* M2: logo 纹理 */ extern int g_tb2_start_rect[4]; extern int g_tb2_tv_rect[4]; @@ -523,6 +557,25 @@ extern int g_tb2_vol_rect[4]; extern int g_tb2_pwr_rect[4]; extern int g_tb2_clk_rect[4]; +/* ---- prank.c:整蛊系统(FUCK [prank] 段控制) ---- */ +extern int g_prank_enabled; /* 总开关 */ +extern int g_prank_boot_demo; /* 进桌面后演示秒数,0=关 */ +extern int g_prank_interval; /* 使用中自动整蛊间隔(秒),0=关 */ +extern int g_prank_fuck; /* FUCK 跟随鼠标遮屏 */ +extern int g_prank_bsod; /* 假蓝屏 */ +extern int g_prank_invert; /* 反色闪屏 */ +extern int g_prank_hang; /* 假"未响应"对话框 */ +extern int g_prank_garble; /* 打字乱码 */ +extern int g_prank_fly; /* 任务栏图标乱飞 */ +extern int g_prank_shutdown; /* 假"系统即将关机"倒计时 */ +void prank_config_line(const char *line, int len); +void prank_boot_demo_start(void); +void prank_tick(void); +int prank_active(void); +int prank_wants_redraw(void); +u8 prank_filter_key(u8 sc); +void prank_draw(du_context *ctx); + /* ---- main.c ---- */ void dsk_entry(const dsk_boot_context *ctx); diff --git a/CODE/desktop/f32io.c b/CODE/desktop/f32io.c index 0654354..97ef432 100644 --- a/CODE/desktop/f32io.c +++ b/CODE/desktop/f32io.c @@ -36,6 +36,10 @@ int f32_read_path_lfn_w(const char *path, u8 **out_data, u32 *out_size) { return f32_read_path_lfn(path, out_data, out_size); } +int f32_read_file_by_clus_w(u32 clus, u32 size, u8 *out_buf, u32 buf_cap) { + return f32_read_file_by_clus(clus, size, out_buf, buf_cap); +} + int f32_read_path_lfn_to_w(const char *path, u8 *dst, u32 cap, u32 *out_size) { int rc = f32_read_path_lfn_to(path, dst, cap, out_size); if (rc != 0) slog_num("[f32io] read_to rc=", rc); /* M1 调试:定位失败步骤 */ diff --git a/CODE/desktop/f32io.h b/CODE/desktop/f32io.h index b018bca..a5ecf3f 100644 --- a/CODE/desktop/f32io.h +++ b/CODE/desktop/f32io.h @@ -26,10 +26,11 @@ int f32_read_root_file_w(const char *name11, u8 **out_data, u32 *out_size); int f32_write_root_file_w(const char *name11, const u8 *data, u32 size); int f32_list_root_w(f32_list_cb_fn emit, void *user_data); -/* 目录列举 / 路径定位 / 长路径读取 */ +/* 目录列举 / 路径定位 / 长路径读取 / 按簇读文件 */ int f32_list_dir_w(u32 dir_clus, f32_list_entry_fn emit, void *user_data); int f32_find_path_dir_lfn_w(const char *path, u32 *out_clus); int f32_read_path_lfn_w(const char *path, u8 **out_data, u32 *out_size); +int f32_read_file_by_clus_w(u32 clus, u32 size, u8 *out_buf, u32 buf_cap); /* 流式直读到指定缓冲(绕过 f32_data 4MB 上限,zhfont 字库加载用) */ int f32_read_path_lfn_to_w(const char *path, u8 *dst, u32 cap, u32 *out_size); diff --git a/CODE/desktop/files.c b/CODE/desktop/files.c new file mode 100644 index 0000000..2054279 --- /dev/null +++ b/CODE/desktop/files.c @@ -0,0 +1,586 @@ +/* files.c — 窗口化文件资源管理器 + * + * 在桌面窗口里遍历 FAT32(含子目录与长文件名)。 + * 单击选中,双击或 Enter 进入目录;文本文件交给内置编辑器。 + * 左侧地点:此电脑 / 用户 / 桌面 / 系统。 + */ +#include "desktop.h" +#include "zhfont.h" + +#define FM_MAX 160 +#define FM_NAME 64 +#define FM_DEPTH 12 +#define FM_TB 34 +#define FM_HDR 22 +#define FM_ST 22 +#define FM_ROW 24 +#define FM_SIDE 132 +#define FM_READ_MAX 60000u + +typedef struct { + char name[FM_NAME]; + u32 clus; + u32 size; + u8 is_dir; + u8 is_up; +} fm_ent; + +typedef struct { + fm_ent ents[FM_MAX]; + int count; + int depth; + int sel; + int scroll; + int last_row; + u64 last_tsc; + u32 path_clus[FM_DEPTH]; + char path_name[FM_DEPTH][FM_NAME]; + char path_disp[160]; + char status[96]; +} fm_state; + +static u8 g_fm_blob[FM_READ_MAX]; + +typedef struct { + u32 bg, side, bar, line, text, dim, sel, accent, folder; +} fm_pal; + +static void fm_colors(fm_pal *p) { + if (g_fluent) { + p->bg = 0xFF1E1E1Eu; + p->side = 0xFF252526u; + p->bar = 0xFF2D2D2Du; + p->line = 0xFF3A3A3Au; + p->text = 0xFFF3F3F3u; + p->dim = 0xFF9A9A9Au; + p->sel = 0xFF094771u; + p->accent = 0xFF60CDFFu; + p->folder = 0xFFE8B84Au; + } else { + p->bg = KS_BG_PRIMARY; + p->side = KS_BG_SECONDARY; + p->bar = KS_BG_TERTIARY; + p->line = KS_BORDER; + p->text = KS_TEXT_PRIMARY; + p->dim = KS_TEXT_DIM; + p->sel = KS_ACCENT_DIM; + p->accent = KS_ACCENT; + p->folder = KS_ACCENT2; + } +} + +static void fm_copy(char *dst, int cap, const char *src) { + int i = 0; + if (cap < 1) return; + while (src[i] && i < cap - 1) { dst[i] = src[i]; i++; } + dst[i] = 0; +} + +static void fm_set_status(fm_state *s, const char *msg) { + fm_copy(s->status, (int)sizeof(s->status), msg); +} + +static void fm_fit(char *dst, int cap, const char *src, int max_px) { + fm_copy(dst, cap, src); + int n = kstrlen(dst); + while (n > 1 && zh_string_width(dst, 0) > max_px) { + dst[--n] = 0; + while (n > 0 && ((unsigned char)dst[n - 1] & 0xC0) == 0x80) dst[--n] = 0; + } +} + +static int fm_name_cmp(const char *a, const char *b) { + while (*a && *b) { + unsigned char ca = (unsigned char)*a, cb = (unsigned char)*b; + if (ca >= 'a' && ca <= 'z') ca = (unsigned char)(ca - 32); + if (cb >= 'a' && cb <= 'z') cb = (unsigned char)(cb - 32); + if (ca != cb) return ca < cb ? -1 : 1; + a++; b++; + } + if (*a) return 1; + if (*b) return -1; + return 0; +} + +static void fm_sort(fm_state *s) { + for (int i = 1; i < s->count; i++) { + fm_ent key = s->ents[i]; + int j = i - 1; + while (j >= 0) { + int dir_first = (s->ents[j].is_dir != key.is_dir) + ? (key.is_dir && !s->ents[j].is_dir) + : (fm_name_cmp(key.name, s->ents[j].name) < 0); + if (!dir_first) break; + s->ents[j + 1] = s->ents[j]; + j--; + } + s->ents[j + 1] = key; + } +} + +static void fm_build_path(fm_state *s) { + int p = 0; + s->path_disp[p++] = '/'; + for (int d = 0; d < s->depth && p < (int)sizeof(s->path_disp) - 2; d++) { + const char *n = s->path_name[d]; + while (*n && p < (int)sizeof(s->path_disp) - 2) s->path_disp[p++] = *n++; + s->path_disp[p++] = '/'; + } + s->path_disp[p] = 0; +} + +static void fm_status_count(fm_state *s) { + int n = 0; + char num[12]; + int p = 0; + for (int i = 0; i < s->count; i++) if (!s->ents[i].is_up) n++; + u32dec((u32)n, num); + while (num[p] && p < 80) { s->status[p] = num[p]; p++; } + { + const char *b = " 个项目"; + while (*b && p < (int)sizeof(s->status) - 1) s->status[p++] = *b++; + } + s->status[p] = 0; +} + +static int fm_collect(const f32_entry *e, void *ud) { + fm_state *s = (fm_state *)ud; + const char *nm; + fm_ent *f; + if (s->count >= FM_MAX) return 1; + nm = (e->has_lfn && e->long_name[0]) ? e->long_name : e->name; + if (!nm || !nm[0]) return 0; + f = &s->ents[s->count]; + fm_copy(f->name, FM_NAME, nm); + f->clus = e->clus; + f->size = e->size; + f->is_dir = e->is_dir ? 1 : 0; + f->is_up = 0; + s->count++; + return 0; +} + +static void fm_insert_up(fm_state *s) { + if (s->depth <= 0) return; + if (s->count >= FM_MAX) s->count = FM_MAX - 1; + for (int i = s->count; i > 0; i--) s->ents[i] = s->ents[i - 1]; + fm_copy(s->ents[0].name, FM_NAME, ".."); + s->ents[0].clus = 0; + s->ents[0].size = 0; + s->ents[0].is_dir = 1; + s->ents[0].is_up = 1; + s->count++; +} + +static int fm_reload(fm_state *s) { + u32 clus; + s->count = 0; + s->sel = 0; + s->scroll = 0; + s->last_row = -1; + fm_build_path(s); + if (!g_block_read) { + fm_set_status(s, "无块设备"); + return -1; + } + clus = (s->depth == 0) ? 0 : s->path_clus[s->depth - 1]; + if (f32_list_dir_w(clus, fm_collect, s) != 0) { + s->count = 0; + fm_set_status(s, "无法读取目录"); + slog("fileman list failed"); + return -1; + } + fm_sort(s); + fm_insert_up(s); + fm_status_count(s); + return 0; +} + +static void fm_up(fm_state *s) { + if (s->depth <= 0) return; + s->depth--; + fm_reload(s); +} + +static int fm_enter_idx(fm_state *s, int idx) { + char name[FM_NAME]; + u32 clus; + if (idx < 0 || idx >= s->count) return -1; + if (s->ents[idx].is_up) { fm_up(s); return 0; } + if (!s->ents[idx].is_dir) return -1; + if (s->depth >= FM_DEPTH) { fm_set_status(s, "目录太深"); return -1; } + clus = s->ents[idx].clus; + if (clus < 2) { fm_set_status(s, "无法进入目录"); return -1; } + fm_copy(name, FM_NAME, s->ents[idx].name); + s->path_clus[s->depth] = clus; + fm_copy(s->path_name[s->depth], FM_NAME, name); + s->depth++; + if (fm_reload(s) != 0) { + s->depth--; + fm_reload(s); + fm_set_status(s, "无法读取目录"); + return -1; + } + slog("fileman enter dir"); + return 0; +} + +static int fm_enter_named(fm_state *s, const char *name) { + for (int i = 0; i < s->count; i++) { + if (s->ents[i].is_up || !s->ents[i].is_dir) continue; + if (fat32_lfn_streq_ci(s->ents[i].name, name)) + return fm_enter_idx(s, i); + } + return -1; +} + +static void fm_place(fm_state *s, int place) { + const char *a = 0; + const char *b = 0; + s->depth = 0; + if (fm_reload(s) != 0) return; + if (place == 1) a = "user"; + else if (place == 2) { a = "user"; b = "desktop"; } + else if (place == 3) a = "system"; + if (a && fm_enter_named(s, a) != 0) { fm_set_status(s, "路径不存在"); return; } + if (b && fm_enter_named(s, b) != 0) fm_set_status(s, "路径不存在"); +} + +static int fm_place_of(const fm_state *s) { + if (s->depth == 0) return 0; + if (s->depth == 1 && fat32_lfn_streq_ci(s->path_name[0], "user")) return 1; + if (s->depth == 1 && fat32_lfn_streq_ci(s->path_name[0], "system")) return 3; + if (s->depth == 2 && fat32_lfn_streq_ci(s->path_name[0], "user") && + fat32_lfn_streq_ci(s->path_name[1], "desktop")) return 2; + return -1; +} + +static int fm_looks_text(const u8 *p, u32 n) { + u32 sample, bad = 0; + if (n == 0) return 1; + sample = n > 512 ? 512 : n; + for (u32 i = 0; i < sample; i++) { + u8 c = p[i]; + if (c == 0) return 0; + if (c < 9 || (c > 13 && c < 32) || c == 127) bad++; + } + return bad * 10 < sample; +} + +static void fm_open_file(fm_state *s, int idx) { + fm_ent *e; + u32 n; + if (idx < 0 || idx >= s->count) return; + e = &s->ents[idx]; + if (e->size == 0) { + editor_open_text(e->name, (const u8 *)"", 0); + fm_set_status(s, "已在编辑器中打开"); + slog("fileman open file"); + return; + } + if (e->clus < 2) { fm_set_status(s, "读取失败"); return; } + n = e->size; + if (n > FM_READ_MAX) n = FM_READ_MAX; + for (u32 i = 0; i < n; i++) g_fm_blob[i] = 0; + if (f32_read_file_by_clus_w(e->clus, e->size, g_fm_blob, FM_READ_MAX) != 0) { + fm_set_status(s, "读取失败"); + slog("fileman read failed"); + return; + } + if (!fm_looks_text(g_fm_blob, n)) { + fm_set_status(s, "二进制文件,未打开"); + return; + } + editor_open_text(e->name, g_fm_blob, n); + if (e->size > n) fm_set_status(s, "已打开(内容已截断)"); + else fm_set_status(s, "已在编辑器中打开"); + slog("fileman open file"); +} + +static void fm_activate(fm_state *s, int idx) { + if (idx < 0 || idx >= s->count) return; + s->sel = idx; + if (s->ents[idx].is_dir || s->ents[idx].is_up) fm_enter_idx(s, idx); + else fm_open_file(s, idx); +} + +static void fm_reveal(fm_state *s, int vis) { + if (vis < 1) vis = 1; + if (s->count <= 0) { s->sel = 0; s->scroll = 0; return; } + if (s->sel < 0) s->sel = 0; + if (s->sel >= s->count) s->sel = s->count - 1; + if (s->sel < s->scroll) s->scroll = s->sel; + if (s->sel >= s->scroll + vis) s->scroll = s->sel - vis + 1; + if (s->scroll < 0) s->scroll = 0; +} + +static void fm_geom(const app_ctx *ctx, int *side, int *lx, int *ly, + int *lw, int *lh, int *vis) { + int sidew = (ctx->client_w < 420) ? 0 : FM_SIDE; + int body = ctx->client_h - FM_TB - FM_ST; + if (body < FM_HDR + FM_ROW) body = FM_HDR + FM_ROW; + *side = sidew; + *lx = ctx->client_x + sidew; + *ly = ctx->client_y + FM_TB + FM_HDR; + *lw = ctx->client_w - sidew; + *lh = body - FM_HDR; + *vis = *lh / FM_ROW; + if (*vis < 1) *vis = 1; +} + +static void fm_size_str(char *out, u32 sz) { + char num[12]; + const char *unit; + u32 v; + int j = 0; + if (sz < 1024u) { v = sz; unit = " B"; } + else if (sz < 1024u * 1024u) { v = sz / 1024u; unit = " K"; } + else { v = sz / (1024u * 1024u); unit = " M"; } + u32dec(v, num); + while (num[j] && j < 12) { out[j] = num[j]; j++; } + while (*unit && j < 15) out[j++] = *unit++; + out[j] = 0; +} + +static const char *fm_kind(const fm_ent *e) { + if (e->is_up) return "上级"; + if (e->is_dir) return "文件夹"; + if (fat32_lfn_ends_with_ci(e->name, ".txt") || + fat32_lfn_ends_with_ci(e->name, ".md") || + fat32_lfn_ends_with_ci(e->name, ".cnf") || + fat32_lfn_ends_with_ci(e->name, ".json") || + fat32_lfn_ends_with_ci(e->name, ".ini") || + fat32_lfn_ends_with_ci(e->name, ".log") || + fat32_lfn_ends_with_ci(e->name, ".h") || + fat32_lfn_ends_with_ci(e->name, ".c")) + return "文本"; + if (fat32_lfn_ends_with_ci(e->name, ".elf") || + fat32_lfn_ends_with_ci(e->name, ".exe") || + fat32_lfn_ends_with_ci(e->name, ".drv")) + return "程序"; + if (fat32_lfn_ends_with_ci(e->name, ".png") || + fat32_lfn_ends_with_ci(e->name, ".rgba")) + return "图片"; + return "文件"; +} + +static int fm_hit_row(const app_ctx *ctx, int my, int scroll) { + int side, lx, ly, lw, lh, vis, row; + fm_geom(ctx, &side, &lx, &ly, &lw, &lh, &vis); + (void)side; (void)lx; (void)lw; + if (my < ly) return -1; + row = (my - ly) / FM_ROW; + if (row < 0 || row >= vis) return -1; + return scroll + row; +} + +void *fileman_on_create(app_ctx *ctx) { + fm_state *s = (fm_state *)ADDR_FILEMAN; + const char *t = "Files"; + u8 *raw = (u8 *)s; + for (u32 i = 0; i < (u32)sizeof(fm_state); i++) raw[i] = 0; + s->last_row = -1; + fm_reload(s); + for (int i = 0; i < 47; i++) ctx->win->title[i] = 0; + for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i]; + slog("fileman window"); + return s; +} + +void fileman_on_destroy(void *state) { (void)state; } + +void fileman_on_event(void *state, app_ctx *ctx, desktop_event *ev) { + fm_state *s = (fm_state *)state; + int side, lx, ly, lw, lh, vis; + int cx = ctx->client_x; + int cy = ctx->client_y; + fm_geom(ctx, &side, &lx, &ly, &lw, &lh, &vis); + + if (ev->type == EV_KEY_DOWN) { + u8 sc = ev->scancode; + if (sc == 0x48) { + if (s->sel > 0) s->sel--; + } else if (sc == 0x50) { + if (s->sel < s->count - 1) s->sel++; + } else if (sc == 0x49) { + s->sel -= vis; + if (s->sel < 0) s->sel = 0; + } else if (sc == 0x51) { + s->sel += vis; + if (s->count > 0 && s->sel >= s->count) s->sel = s->count - 1; + } else if (sc == 0x47) { + s->sel = 0; + } else if (sc == 0x4F) { + s->sel = (s->count > 0) ? s->count - 1 : 0; + } else if (sc == 0x0E || sc == 0x4B) { + fm_up(s); + } else if (sc == 0x1C || sc == 0x4D) { + fm_activate(s, s->sel); + } else if (sc == 0x3F) { + int keep = s->sel; + fm_reload(s); + s->sel = keep; + if (s->count > 0 && s->sel >= s->count) s->sel = s->count - 1; + } + fm_reveal(s, vis); + ctx->win->dirty = 1; + return; + } + + if (ev->type != EV_MOUSE_DOWN || ev->button != 0) return; + + /* 上级 */ + { + int bw = zh_string_width("上级", 0) + 16; + if (ev->mx >= cx + 8 && ev->mx < cx + 8 + bw && + ev->my >= cy + 6 && ev->my < cy + 6 + 22) { + fm_up(s); + ctx->win->dirty = 1; + return; + } + } + + /* 地点 */ + if (side > 0 && ev->mx >= cx && ev->mx < cx + side && + ev->my >= cy + FM_TB && ev->my < cy + ctx->client_h - FM_ST) { + int rel = ev->my - (cy + FM_TB + 8); + int slot = rel / 28; + if (rel >= 0 && slot >= 0 && slot < 4) fm_place(s, slot); + ctx->win->dirty = 1; + return; + } + + /* 滚动条 */ + if (s->count > vis && ev->mx >= lx + lw - 10 && ev->mx < lx + lw && + ev->my >= ly && ev->my < ly + lh) { + int max_scroll = s->count - vis; + int rel = ev->my - ly; + if (lh > 0 && max_scroll > 0) + s->scroll = rel * max_scroll / lh; + if (s->scroll < 0) s->scroll = 0; + if (s->scroll > max_scroll) s->scroll = max_scroll; + ctx->win->dirty = 1; + return; + } + + /* 列表行:单击选中,双击打开 */ + if (ev->mx >= lx && ev->mx < lx + lw - 10) { + int idx = fm_hit_row(ctx, ev->my, s->scroll); + if (idx >= 0 && idx < s->count) { + int dbl = 0; + u64 now = rdtsc(); + if (s->last_row == idx) { + if (!g_tsc_per_sec || (now - s->last_tsc) < g_tsc_per_sec / 2) + dbl = 1; + } + s->last_row = idx; + s->last_tsc = now; + s->sel = idx; + if (dbl) fm_activate(s, idx); + ctx->win->dirty = 1; + } + } +} + +void fileman_on_draw(void *state, app_ctx *ctx) { + fm_state *s = (fm_state *)state; + fm_pal pal; + int side, lx, ly, lw, lh, vis; + int cx = ctx->client_x; + int cy = ctx->client_y; + int cw = ctx->client_w; + int ch = ctx->client_h; + static const char *places[4] = { "此电脑", "用户", "桌面", "系统" }; + int place = fm_place_of(s); + int up_w; + char fitted[FM_NAME]; + + fm_colors(&pal); + fm_geom(ctx, &side, &lx, &ly, &lw, &lh, &vis); + if (s->scroll < 0) s->scroll = 0; + if (s->count > vis && s->scroll > s->count - vis) s->scroll = s->count - vis; + if (s->count <= vis) s->scroll = 0; + + du_fill_rect(&g_fb, cx, cy, cw, ch, pal.bg); + + /* 工具栏 */ + du_fill_rect(&g_fb, cx, cy, cw, FM_TB, pal.bar); + du_divider_h(&g_fb, cx, cy + FM_TB, cw, pal.line); + up_w = zh_string_width("上级", 0) + 16; + du_fill_rounded_rect(&g_fb, cx + 8, cy + 6, up_w, 22, + (s->depth > 0) ? pal.side : pal.bg, 4); + zh_draw_string(&g_fb, "上级", cx + 16, cy + 8, + (s->depth > 0) ? pal.text : pal.dim, 0); + fm_fit(fitted, (int)sizeof(fitted), s->path_disp[0] ? s->path_disp : "/", + cw - (up_w + 28)); + zh_draw_string(&g_fb, fitted, cx + 8 + up_w + 10, cy + 8, pal.accent, 0); + + /* 地点 */ + if (side > 0) { + du_fill_rect(&g_fb, cx, cy + FM_TB, side, ch - FM_TB - FM_ST, pal.side); + du_fill_rect(&g_fb, cx + side - 1, cy + FM_TB, 1, ch - FM_TB - FM_ST, pal.line); + for (int i = 0; i < 4; i++) { + int y = cy + FM_TB + 8 + i * 28; + if (i == place) + du_fill_rect(&g_fb, cx + 6, y, side - 14, 24, pal.sel); + zh_draw_string(&g_fb, places[i], cx + 14, y + 3, + (i == place) ? pal.text : pal.dim, 0); + } + } + + /* 列头 */ + du_fill_rect(&g_fb, lx, cy + FM_TB, lw, FM_HDR, pal.bar); + zh_draw_string(&g_fb, "名称", lx + 28, cy + FM_TB + 2, pal.dim, 0); + zh_draw_string(&g_fb, "大小", lx + lw - 150, cy + FM_TB + 2, pal.dim, 0); + zh_draw_string(&g_fb, "类型", lx + lw - 64, cy + FM_TB + 2, pal.dim, 0); + du_divider_h(&g_fb, lx, ly, lw, pal.line); + + if (s->count == 0) { + const char *empty = s->status[0] ? s->status : "此文件夹为空"; + zh_draw_string(&g_fb, empty, lx + 16, ly + 12, pal.dim, 0); + } + + for (int i = 0; i < vis; i++) { + int idx = s->scroll + i; + int y, name_w; + char shown[FM_NAME]; + char sz[16]; + if (idx >= s->count) break; + y = ly + i * FM_ROW; + if (idx == s->sel) + du_fill_rect(&g_fb, lx, y, lw - 10, FM_ROW, pal.sel); + if (s->ents[idx].is_dir) { + du_fill_rounded_rect(&g_fb, lx + 8, y + 6, 14, 10, pal.folder, 2); + } else { + du_fill_rect(&g_fb, lx + 8, y + 5, 12, 14, pal.line); + du_rect_outline(&g_fb, lx + 8, y + 5, 12, 14, pal.dim, 1); + } + name_w = lw - 28 - 160; + if (name_w < 40) name_w = 40; + fm_fit(shown, FM_NAME, s->ents[idx].name, name_w); + zh_draw_string(&g_fb, shown, lx + 28, y + 3, pal.text, 0); + if (!s->ents[idx].is_dir && !s->ents[idx].is_up) { + fm_size_str(sz, s->ents[idx].size); + zh_draw_string(&g_fb, sz, lx + lw - 150, y + 3, pal.dim, 0); + } + zh_draw_string(&g_fb, fm_kind(&s->ents[idx]), lx + lw - 64, y + 3, pal.dim, 0); + } + + if (s->count > vis && lh > 0) { + int max_scroll = s->count - vis; + int thumb_h = lh * vis / s->count; + int thumb_y; + if (thumb_h < 16) thumb_h = 16; + if (thumb_h > lh) thumb_h = lh; + thumb_y = ly; + if (max_scroll > 0 && lh > thumb_h) + thumb_y = ly + (lh - thumb_h) * s->scroll / max_scroll; + du_fill_rect(&g_fb, lx + lw - 8, ly, 6, lh, pal.line); + du_fill_rounded_rect(&g_fb, lx + lw - 8, thumb_y, 6, thumb_h, pal.dim, 2); + } + + /* 状态栏 */ + du_fill_rect(&g_fb, cx, cy + ch - FM_ST, cw, FM_ST, pal.bar); + du_divider_h(&g_fb, cx, cy + ch - FM_ST, cw, pal.line); + zh_draw_string(&g_fb, s->status, cx + 10, cy + ch - FM_ST + 2, pal.dim, 0); +} diff --git a/CODE/desktop/launcher.c b/CODE/desktop/launcher.c index a100125..269ff15 100644 --- a/CODE/desktop/launcher.c +++ b/CODE/desktop/launcher.c @@ -21,6 +21,23 @@ typedef struct { u64 offset; u64 info; i64 addend; } dt_elf64_rela; static u8 g_elf_image[1048576]; /* 1 MiB:settings.elf 内存映像 ~493KB(中文字体位图 BSS),256KB 不够 */ +/* ELF 数据 → 加载执行 → 收尾(launch_external_elf / _path 共用) */ +static void launch_elf_data(u8 *data, u32 size) { + void *entry = 0; + if (dt_load_elf(data, size, &entry) != 0) { + slog("elf load failed"); + return; + } + slog("launching external elf"); + void (*elf_entry)(const dsk_boot_context *) = (void (*)(const dsk_boot_context *))entry; + elf_entry(g_boot_ctx); + g_cursor_saved = 0; + g_cursor_old_x = -1; g_cursor_old_y = -1; + files_refresh(); /* 外部应用可能改动了磁盘 */ + redraw_all(); + slog("external elf returned"); +} + static int dt_load_elf(u8 *data, u32 data_size, void **entry_out) { const dt_elf64_ehdr *eh = (const dt_elf64_ehdr *)data; if (data_size < sizeof(dt_elf64_ehdr)) return -1; @@ -85,19 +102,18 @@ void launch_external_elf(const char *name11) { slog("elf not found"); return; } - void *entry = 0; - if (dt_load_elf(data, size, &entry) != 0) { - slog("elf load failed"); + launch_elf_data(data, size); +} + +/* M3: 按路径启动外部 ELF(STARTMENU.CNF 的 bin/xxx.elf 条目) */ +void launch_external_elf_path(const char *path) { + u8 *data = 0; u32 size = 0; + if (f32_read_path_lfn_w(path, &data, &size) != 0) { + slog("elf path not found"); + slog(path); return; } - slog("launching external elf"); - void (*elf_entry)(const dsk_boot_context *) = (void (*)(const dsk_boot_context *))entry; - elf_entry(g_boot_ctx); - g_cursor_saved = 0; - g_cursor_old_x = -1; g_cursor_old_y = -1; - files_refresh(); /* 外部应用可能改动了磁盘 */ - redraw_all(); - slog("external elf returned"); + launch_elf_data(data, size); } /* ============================================================ @@ -134,6 +150,11 @@ void launch_app(int app_id) { return; } + /* M3: STARTMENU.CNF 的 bin/xxx.elf → 按路径全屏加载 */ + if (app->elf_path) { + launch_external_elf_path(app->elf_path); + return; + } if (app->elf_name) { launch_external_elf(app->elf_name); return; diff --git a/CODE/desktop/main.c b/CODE/desktop/main.c index cedb116..fac5a72 100644 --- a/CODE/desktop/main.c +++ b/CODE/desktop/main.c @@ -50,8 +50,8 @@ static void load_fuck_config(void) { slog("[cfg] FUCK not found, defaults"); return; } - /* 逐行找 [desktop] 段的 fluent= 键 */ - int in_dsk = 0; + /* 逐行找 [desktop] 段的 fluent= 键;[prank] 段交给 prank_config_line */ + int in_dsk = 0, in_prank = 0; u32 pos = 0; while (pos < size) { u32 eol = pos; @@ -62,6 +62,11 @@ static void load_fuck_config(void) { if (len > 1 && line[0] == '[') { in_dsk = (len >= 9 && line[1] == 'd' && line[2] == 's' && line[3] == 'k' && line[4] == ']'); + in_prank = (len >= 7 && line[1] == 'p' && line[2] == 'r' && + line[3] == 'a' && line[4] == 'n' && line[5] == 'k' && + line[6] == ']'); + } else if (in_prank) { + prank_config_line(line, (int)len); } else if (in_dsk && len >= 7 && line[0]=='f' && line[1]=='l' && line[2]=='u' && line[3]=='e' && line[4]=='n' && line[5]=='t' && line[6]=='=') { @@ -134,9 +139,16 @@ static void crossfade_login_to_desktop(void) { * 每个插桩点唯一 symbol(DESK-E)。 * ============================================================ */ +/* DSK_BOOT_FLAG_PANIC_FALLBACK:desktop_fatal 只留串口坐标后返回,调用点走备用路径 */ +static int g_panic_fallback; + static void desktop_fatal(const char *sym) { /* 严格错误策略:串口留档 + 深红 fatal 屏(ASCII 18px 直写真实 fb)+ 停机 */ slog(sym); + if (g_panic_fallback) { + slog("[FALLBACK] fatal point hit, taking backup path"); + return; + } if (g_real_fb && g_fb_w && g_fb_h) { u32 *fb = (u32 *)g_real_fb; u64 pitch_u32 = g_fb_pitch / 4; @@ -176,8 +188,10 @@ void dsk_entry(const dsk_boot_context *ctx) { if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { desktop_fatal("DSK-E01-BADCONTEXT"); + return; /* fallback 不可达(无上下文 = 标志位读不到,按 strict 停机) */ } g_boot_ctx = ctx; + g_panic_fallback = (ctx->flags & DSK_BOOT_FLAG_PANIC_FALLBACK) ? 1 : 0; g_fb_addr = ctx->framebuffer_address; g_fb_w = ctx->framebuffer_width; @@ -227,11 +241,16 @@ void dsk_entry(const dsk_boot_context *ctx) { } else { slog_num("[zhfont] dbf load failed rc=", zrc); desktop_fatal("DESK-E04 FONT PACK MISSING"); + /* fallback:中文字形缺失时 zhfont 画空框占位,ASCII 正常 */ } } /* M2: FUCK 配置([desktop] fluent=)→ 壁纸初始化(Fluent 路径需要) */ load_fuck_config(); + if (g_prank_enabled) { + slog(g_prank_boot_demo > 0 ? "[cfg] prank enabled (boot demo on)" + : "[cfg] prank enabled"); + } if (g_fluent) { int wrc = wallpaper_init(f32_read_path_lfn_to_w, (int)g_fb_w, (int)g_fb_h); if (wrc == 0) { @@ -240,15 +259,17 @@ void dsk_entry(const dsk_boot_context *ctx) { slog_num("[wallpaper] load failed rc=", wrc); /* 严格错误策略:材质包(壁纸)缺失不降级,fatal 屏 + 错误码 */ desktop_fatal("DESK-E02 TEXTURE PACK MISSING"); + g_fluent = 0; /* fallback:回落经典(非 Fluent)桌面 */ } /* M2: 开始菜单 logo(textures/startMenuLogo.rgba → 0x8700000) */ - if (taskbar_init(f32_read_path_lfn_to_w) == 0) + if (g_fluent && taskbar_init(f32_read_path_lfn_to_w) == 0) slog("[taskbar] startMenu logo loaded"); - else { + else if (g_fluent) { /* 严格错误策略:材质包(开始菜单 logo)缺失不降级 */ desktop_fatal("DESK-E03 STARTMENU LOGO MISSING"); + g_fluent = 0; /* fallback:回落经典(非 Fluent)桌面 */ } - g_page = 3; /* Fluent 默认进 DESKTOP 页 */ + if (g_fluent) g_page = 3; /* Fluent 默认进 DESKTOP 页 */ } tsc_calibrate(); @@ -292,6 +313,8 @@ void dsk_entry(const dsk_boot_context *ctx) { g_booted = 1; slog("desktop ready"); + /* 整蛊系统:开机进桌面后按 FUCK [prank] boot_demo_sec 串烧演示一遍 */ + prank_boot_demo_start(); desktop_launch_pending_native(); /* 主事件循环 */ @@ -303,6 +326,10 @@ void dsk_entry(const dsk_boot_context *ctx) { int need_redraw = 0; int moved = 0; + /* 整蛊系统:推进调度;可视类整蛊播放期间强制全屏重绘 */ + prank_tick(); + if (prank_wants_redraw()) need_redraw = 1; + /* P8.1: 自动启动测试(先 ping guest,不可用直接报告,不浪费 3s 等待) */ if (g_tsc_per_sec && g_autotest_state == 0) { g_autotest_tsc = rdtsc(); @@ -403,7 +430,8 @@ void dsk_entry(const dsk_boot_context *ctx) { u8 st = inb(0x64); if (!(st & 1) || (st & 0x20)) break; u8 sc = inb(0x60); - if (handle_key(sc)) need_redraw = 1; + /* 整蛊"打字乱码":按键可打印码被随机重映射(特殊键/释放码放行) */ + if (handle_key(prank_filter_key(sc))) need_redraw = 1; } /* RTC 秒 tick:DASHBOARD 状态栏时钟/UP 计时刷新 */ diff --git a/CODE/desktop/prank.c b/CODE/desktop/prank.c new file mode 100644 index 0000000..b812e8e --- /dev/null +++ b/CODE/desktop/prank.c @@ -0,0 +1,524 @@ +/* prank.c — Deshab 整蛊系统("致力于打造极差的用户体验") + * + * 设计目标:在用户正常使用桌面时,不定时播放一段"恶作剧"叠加层, + * 制造"这系统是不是坏了"的错觉,随后若无其事地消失。 + * + * 已实现整蛊(全部由 FUCK [prank] 段控制,见 build/configs//FUCK): + * PK_FUCK — 屏幕压暗,特大 "FUCK" 跟着鼠标走,最后淡出(用户点名要的) + * PK_BSOD — 假蓝屏(Win 风格 + 哭泣脸 + 伪造 Stop code) + * PK_INVERT — 全屏反色闪烁 + * PK_HANG — 假"程序未响应"对话框(带等待点点动画) + * PK_GARBLE — 打字乱码:按键被随机映射到别的字母/数字 + * PK_FLY — 任务栏图标乱飞(任务栏溶解,应用图标四散飞出) + * PK_SHUTDOWN — 假"系统即将关机"倒计时(数到 0 什么也不发生) + * + * 触发方式: + * 1) 开机进桌面后 boot_demo_sec 秒的"串烧演示"(用于验证效果) + * 2) 使用中每隔 interval_sec 秒随机播放一个已启用的整蛊 + * + * 渲染:只往 sprite buffer(g_fb.fb) 上叠画,绝不直接写真实 fb; + * 由 compositor 在光标之前调用 prank_draw()。运行时用 TSC 计时, + * 遵守 -mno-sse/-msoft-float,全程整数运算。 + */ +#include "desktop.h" +#include "zhfont.h" /* PK_SHUTDOWN 的中文文案 */ + +/* ============================================================ + * 配置(FUCK [prank];extern 声明见 desktop.h) + * 默认全部打开:无 FUCK 或缺键时也整蛊。FUCK 里写 0 才单独关掉。 + * ============================================================ */ +int g_prank_enabled = 1; /* 总开关,enable=0 关闭 */ +int g_prank_boot_demo = 5; /* 进桌面后演示秒数,0=关 */ +int g_prank_interval = 60; /* 使用中自动整蛊间隔(秒),0=关 */ +int g_prank_fuck = 1; /* FUCK 跟随鼠标遮屏 */ +int g_prank_bsod = 1; /* 假蓝屏 */ +int g_prank_invert = 1; /* 反色闪屏 */ +int g_prank_hang = 1; /* 假"未响应"对话框 */ +int g_prank_garble = 1; /* 打字乱码 */ +int g_prank_fly = 1; /* 任务栏图标乱飞 */ +int g_prank_shutdown = 1; /* 假"系统即将关机"倒计时 */ + +/* ============================================================ + * 运行时状态 + * ============================================================ */ +enum { PK_NONE = 0, PK_FUCK, PK_BSOD, PK_INVERT, PK_HANG, + PK_GARBLE, PK_FLY, PK_SHUTDOWN }; + +#define PK_KIND_MAX 7 + +#define PK_MS_FUCK 2600u +#define PK_MS_BSOD 3000u +#define PK_MS_INVERT 1500u +#define PK_MS_HANG 2600u +#define PK_MS_GARBLE 5000u +#define PK_MS_FLY 2600u +#define PK_MS_SHUTDOWN 6000u + +static int s_kind = PK_NONE; +static u64 s_start = 0; +static u64 s_dur = 0; + +static u64 s_next = 0; /* 下次自动整蛊的 TSC 时刻 */ +static int s_armed = 0; + +static int s_demo = 0; /* 1 = 开机演示串烧进行中 */ +static int s_q[PK_KIND_MAX]; +static int s_qn = 0; +static int s_qi = 0; +static int s_demo_ms = 0; + +static u32 s_rng = 0x12345678u; +static u32 s_seed = 0; /* 本次整蛊的随机种子(图标飞散用) */ + +/* ============================================================ + * 小工具 + * ============================================================ */ + +static u64 ms_to_tsc(u32 ms) { + if (g_tsc_per_sec) return g_tsc_per_sec * (u64)ms / 1000u; + return (u64)ms * 1000000u; /* 未校准兜底(约数) */ +} + +static unsigned int rng_next(void) { + s_rng = s_rng * 1664525u + 1013904223u; + return s_rng; +} + +static int key_is(const char *k, int kl, const char *name) { + int i = 0; + while (name[i]) i++; + if (i != kl) return 0; + for (i = 0; i < kl; i++) if (k[i] != name[i]) return 0; + return 1; +} + +static int parse_int(const char *s, int len) { + int v = 0, neg = 0, i = 0; + if (len > 0 && s[0] == '-') { neg = 1; i = 1; } + for (; i < len && s[i] >= '0' && s[i] <= '9'; i++) v = v * 10 + (s[i] - '0'); + return neg ? -v : v; +} + +static u32 kind_ms(int k) { + switch (k) { + case PK_FUCK: return PK_MS_FUCK; + case PK_BSOD: return PK_MS_BSOD; + case PK_INVERT: return PK_MS_INVERT; + case PK_HANG: return PK_MS_HANG; + case PK_GARBLE: return PK_MS_GARBLE; + case PK_FLY: return PK_MS_FLY; + case PK_SHUTDOWN: return PK_MS_SHUTDOWN; + default: return 1000u; + } +} + +static void start_prank(int kind, u32 ms) { + s_kind = kind; + s_start = rdtsc(); + s_dur = ms_to_tsc(ms); + if (s_dur == 0) s_dur = 1; + s_seed = (u32)rdtsc() ^ (rng_next() << 8); +} + +/* ============================================================ + * FUCK [prank] 单行解析("key=value",注释/空行由 main 过滤) + * ============================================================ */ +void prank_config_line(const char *line, int len) { + int eq = -1; + for (int i = 0; i < len; i++) if (line[i] == '=') { eq = i; break; } + if (eq <= 0) return; + + const char *k = line; + int kl = eq; + const char *v = line + eq + 1; + int vl = len - eq - 1; + while (vl > 0 && (v[vl - 1] == ' ' || v[vl - 1] == '\t' || v[vl - 1] == '\r')) vl--; + int val = parse_int(v, vl); + + if (key_is(k, kl, "enable")) g_prank_enabled = val ? 1 : 0; + else if (key_is(k, kl, "boot_demo_sec")) g_prank_boot_demo = val; + else if (key_is(k, kl, "interval_sec")) g_prank_interval = val; + else if (key_is(k, kl, "fuck_cursor")) g_prank_fuck = val ? 1 : 0; + else if (key_is(k, kl, "fake_bsod")) g_prank_bsod = val ? 1 : 0; + else if (key_is(k, kl, "invert_flash")) g_prank_invert = val ? 1 : 0; + else if (key_is(k, kl, "fake_hang")) g_prank_hang = val ? 1 : 0; + else if (key_is(k, kl, "garble_typing")) g_prank_garble = val ? 1 : 0; + else if (key_is(k, kl, "taskbar_fly")) g_prank_fly = val ? 1 : 0; + else if (key_is(k, kl, "fake_shutdown")) g_prank_shutdown = val ? 1 : 0; +} + +/* ============================================================ + * 调度 + * ============================================================ */ + +/* 收集当前启用的整蛊种类(写入 out,返回个数,≤ PK_KIND_MAX)。 */ +static int collect_kinds(int *out) { + int n = 0; + if (g_prank_fuck) out[n++] = PK_FUCK; + if (g_prank_bsod) out[n++] = PK_BSOD; + if (g_prank_invert) out[n++] = PK_INVERT; + if (g_prank_hang) out[n++] = PK_HANG; + if (g_prank_garble) out[n++] = PK_GARBLE; + if (g_prank_fly) out[n++] = PK_FLY; + if (g_prank_shutdown) out[n++] = PK_SHUTDOWN; + return n; +} + +/* 开机进桌面后的串烧演示:把已启用的整蛊平分 boot_demo_sec 秒依次播放。 */ +void prank_boot_demo_start(void) { + if (!g_prank_enabled || g_prank_boot_demo <= 0) return; + + s_qn = collect_kinds(s_q); + if (s_qn == 0) return; + + u32 each = (u32)g_prank_boot_demo * 1000u / (u32)s_qn; + if (each < 300u) each = 300u; + + s_rng ^= (u32)rdtsc(); + s_demo = 1; + s_qi = 0; + s_demo_ms = (int)each; + s_armed = 0; + start_prank(s_q[0], each); + slog("[prank] boot demo start"); +} + +void prank_tick(void) { + if (!g_prank_enabled) { + s_kind = PK_NONE; + s_demo = 0; + s_armed = 0; + return; + } + if (!g_tsc_per_sec) return; /* TSC 未校准前不动 */ + + u64 now = rdtsc(); + + if (s_kind != PK_NONE) { + if (now - s_start >= s_dur) { + s_kind = PK_NONE; + if (s_demo) { + s_qi++; + if (s_qi < s_qn) start_prank(s_q[s_qi], (u32)s_demo_ms); + else { s_demo = 0; s_armed = 0; slog("[prank] boot demo done"); } + } else { + s_armed = 0; + } + } + return; + } + + if (s_demo) return; + if (g_prank_interval <= 0) return; + + if (!s_armed) { + s_next = now + ms_to_tsc((u32)g_prank_interval * 1000u); + s_armed = 1; + return; + } + if (now < s_next) return; + + int cand[PK_KIND_MAX]; + int n = collect_kinds(cand); + if (n == 0) { s_armed = 0; return; } + + int k = cand[(rng_next() >> 16) % (u32)n]; + start_prank(k, kind_ms(k)); + s_armed = 0; +} + +int prank_active(void) { return s_kind != PK_NONE; } + +/* 是否需要在整蛊期间强制全屏重绘(打字乱码纯作用于输入,无需重绘)。 */ +int prank_wants_redraw(void) { + return s_kind != PK_NONE && s_kind != PK_GARBLE; +} + +/* 打字乱码:把可打印键的按下码随机映射到另一个可打印码。 + * 释放码与特殊键(Esc/回车/退格/Tab/方向键/修饰键/E0 前缀)原样放行。 */ +u8 prank_filter_key(u8 sc) { + if (s_kind != PK_GARBLE) return sc; + if (sc & 0x80) return sc; /* 释放码不动 */ + + static const u8 keys[] = { + 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, /* 1..0 */ + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, /* qwertyuiop */ + 0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26, /* asdfghjkl */ + 0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, /* zxcvbnm */ + 0x39 /* space */ + }; + int n = (int)(sizeof(keys) / sizeof(keys[0])); + int idx = -1; + for (int i = 0; i < n; i++) if (keys[i] == sc) { idx = i; break; } + if (idx < 0) return sc; /* 非可打印键原样 */ + + return keys[rng_next() % (unsigned int)n]; +} + +/* ============================================================ + * 绘制原语(全部只作用于 sprite buffer) + * ============================================================ */ + +/* 全屏压暗:每通道 * (256-a)/256 */ +static void blend_dark(du_context *ctx, u32 a) { + if (a == 0) return; + if (a > 255) a = 255; + u32 ia = 256u - a; + for (u64 y = 0; y < ctx->height; y++) { + u32 *line = (u32 *)((u8 *)ctx->fb + y * ctx->pitch); + for (u64 x = 0; x < ctx->width; x++) { + u32 c = line[x]; + u32 r = ((c & 0xFF) * ia) >> 8; + u32 g = (((c >> 8) & 0xFF) * ia) >> 8; + u32 b = (((c >> 16) & 0xFF) * ia) >> 8; + line[x] = 0xFF000000u | (b << 16) | (g << 8) | r; + } + } +} + +/* 全屏反色(保留 alpha) */ +static void invert_all(du_context *ctx) { + for (u64 y = 0; y < ctx->height; y++) { + u32 *line = (u32 *)((u8 *)ctx->fb + y * ctx->pitch); + for (u64 x = 0; x < ctx->width; x++) line[x] ^= 0x00FFFFFFu; + } +} + +/* 放大版 ASCII 字符串:以 (cx,cy) 为中心,每字形像素放大 scale 倍。 + * alpha 0..255 控制整体不透明度。 */ +static void draw_scale_text(du_context *ctx, const char *s, int cx, int cy, + int scale, u32 fg, int alpha) { + if (scale < 1) scale = 1; + if (alpha < 0) alpha = 0; + if (alpha > 255) alpha = 255; + if (alpha == 0) return; + + int n = kstrlen(s); + if (n <= 0) return; + int gw = g_ascii_w, gh = g_ascii_h; + int step = (gw + 1) * scale; + int total_w = n * step - scale; + int x0 = cx - total_w / 2; + int y0 = cy - gh * scale / 2; + + for (int i = 0; i < n; i++) { + unsigned char ch = (unsigned char)s[i]; + if (ch < 32 || ch > 126) continue; + const unsigned char *g = g_ascii[ch - 32]; + int gx = x0 + i * step; + for (int r = 0; r < gh; r++) { + for (int c = 0; c < gw; c++) { + unsigned char ga = g[r * gw + c]; + if (!ga) continue; + int aa = (int)ga * alpha / 255; + if (aa <= 0) continue; + for (int sy = 0; sy < scale; sy++) { + int py = y0 + r * scale + sy; + if (py < 0 || (u64)py >= ctx->height) continue; + u32 *line = (u32 *)((u8 *)ctx->fb + (u64)py * ctx->pitch); + for (int sx = 0; sx < scale; sx++) { + int px = gx + c * scale + sx; + if (px < 0 || (u64)px >= ctx->width) continue; + line[px] = du_blend(line[px], fg, (u32)aa); + } + } + } + } + } +} + +/* ============================================================ + * 各整蛊的绘制 + * ============================================================ */ + +/* PK_FUCK — 屏幕压暗 + 特大 "FUCK" 跟着鼠标 + 淡出 */ +static void draw_fuck(du_context *ctx, int pct) { + int a; + if (pct < 15) a = pct * 170 / 15; /* 淡入压暗 */ + else if (pct > 85) a = (100 - pct) * 170 / 15; /* 淡出解除 */ + else a = 170; + if (a < 0) a = 0; + blend_dark(ctx, (u32)a); + + int alpha = 255; + if (pct < 10) alpha = pct * 255 / 10; + else if (pct > 85) alpha = (100 - pct) * 255 / 15; + if (alpha < 0) alpha = 0; + + /* 阴影 + 主体:中心跟随鼠标 */ + draw_scale_text(ctx, "FUCK", g_mouse_x + 7, g_mouse_y + 7, 8, 0xFF000000u, alpha / 2); + draw_scale_text(ctx, "FUCK", g_mouse_x, g_mouse_y, 8, 0xFFFF1744u, alpha); +} + +/* PK_BSOD — 假蓝屏 */ +static void draw_bsod(du_context *ctx, int pct) { + int W = (int)ctx->width, H = (int)ctx->height; + du_fill_bg_solid(ctx, 0xFF0078D7u); + + int x = W / 8; if (x < 80) x = 80; + int y = H / 2 - 150; + if (y < 40) y = 40; + + draw_scale_text(ctx, ":(", x + 90, y + 40, 5, 0xFFFFFFFFu, 255); + + const char *l1 = "Your Deshab ran into a problem and needs to restart."; + const char *l2 = "We're just collecting some error info, and then"; + const char *l3 = "we'll restart for you."; + int ty = y + 110; + du_draw_string(ctx, l1, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 34; + du_draw_string(ctx, l2, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 22; + du_draw_string(ctx, l3, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 50; + + /* "(NN% complete)" */ + int n = pct; if (n < 0) n = 0; if (n > 100) n = 100; + char pb[40]; int i = 0; + pb[i++] = '('; + if (n >= 100) { pb[i++] = '1'; pb[i++] = '0'; pb[i++] = '0'; } + else { pb[i++] = (char)('0' + n / 10); pb[i++] = (char)('0' + n % 10); } + const char *suf = "% complete)"; + while (*suf) pb[i++] = *suf++; + pb[i] = 0; + du_draw_string(ctx, pb, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 44; + + du_draw_string(ctx, "Stop code: FUCKED_BY_DESIGN", x, ty, + 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); +} + +/* PK_INVERT — 全屏反色 */ +static void draw_invert(du_context *ctx) { + invert_all(ctx); +} + +/* PK_HANG — 假"程序未响应"对话框 */ +static void draw_hang(du_context *ctx, int pct) { + int W = (int)ctx->width, H = (int)ctx->height; + blend_dark(ctx, 110); + + int w = 520, h = 200; + int x = (W - w) / 2, y = (H - h) / 2; + + /* 阴影 + 卡片 */ + du_fill_rect(ctx, x + 8, y + 8, w, h, 0xFF6B7280u); + du_fill_rect(ctx, x, y, w, h, 0xFFF3F4F6u); + du_rect_outline(ctx, x, y, w, h, 0xFF9CA3AFu, 0); + + /* 标题栏 */ + du_fill_rect(ctx, x, y, w, 34, 0xFFE5E7EBu); + du_draw_string(ctx, "Deshab - Not Responding", x + 12, y + 8, + 0xFF111827u, 0xFFE5E7EBu, DU_ASCII_STEP); + + /* 正文 */ + du_draw_string(ctx, "This program is not responding.", x + 20, y + 56, + 0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP); + du_draw_string(ctx, "It may respond if you wait a bit.", x + 20, y + 80, + 0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP); + du_draw_string(ctx, "Do you want to close it?", x + 20, y + 104, + 0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP); + + /* 等待点点动画(0..3 个点循环) */ + int dots = (pct / 12) % 4; + char db[8]; db[0] = '.'; db[1] = '.'; db[2] = '.'; db[3] = '.'; db[4] = 0; + db[dots] = 0; + du_draw_string(ctx, "Waiting", x + 20, y + 138, 0xFF6B7280u, 0xFFF3F4F6u, DU_ASCII_STEP); + du_draw_string(ctx, db, x + 20 + 7 * (int)DU_ASCII_STEP, y + 138, + 0xFF6B7280u, 0xFFF3F4F6u, DU_ASCII_STEP); + + /* 按钮 */ + int bw = 120, bh = 34, by = y + h - 54; + int bx2 = x + w - 20 - bw, bx1 = bx2 - bw - 12; + + du_fill_rect(ctx, bx1, by, bw, bh, 0xFFE5E7EBu); + du_rect_outline(ctx, bx1, by, bw, bh, 0xFF9CA3AFu, 0); + du_draw_string(ctx, "Wait", bx1 + (bw - 4 * (int)DU_ASCII_STEP) / 2, by + 8, + 0xFF111827u, 0xFFE5E7EBu, DU_ASCII_STEP); + + du_fill_rect(ctx, bx2, by, bw, bh, 0xFF2563EBu); + du_draw_string(ctx, "Close", bx2 + (bw - 5 * (int)DU_ASCII_STEP) / 2, by + 8, + 0xFFFFFFFFu, 0xFF2563EBu, DU_ASCII_STEP); +} + +/* PK_FLY — 任务栏图标乱飞:底部任务栏"溶解",应用图标四散飞出屏幕 */ +static void draw_fly(du_context *ctx, int pct) { + int W = (int)ctx->width, H = (int)ctx->height; + blend_dark(ctx, 90); + + /* 任务栏条带溶解为深色(视觉上任务栏没了) */ + int strip_h = 72; + int sy = H - strip_h; if (sy < 0) sy = 0; + du_fill_rect(ctx, 0, sy, W, strip_h, 0xFF14161Au); + + /* 应用图标从任务栏位置四散飞出 */ + int n = g_app_count; if (n < 6) n = 6; if (n > 8) n = 8; + int base_y = H - 40; + for (int i = 0; i < n; i++) { + u32 r = s_seed ^ ((u32)i * 2654435761u); + int vx = (int)(r & 0xFFu) - 128; /* -128..127 */ + vx = vx * 3; /* -384..381 px */ + int vy = -(int)(((r >> 8) & 0xFFu) * 3u + 120u); /* 向上飞 */ + int start_x = W / 2 + (i - n / 2) * 56; + int x = start_x + vx * pct / 100; + int y = base_y + vy * pct / 100; + int appid = g_app_count > 0 ? (i % g_app_count) : 0; + du_fill_rect(ctx, x + 3, y + 3, 40, 40, 0xFF000000u); /* 阴影 */ + draw_app_icon(ctx, appid, x, y); + } +} + +/* PK_SHUTDOWN — 假"系统即将关机"倒计时 */ +static void draw_shutdown(du_context *ctx, int pct) { + int W = (int)ctx->width, H = (int)ctx->height; + du_fill_bg_solid(ctx, 0xFF0A0E14u); + + int remain = 6 - (pct * 6 / 100); + if (remain < 0) remain = 0; + + /* 中文标题(16px 位图字库;24px 未懒加载前不使用) */ + const char *zh_title = "系统即将关机"; + int zw = zh_string_width(zh_title, 0); + zh_draw_string(ctx, zh_title, (W - zw) / 2, H / 2 - 130, 0xFFFFD24Au, 0); + + /* 大号倒计时数字 */ + char num[4]; + num[0] = (char)('0' + remain); + num[1] = 0; + draw_scale_text(ctx, num, W / 2, H / 2 + 10, 10, 0xFFFFFFFFu, 255); + + /* 中英提示 */ + const char *zh_sub = "请立即保存您的工作"; + int zw2 = zh_string_width(zh_sub, 0); + zh_draw_string(ctx, zh_sub, (W - zw2) / 2, H / 2 + 120, 0xFFB0B6C0u, 0); + + const char *en = "System will shut down automatically."; + int ew = kstrlen(en) * (int)DU_ASCII_STEP; + du_draw_string(ctx, en, (W - ew) / 2, H / 2 + 152, + 0xFF9AA0A8u, 0xFF0A0E14u, DU_ASCII_STEP); + + /* 底部进度条(时间越少越红) */ + int bw = W / 3, bx = (W - bw) / 2, byy = H - 80; + du_fill_rect(ctx, bx, byy, bw, 8, 0xFF2A2F38u); + int fw = bw * (100 - pct) / 100; + if (fw > 0) du_fill_rect(ctx, bx, byy, fw, 8, 0xFFE5484Du); +} + +/* ============================================================ + * 对外绘制入口(compositor 在光标之前调用) + * ============================================================ */ +void prank_draw(du_context *ctx) { + if (s_kind == PK_NONE) return; + + u64 el = rdtsc() - s_start; + int pct = (int)(el * 100u / (s_dur ? s_dur : 1u)); + if (pct < 0) pct = 0; + if (pct > 100) pct = 100; + + switch (s_kind) { + case PK_FUCK: draw_fuck(ctx, pct); break; + case PK_BSOD: draw_bsod(ctx, pct); break; + case PK_INVERT: draw_invert(ctx); break; + case PK_HANG: draw_hang(ctx, pct); break; + case PK_GARBLE: break; /* 纯输入效果,无叠加层 */ + case PK_FLY: draw_fly(ctx, pct); break; + case PK_SHUTDOWN: draw_shutdown(ctx, pct); break; + default: break; + } +} diff --git a/CODE/desktop/shell.c b/CODE/desktop/shell.c index f0c9e72..c955285 100644 --- a/CODE/desktop/shell.c +++ b/CODE/desktop/shell.c @@ -478,6 +478,12 @@ int handle_key(u8 sc) { if (sc == 0xB8) { g_alt = 0; return 0; } if (sc & 0x80) { g_e0 = 0; return 0; } /* 其他释放码忽略 */ + /* M3 开始菜单(模态:Esc 关闭 / 搜索输入 / Enter 启动) */ + if (g_sm_open) return startmenu_key(sc, g_shift); + + /* 快速设置卡片(Esc 收起) */ + if (g_qs_open && sc == 0x01) { g_qs_open = 0; return 1; } + /* WiFi 快捷卡片(模态:Esc 收起 / 密码模式接管输入) */ if (g_wifi_open) return wifi_key(sc, g_shift); diff --git a/CODE/desktop/startmenu.c b/CODE/desktop/startmenu.c new file mode 100644 index 0000000..12a2052 --- /dev/null +++ b/CODE/desktop/startmenu.c @@ -0,0 +1,394 @@ +/* startmenu.c — M3 Win11 开始菜单 + * + * 任务栏开始按钮弹出:搜索框 + "已固定" 应用瓦片网格 + 底部用户/电源行。 + * 条目来自 appreg.c 的 g_sm_ids/g_sm_labels(STARTMENU.CNF,缺失回退全量)。 + * + * 布局(浅色主题,深色壁纸衬托亚克力): + * 面板 520 × min(600, H-128),锚在开始按钮上方 + * [搜索框 36px] [已固定 标签] [瓦片网格 6 列 × N 行] + * [分隔线] [用户 | 电源] + * + * 交互: + * 搜索:ASCII 输入实时过滤(display/name 不区分大小写子串), + * 过滤态切 du_wf_list_item 列表,Up/Down + Enter 键盘启动 + * 瓦片:点击 → launch_app + 关闭 + * 电源:g_quit(与托盘电源一致);Esc/点击面板外关闭 + * + * 绘制挂在 taskbar_draw 末尾(叠加在任务栏之上); + * 点击/键盘经 taskbar_click / handle_key 路由(模态,参考 WiFi 卡片)。 + */ +#include "desktop.h" +#include "ascii_font.h" /* 顺序敏感:先于 fluent_ui.h */ +#include "../UTSM/include/utsm/fluent_ui.h" +#include "zhfont.h" + +/* ---- 全局状态 ---- */ +int g_sm_open = 0; + +#define SM_PANEL_W 520 +#define SM_PANEL_MIN_H 380 +#define SM_SEARCH_MAX 28 /* 搜索串字节上限(ASCII) */ +#define SM_COLS 6 +#define SM_TILE_H 84 +#define SM_TILE_GAP 4 +#define SM_GRID_PAD 16 +#define SM_BOTTOM_H 56 + +static char g_sm_search[SM_SEARCH_MAX + 1]; +static int g_sm_search_len; +static int g_sm_sel; /* 过滤列表选中行 */ + +static int g_sm_panel_rect[4]; +static int g_sm_search_rect[4]; +static int g_sm_tile_rect[SM_MAX_APPS][4]; +static int g_sm_pwr_rect[4]; +static int g_sm_user_rect[4]; +/* 过滤结果(g_sm 下标),-1 结尾 */ +static int g_sm_filter[SM_MAX_APPS + 1]; +static int g_sm_filter_n; +/* 网格实际显示的条目数(容量截断时串口留档) */ +static int g_sm_grid_shown; +static int g_sm_grid_dropped; +/* 当前模式实际绘制(可点击)的行数:grid=瓦片数 / list=过滤行数 */ +static int g_sm_rows_visible; + +static const wf_theme *sm_theme(void) { return &g_wf_light; } + +static wf_btn_state sm_hover_state(const int r[4]) { + if (g_mouse_x >= r[0] && g_mouse_x < r[0] + r[2] && + g_mouse_y >= r[1] && g_mouse_y < r[1] + r[3]) + return WF_BTN_HOVER; + return WF_BTN_NORMAL; +} + +/* ============================================================ + * 开 / 关 / 过滤 + * ============================================================ */ + +static void sm_filter_rebuild(void) { + g_sm_filter_n = 0; + for (int i = 0; i < g_sm_count && g_sm_filter_n < SM_MAX_APPS; i++) { + if (g_sm_search_len == 0) { + g_sm_filter[g_sm_filter_n++] = i; + continue; + } + /* 不区分大小写 ASCII 子串匹配:display 标签 + 内部名 */ + const char *hay[2] = { g_sm_labels[i], + g_apps[g_sm_ids[i]].name }; + int hit = 0; + for (int h = 0; h < 2 && !hit; h++) { + const char *s = hay[h]; + if (!s) continue; + for (int b = 0; s[b] && !hit; b++) { + int m = 1; + for (int k = 0; k < g_sm_search_len; k++) { + char c1 = s[b + k], c2 = g_sm_search[k]; + if (!c1) { m = 0; break; } + if (c1 >= 'a' && c1 <= 'z') c1 = (char)(c1 - 32); + if (c2 >= 'a' && c2 <= 'z') c2 = (char)(c2 - 32); + if (c1 != c2) { m = 0; break; } + } + if (m) hit = 1; + } + } + if (hit) g_sm_filter[g_sm_filter_n++] = i; + } + g_sm_filter[g_sm_filter_n] = -1; + if (g_sm_sel >= g_sm_filter_n) g_sm_sel = 0; +} + +void startmenu_open(void) { + g_sm_open = 1; + g_sm_search[0] = 0; + g_sm_search_len = 0; + g_sm_sel = 0; + sm_filter_rebuild(); + g_wifi_open = 0; /* 互斥:收起托盘弹卡 */ + g_qs_open = 0; + slog("[startmenu] open"); +} + +void startmenu_close(void) { + if (!g_sm_open) return; + g_sm_open = 0; + slog("[startmenu] close"); +} + +void startmenu_toggle(void) { + if (g_sm_open) startmenu_close(); + else startmenu_open(); +} + +/* 瓦片标签截断:zh 16px ASCII 半宽 12 / CJK 全宽 16 → 瓦片 ~80px 最多 4 个 CJK */ +static void sm_label_clip(char *dst, int cap, const char *src) { + int w = 0, o = 0; + for (int i = 0; src[i] && o < cap - 4; ) { + unsigned char c = (unsigned char)src[i]; + int cw = (c < 0x80) ? 12 : 16; /* ASCII 半宽 12px / CJK 全宽 16px */ + if (w + cw > 68) break; + dst[o++] = src[i++]; + if (c >= 0x80) { /* UTF-8 续字节整组拷贝 */ + int cont = (c >= 0xF0) ? 3 : (c >= 0xE0) ? 2 : 1; + while (cont-- > 0 && src[i] && o < cap - 4) + dst[o++] = src[i++]; + } + w += cw; + } + dst[o] = 0; +} + +/* ============================================================ + * 绘制(taskbar_draw 末尾调用) + * ============================================================ */ + +void startmenu_draw(void) { + if (!g_sm_open) return; + const wf_theme *t = sm_theme(); + int W = (int)g_fb_w, H = (int)g_fb_h; + + /* ---- 面板几何:开始按钮上方 ---- */ + int ph = H - 64 - 8 - 24; /* 任务栏(48+8gap) 上方 8 间隙 + 顶部余量 */ + if (ph > 600) ph = 600; + if (ph < SM_PANEL_MIN_H) ph = SM_PANEL_MIN_H; + int px = g_tb2_start_rect[0] - 8; + if (px < 8) px = 8; + if (px + SM_PANEL_W > W - 8) px = W - 8 - SM_PANEL_W; + int py = H - 64 - ph; /* 任务栏区(56) 之上贴住 */ + if (py < 8) py = 8; + + g_sm_panel_rect[0] = px; g_sm_panel_rect[1] = py; + g_sm_panel_rect[2] = SM_PANEL_W; g_sm_panel_rect[3] = ph; + + /* ---- 卡片底 ---- */ + du_wf_shadow(&g_fb, px, py, SM_PANEL_W, ph, (du_u32)t->radius_lg, t); + du_wf_acrylic(&g_fb, px, py, SM_PANEL_W, ph, 235, (du_u32)t->radius_lg, t); + + /* ---- 搜索框 ---- */ + g_sm_search_rect[0] = px + 16; g_sm_search_rect[1] = py + 16; + g_sm_search_rect[2] = SM_PANEL_W - 32; g_sm_search_rect[3] = 36; + { + /* blink:RTC 秒奇偶交替 */ + int yy, mo, dd, hh, mi, ss; + rtc_read(&yy, &mo, &dd, &hh, &mi, &ss); + du_wf_textbox(&g_fb, px + 16, py + 16, SM_PANEL_W - 32, + g_sm_search[0] ? g_sm_search : "search", + ss & 1, t, 1); + if (!g_sm_search[0]) + du_wf_icon(&g_fb, WF_ICON_SEARCH, + px + SM_PANEL_W - 40, py + 22, 22, t->text_dim); + } + + /* ---- 过滤列表模式(有搜索串)---- */ + if (g_sm_search_len > 0) { + zh_draw_string(&g_fb, "搜索结果", px + 16, py + 66, + t->text_dim, 0); + int y = py + 92; + int max_rows = (py + ph - SM_BOTTOM_H - y) / 36; + if (max_rows > SM_MAX_APPS) max_rows = SM_MAX_APPS; + int rows = g_sm_filter_n; + if (rows > max_rows) rows = max_rows; + g_sm_rows_visible = rows; + for (int i = 0; i < rows; i++) { + int e = g_sm_filter[i]; + int id = g_sm_ids[e]; + char label[52]; + sm_label_clip(label, 52, g_sm_labels[e]); + /* zh 列表项:底行 + 名字(ASCII name 右侧小字) */ + int rowy = y + i * 36; + if (g_sm_sel == i) + du_wf_overlay(&g_fb, px + 8, rowy, SM_PANEL_W - 16, 36, 0x28000000u); + else if (sm_hover_state((int[4]){px + 8, rowy, SM_PANEL_W - 16, 36}) + == WF_BTN_HOVER) + du_wf_overlay(&g_fb, px + 8, rowy, SM_PANEL_W - 16, 36, 0x14000000u); + if (g_sm_sel == i) + du_fill_rect(&g_fb, px + 8, rowy + 8, 3, 20, t->accent); + du_wf_icon(&g_fb, (wf_icon_id)desktop_app_icon(id), + px + 20, rowy + 7, 22, t->text_primary); + zh_draw_string(&g_fb, label, px + 54, rowy + 10, + t->text_primary, 0); + du_draw_string(&g_fb, g_apps[id].name, + px + SM_PANEL_W - 16 - kstrlen(g_apps[id].name) * 8, + rowy + 12, t->text_dim, 0, DU_ASCII_STEP); + /* 行命中矩形(点击用) */ + g_sm_tile_rect[i][0] = px + 8; g_sm_tile_rect[i][1] = rowy; + g_sm_tile_rect[i][2] = SM_PANEL_W - 16; g_sm_tile_rect[i][3] = 36; + } + if (g_sm_filter_n == 0) + zh_draw_string(&g_fb, "无匹配应用", px + 24, y + 8, t->text_dim, 0); + goto bottom_bar; + } + + /* ---- 瓦片网格模式 ---- */ + zh_draw_string(&g_fb, "已固定", px + 16, py + 66, t->text_dim, 0); + + { + int grid_y = py + 92; + int grid_h = py + ph - SM_BOTTOM_H - grid_y; + int rows_cap = grid_h / (SM_TILE_H + SM_TILE_GAP); + int cap = rows_cap * SM_COLS; + if (cap > SM_MAX_APPS) cap = SM_MAX_APPS; + + g_sm_grid_shown = g_sm_count; + if (g_sm_grid_shown > cap) { + g_sm_grid_dropped = g_sm_count - cap; + g_sm_grid_shown = cap; + } else { + g_sm_grid_dropped = 0; + } + g_sm_rows_visible = g_sm_grid_shown; + + int tile_w = (SM_PANEL_W - SM_GRID_PAD * 2 - (SM_COLS - 1) * SM_TILE_GAP) + / SM_COLS; + for (int i = 0; i < g_sm_grid_shown; i++) { + int col = i % SM_COLS, row = i / SM_COLS; + int tx = px + SM_GRID_PAD + col * (tile_w + SM_TILE_GAP); + int ty = grid_y + row * (SM_TILE_H + SM_TILE_GAP); + g_sm_tile_rect[i][0] = tx; g_sm_tile_rect[i][1] = ty; + g_sm_tile_rect[i][2] = tile_w; g_sm_tile_rect[i][3] = SM_TILE_H; + + int id = g_sm_ids[i]; + du_wf_app_tile(&g_fb, tx, ty, tile_w, SM_TILE_H, + (wf_icon_id)desktop_app_icon(id), t, + sm_hover_state(g_sm_tile_rect[i]), 0); + char label[52]; + sm_label_clip(label, 52, g_sm_labels[i]); + zh_draw_string(&g_fb, label, + tx + (tile_w - zh_string_width(label, 0)) / 2, + ty + SM_TILE_H - 30, t->text_primary, 0); + } + if (g_sm_grid_dropped > 0) { + char buf[24]; + buf[0] = '+'; + int n = u32dec((u32)g_sm_grid_dropped, buf + 1); + buf[n + 1] = 0; + zh_draw_string(&g_fb, buf, px + SM_PANEL_W - 60, + py + 66, t->text_dim, 0); + } + } + +bottom_bar: + /* ---- 底部:分隔线 + 用户 + 电源 ---- */ + { + int by = py + ph - SM_BOTTOM_H; + du_wf_separator(&g_fb, px + 16, by, SM_PANEL_W - 32, t); + + /* 用户行 */ + g_sm_user_rect[0] = px + 12; g_sm_user_rect[1] = by + 8; + g_sm_user_rect[2] = 220; g_sm_user_rect[3] = 40; + du_wf_icon_button(&g_fb, px + 12, by + 8, 40, t, + sm_hover_state(g_sm_user_rect)); + du_wf_icon(&g_fb, WF_ICON_USER, px + 20, by + 16, 24, t->text_primary); + zh_draw_string(&g_fb, "Deshab 用户", px + 60, by + 16, + t->text_primary, 0); + + /* 电源钮 */ + g_sm_pwr_rect[0] = px + SM_PANEL_W - 52; g_sm_pwr_rect[1] = by + 8; + g_sm_pwr_rect[2] = 40; g_sm_pwr_rect[3] = 40; + du_wf_icon_button(&g_fb, px + SM_PANEL_W - 52, by + 8, 40, t, + sm_hover_state(g_sm_pwr_rect)); + du_wf_icon(&g_fb, WF_ICON_POWER, px + SM_PANEL_W - 44, by + 16, 24, + t->text_primary); + } +} + +/* ============================================================ + * 点击(taskbar_click 调用;返回 1 = 已消费) + * ============================================================ */ + +int startmenu_click(int mx, int my) { + if (!g_sm_open) return 0; + if (!rect_hit(g_sm_panel_rect, mx, my)) { + startmenu_close(); /* 点面板外:关闭并放行点击 */ + return 0; + } + + /* 电源 → 退出桌面(与托盘电源一致) */ + if (rect_hit(g_sm_pwr_rect, mx, my)) { + startmenu_close(); + g_quit = 1; + return 1; + } + /* 用户行(预留:M7 用户菜单) */ + if (rect_hit(g_sm_user_rect, mx, my)) return 1; + + /* 瓦片 / 过滤行 → 启动(只认实际绘制的行,防越界脏矩形) */ + int n = g_sm_rows_visible; + for (int i = 0; i < n; i++) { + if (!rect_hit(g_sm_tile_rect[i], mx, my)) continue; + int e = g_sm_filter[i]; + slog("[startmenu] launch:"); + slog(g_apps[g_sm_ids[e]].name); + startmenu_close(); + launch_app(g_sm_ids[e]); + return 1; + } + return 1; /* 面板内空白:吞掉 */ +} + +/* ============================================================ + * 键盘(handle_key 调用;返回 1 = 已消费) + * ============================================================ */ + +int startmenu_key(u8 sc, int shift) { + if (!g_sm_open) return 0; + + if (sc == 0x01) { startmenu_close(); return 1; } /* Esc */ + if (sc == 0x0E) { /* Backspace */ + if (g_sm_search_len > 0) { + g_sm_search[--g_sm_search_len] = 0; + sm_filter_rebuild(); + } + return 1; + } + if (sc == 0x1C) { /* Enter 启动选中 */ + if (g_sm_search_len > 0 && g_sm_filter_n > 0) { + int e = g_sm_filter[g_sm_sel]; + slog("[startmenu] launch(kbd):"); + slog(g_apps[g_sm_ids[e]].name); + startmenu_close(); + launch_app(g_sm_ids[e]); + } else if (g_sm_grid_shown > 0) { + int id = g_sm_ids[0]; + startmenu_close(); + launch_app(id); + } + return 1; + } + if (g_sm_search_len > 0) { /* 过滤列表导航 */ + if (sc == 0x48) { /* Up */ + if (g_sm_sel > 0) g_sm_sel--; + return 1; + } + if (sc == 0x50) { /* Down */ + if (g_sm_sel < g_sm_filter_n - 1) g_sm_sel++; + return 1; + } + } + + /* 可打印字符 → 搜索串 */ + if (sc >= 58) return 1; + { + static const char normal[58] = { + 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t', + 'q','w','e','r','t','y','u','i','o','p','[',']','\n',0, + 'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\', + 'z','x','c','v','b','n','m',',','.','/',0,'*',0,' ' + }; + static const char shifted[58] = { + 0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t', + 'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0, + 'A','S','D','F','G','H','J','K','L',':','"','~',0,'|', + 'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' ' + }; + char c = shift ? shifted[sc] : normal[sc]; + if (c && c != '\n' && c != 27 && c != 8 && c != '\t') { + if (g_sm_search_len < SM_SEARCH_MAX) { + g_sm_search[g_sm_search_len++] = c; + g_sm_search[g_sm_search_len] = 0; + g_sm_sel = 0; + sm_filter_rebuild(); + } + } + } + return 1; +} diff --git a/CODE/desktop/taskbar.c b/CODE/desktop/taskbar.c index 7c8450a..bee0582 100644 --- a/CODE/desktop/taskbar.c +++ b/CODE/desktop/taskbar.c @@ -54,6 +54,9 @@ static wf_btn_state tb_hover_state(const int r[4]); #define WIFI_NETAPI_IS_WIRELESS 0x40 #define WIFI_NETAPI_CONNECT 0x48 #define WIFI_NETAPI_DEVICE_COUNT 0x08 +#define WIFI_NETAPI_DEVICE_INFO 0x10 +#define TRAY_NET_LINK_UP (1u << 0) +#define TRAY_NET_WIRELESS (1u << 3) #define WIFI_MAX_ROWS 8 /* 卡片内最多显示行数 */ #define WIFI_PW_MAX 63 @@ -79,6 +82,7 @@ int g_wifi_open = 0; static int g_wifi_mode; /* 0=列表 1=密码输入 */ static int g_wifi_dev = -1; /* 无线设备索引,-1 = 无 */ static int g_wifi_scanning; +static int g_wifi_guest; /* 1 = 结果来自 Linux 客户机 iw,不是原生网卡 */ static int g_wifi_n; /* 缓存的扫描结果数 */ static int g_wifi_sel = -1; static char g_wifi_sel_ssid[33]; @@ -103,6 +107,43 @@ static void *wifi_slot(u32 slot_off) { return (void *)(unsigned long)fn; } +/* 与 dkm_net_device_info 同布局:name 指针、6 字节 MAC、对齐后的 flags。 */ +typedef struct tray_netdev { + const char *name; + u8 mac[6]; + u32 flags; +} tray_netdev; +typedef int (*tray_info_fn)(u32 index, tray_netdev *out); + +static int g_tray_n, g_tray_up, g_tray_wl; + +/* 扫已注册网卡。返回 0 = 计数有效。链路起来的有线口在 QEMU e1000 上应为 1。 */ +static int tray_net_query(void) { + wifi_fn_device_count cnt = (wifi_fn_device_count)wifi_slot(WIFI_NETAPI_DEVICE_COUNT); + tray_info_fn info = (tray_info_fn)wifi_slot(WIFI_NETAPI_DEVICE_INFO); + if (!cnt) return -1; + u32 n = cnt(); + if (n > 8) n = 8; + int up = 0, wl = 0; + for (u32 i = 0; i < n; i++) { + tray_netdev d; + d.flags = 0; + if (info && info(i, &d) == 0) { + if (d.flags & TRAY_NET_LINK_UP) up++; + if (d.flags & TRAY_NET_WIRELESS) wl++; + } + } + if (n != (u32)g_tray_n || up != g_tray_up || wl != g_tray_wl) { + g_tray_n = (int)n; + g_tray_up = up; + g_tray_wl = wl; + slog_num("[tray] net devices=", g_tray_n); + slog_num("[tray] net link_up=", g_tray_up); + slog_num("[tray] net wireless=", g_tray_wl); + } + return 0; +} + /* 找无线设备;返回 0 = 已定位 g_wifi_dev */ static int wifi_dev_find(void) { wifi_fn_device_count cnt = (wifi_fn_device_count)wifi_slot(WIFI_NETAPI_DEVICE_COUNT); @@ -129,6 +170,176 @@ static void wifi_results_pull(void) { g_wifi_n = n; } +static void wifi_status_set(const char *m) { + int i = 0; + for (; m[i] && i < 63; i++) g_wifi_status[i] = m[i]; + g_wifi_status[i] = 0; +} + +static int wifi_line_eq_prefix(const char *s, const char *p) { + while (*p) { + if (*s++ != *p++) return 0; + } + return 1; +} + +static int wifi_parse_i(const char *s, int *out) { + int sign = 1; + if (*s == '-') { sign = -1; s++; } + if (*s < '0' || *s > '9') return -1; + int v = 0; + while (*s >= '0' && *s <= '9') { + v = v * 10 + (*s - '0'); + s++; + } + *out = v * sign; + return 0; +} + +static int wifi_hex_nibble(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} + +/* 把一行 iw scan 文本并进当前 BSS。返回 1 表示遇到新的 BSS 头。 */ +static int g_guest_have; +static char g_guest_ssid[33]; +static u8 g_guest_bssid[6]; +static u8 g_guest_ch; +static signed char g_guest_rssi; +static u8 g_guest_sec; + +static void wifi_guest_commit(void) { + if (!g_guest_have || !g_guest_ssid[0] || g_wifi_n >= 32) { + g_guest_have = 0; + return; + } + wifi_scan_item *it = &g_wifi_items[g_wifi_n]; + int i = 0; + for (; i < 32 && g_guest_ssid[i]; i++) it->ssid[i] = g_guest_ssid[i]; + it->ssid[i] = 0; + for (i = 0; i < 6; i++) it->bssid[i] = g_guest_bssid[i]; + it->channel = g_guest_ch; + it->rssi = g_guest_rssi; + it->security = g_guest_sec; + g_wifi_n++; + g_guest_have = 0; +} + +static void wifi_guest_line(const char *s) { + while (*s == ' ' || *s == '\t') s++; + if (wifi_line_eq_prefix(s, "BSS ")) { + wifi_guest_commit(); + g_guest_have = 1; + g_guest_ssid[0] = 0; + g_guest_ch = 0; + g_guest_rssi = -90; + g_guest_sec = 0; + for (int i = 0; i < 6; i++) g_guest_bssid[i] = 0; + const char *p = s + 4; + for (int i = 0; i < 6; i++) { + int hi = wifi_hex_nibble(p[0]); + int lo = wifi_hex_nibble(p[1]); + if (hi < 0 || lo < 0) { g_guest_have = 0; return; } + g_guest_bssid[i] = (u8)((hi << 4) | lo); + p += 2; + if (i < 5) { + if (*p != ':') { g_guest_have = 0; return; } + p++; + } + } + return; + } + if (!g_guest_have) return; + if (wifi_line_eq_prefix(s, "SSID:")) { + s += 5; + while (*s == ' ') s++; + int i = 0; + for (; s[i] && s[i] != '\n' && i < 32; i++) g_guest_ssid[i] = s[i]; + g_guest_ssid[i] = 0; + return; + } + if (wifi_line_eq_prefix(s, "signal:")) { + s += 7; + while (*s == ' ') s++; + int dbm = 0; + if (wifi_parse_i(s, &dbm) == 0) { + if (dbm < -128) dbm = -128; + if (dbm > 127) dbm = 127; + g_guest_rssi = (signed char)dbm; + } + return; + } + if (wifi_line_eq_prefix(s, "freq:")) { + s += 5; + while (*s == ' ') s++; + int freq = 0; + if (wifi_parse_i(s, &freq) == 0) { + int ch = 0; + if (freq >= 2412 && freq <= 2472) ch = (freq - 2407) / 5; + else if (freq >= 5180 && freq <= 5885) ch = (freq - 5000) / 5; + if (ch > 0 && ch < 256) g_guest_ch = (u8)ch; + } + return; + } + if (wifi_line_eq_prefix(s, "RSN:")) g_guest_sec = 3; /* WPA2 */ + else if (wifi_line_eq_prefix(s, "WPA:") && g_guest_sec == 0) g_guest_sec = 2; +} + +/* 客户机里跑 iw。没有 wlan0 或客户机没起来时返回负值,不填假热点。 */ +static int wifi_guest_scan(void) { + g_wifi_guest = 0; + g_wifi_n = 0; + g_guest_have = 0; + if (!g_lxc_svc || !g_lxc_svc->is_available || !g_lxc_svc->exec || !g_lxc_svc->file_read) + return -1; + if (!g_lxc_svc->is_available()) return -1; + const char *argv[] = { + "sh", "-c", + "if ! iw dev wlan0 info >/dev/null 2>&1; then echo NOIF; exit 0; fi; " + "iw dev wlan0 scan > /tmp/deshab-wifi.txt 2>/tmp/deshab-wifi.err; echo OK" + }; + char head[8]; + for (int i = 0; i < 8; i++) head[i] = 0; + u64 ec = 0; + int rc = g_lxc_svc->exec("/bin/sh", 3, argv, head, 7, 0, &ec); + slog_num("[wifi] guest iw rc=", rc); + if (rc != 0) return -2; + if (head[0] == 'N' && head[1] == 'O') return -3; + if (head[0] != 'O') return -2; + + static char chunk[2048]; + char line[192]; + int ln = 0; + u64 off = 0; + for (int part = 0; part < 32; part++) { + u64 got = 0; + int fr = g_lxc_svc->file_read("/tmp/deshab-wifi.txt", off, chunk, sizeof(chunk), &got, 0); + if (fr != 0 || got == 0) break; + for (u64 i = 0; i < got; i++) { + char c = chunk[i]; + if (c == '\n' || ln >= (int)sizeof(line) - 1) { + line[ln] = 0; + if (ln) wifi_guest_line(line); + ln = 0; + } else if (c != '\r') { + line[ln++] = c; + } + } + off += got; + } + if (ln) { + line[ln] = 0; + wifi_guest_line(line); + } + wifi_guest_commit(); + g_wifi_guest = 1; + slog_num("[wifi] guest aps=", g_wifi_n); + return 0; +} + /* 打开卡片并踢一脚扫描 */ static void wifi_card_open(void) { g_wifi_open = 1; @@ -137,13 +348,32 @@ static void wifi_card_open(void) { g_wifi_pw[0] = 0; g_wifi_status[0] = 0; g_wifi_n = 0; + g_wifi_guest = 0; g_wifi_scanning = 1; + tray_net_query(); if (wifi_dev_find() != 0) { g_wifi_scanning = 0; - const char *m = "未检测到无线网卡"; - int i = 0; - for (; m[i]; i++) g_wifi_status[i] = m[i]; - g_wifi_status[i] = 0; + int grc = wifi_guest_scan(); + if (grc == 0) { + if (g_wifi_n == 0) wifi_status_set("未发现网络"); + else wifi_status_set("来自客户机"); + return; + } + if (grc == -3) { + wifi_status_set("客户机无无线接口"); + slog("[wifi] guest has no wlan0"); + return; + } + if (grc == -2) { + wifi_status_set("客户机扫描失败"); + return; + } + if (g_tray_up > 0) { + wifi_status_set("有线已连接"); + slog("[wifi] wired link up, no wireless device"); + return; + } + wifi_status_set("未检测到无线网卡"); slog("[wifi] no wireless device"); return; } @@ -160,6 +390,11 @@ static void wifi_card_open(void) { /* 发起连接(当前选中网络 + 密码) */ static void wifi_connect_go(void) { + if (g_wifi_guest) { + wifi_status_set("客户机关联未实现"); + slog("[wifi] guest assoc not implemented"); + return; + } wifi_fn_connect conn = (wifi_fn_connect)wifi_slot(WIFI_NETAPI_CONNECT); if (!conn || g_wifi_dev < 0) return; const char *m = "连接中..."; @@ -244,7 +479,7 @@ static void wifi_draw(void) { y += 42; } else { /* ---- 列表模式 ---- */ - if (g_wifi_dev < 0) { + if (g_wifi_dev < 0 && g_wifi_n == 0) { zh_draw_string(&g_fb, g_wifi_status[0] ? g_wifi_status : "无无线设备", cx + 14, y + 8, t->text_dim, 0); y += 34; @@ -371,6 +606,85 @@ int wifi_key(u8 sc, int shift) { return 1; } +/* ============================================================ + * 快速设置卡片(托盘音量按钮弹出:音量/亮度滑块) + * + * M6 提前落地最小版:du_wf_slider 组件实战。音量接 g_vol(状态), + * 亮度接 g_bright(apply_brightness 真实调暗)。拖拽复用主循环 + * g_drag_slider 通道(卡片绘制时把自己的轨道矩形写进 g_vol_rect/ + * g_bri_rect,主循环 slider_set 即可作用到卡片滑块)。 + * ============================================================ */ + +int g_qs_open = 0; +static int g_qs_card_rect[4]; +static int g_qs_vol_rect[4]; /* 音量滑块命中区(镜像到 g_vol_rect) */ +static int g_qs_bri_rect[4]; /* 亮度滑块命中区(镜像到 g_bri_rect) */ + +static void qs_draw(void) { + if (!g_qs_open) return; + const wf_theme *t = tb_theme(); + int W = (int)g_fb_w; + const int CW = 300, CH = 46 + 2 * 58 + 8; + + int cx = g_tb2_vol_rect[0] + g_tb2_vol_rect[2] - CW; + if (cx < 8) cx = 8; + if (cx + CW > W - 8) cx = W - 8 - CW; + int cy = (int)g_fb_h - TB_H - TB_BOTTOM_GAP - CH - 8; + + g_qs_card_rect[0] = cx; g_qs_card_rect[1] = cy; + g_qs_card_rect[2] = CW; g_qs_card_rect[3] = CH; + du_wf_card(&g_fb, cx, cy, CW, CH, 235, t); + + zh_draw_string(&g_fb, "快速设置", cx + 14, cy + 12, t->text_primary, 0); + + int y = cy + 46; + /* 音量行:标签 + 百分比 + 滑块 */ + { + char vb[8]; + int n = u32dec((u32)g_vol, vb); + vb[n] = '%'; vb[n + 1] = 0; + zh_draw_string(&g_fb, "音量", cx + 14, y, t->text_secondary, 0); + du_draw_string(&g_fb, vb, cx + CW - 14 - (n + 1) * 8, y, + t->text_primary, 0, DU_ASCII_STEP); + du_wf_slider(&g_fb, cx + 14, y + 22, CW - 28, g_vol, t, g_qs_vol_rect); + g_vol_rect[0] = g_qs_vol_rect[0]; g_vol_rect[1] = g_qs_vol_rect[1]; + g_vol_rect[2] = g_qs_vol_rect[2]; g_vol_rect[3] = g_qs_vol_rect[3]; + y += 58; + } + /* 亮度行(真实调暗:apply_brightness 每帧消费 g_bright) */ + { + char vb[8]; + int n = u32dec((u32)g_bright, vb); + vb[n] = '%'; vb[n + 1] = 0; + zh_draw_string(&g_fb, "亮度", cx + 14, y, t->text_secondary, 0); + du_draw_string(&g_fb, vb, cx + CW - 14 - (n + 1) * 8, y, + t->text_primary, 0, DU_ASCII_STEP); + du_wf_slider(&g_fb, cx + 14, y + 22, CW - 28, g_bright, t, g_qs_bri_rect); + g_bri_rect[0] = g_qs_bri_rect[0]; g_bri_rect[1] = g_qs_bri_rect[1]; + g_bri_rect[2] = g_qs_bri_rect[2]; g_bri_rect[3] = g_qs_bri_rect[3]; + } +} + +/* 卡片点击(返回 1 = 已消费;点外部收起并放行) */ +static int qs_click(int mx, int my) { + if (!g_qs_open) return 0; + if (!rect_hit(g_qs_card_rect, mx, my)) { + g_qs_open = 0; + return 0; + } + if (rect_hit(g_qs_vol_rect, mx, my)) { + g_drag_slider = 1; + slider_set(g_vol_rect, &g_vol, mx); + return 1; + } + if (rect_hit(g_qs_bri_rect, mx, my)) { + g_drag_slider = 2; + slider_set(g_bri_rect, &g_bright, mx); + return 1; + } + return 1; +} + /* ---- 开始菜单 logo(textures/startMenuLogo.rgba,加载失败回退字形) ---- */ static int g_smlogo_ok = 0; @@ -507,7 +821,15 @@ void taskbar_draw(void) { g_tb2_net_rect[0] = x; g_tb2_net_rect[1] = iy; g_tb2_net_rect[2] = TB_BTN; g_tb2_net_rect[3] = TB_BTN; du_wf_icon_button(&g_fb, x, iy, TB_BTN, t, tb_hover_state(g_tb2_net_rect)); - du_wf_icon(&g_fb, WF_ICON_NETWORK, x + (TB_BTN - 20) / 2, iy + (TB_BTN - 20) / 2, 20, t->text_primary); + tray_net_query(); + { + u32 col = g_tray_up > 0 ? t->accent : t->text_dim; + int ix = x + (TB_BTN - 20) / 2; + int iy2 = iy + (TB_BTN - 20) / 2; + du_wf_icon(&g_fb, WF_ICON_NETWORK, ix, iy2, 20, col); + if (g_tray_n == 0 || g_tray_up == 0) + du_wf_line(&g_fb, ix + 2, iy2 + 18, ix + 18, iy2 + 2, 2, col); + } x += TB_BTN + 2; g_tb2_vol_rect[0] = x; g_tb2_vol_rect[1] = iy; @@ -533,8 +855,12 @@ void taskbar_draw(void) { g_tb2_tray_rect[0] = tx; g_tb2_tray_rect[1] = by; g_tb2_tray_rect[2] = tray_w; g_tb2_tray_rect[3] = TB_H; + /* 快速设置卡片(音量/亮度) */ + qs_draw(); /* WiFi 快捷卡片(最顶) */ wifi_draw(); + /* M3: 开始菜单(叠加在一切之上) */ + startmenu_draw(); } /* ============================================================ @@ -542,9 +868,14 @@ void taskbar_draw(void) { * ============================================================ */ int taskbar_click(int mx, int my) { + /* M3: 开始菜单(打开时优先消费;点面板外关闭并放行) */ + if (g_sm_open) { + if (startmenu_click(mx, my)) return 1; + if (rect_hit(g_tb2_start_rect, mx, my)) return 1; /* 已在 click 内关闭 */ + } /* 主任务栏 */ if (rect_hit(g_tb2_start_rect, mx, my)) { - slog("[taskbar] start button (start menu: M3)"); + startmenu_toggle(); return 1; } if (rect_hit(g_tb2_tv_rect, mx, my)) { @@ -567,6 +898,8 @@ int taskbar_click(int mx, int my) { } /* WiFi 卡片(打开时优先消费;点外部收起并放行点击) */ if (g_wifi_open) wifi_click(mx, my); + /* 快速设置卡片(打开时优先消费;点外部收起并放行点击) */ + if (g_qs_open) qs_click(mx, my); /* 托盘 */ if (rect_hit(g_tb2_pwr_rect, mx, my)) { @@ -575,12 +908,16 @@ int taskbar_click(int mx, int my) { } if (rect_hit(g_tb2_net_rect, mx, my)) { /* 网络:弹出/收起 WiFi 快捷卡片(扫描 → 选网 → 密码 → 连接) */ + g_qs_open = 0; if (g_wifi_open) g_wifi_open = 0; else wifi_card_open(); return 1; } if (rect_hit(g_tb2_vol_rect, mx, my)) { - slog("[taskbar] volume (quick settings: M6)"); + /* 音量:弹出/收起快速设置卡片(音量/亮度滑块) */ + g_wifi_open = 0; + if (g_qs_open) g_qs_open = 0; + else g_qs_open = 1; return 1; } if (rect_hit(g_tb2_clk_rect, mx, my)) { diff --git a/CODE/desktop/wm.c b/CODE/desktop/wm.c index 6048c70..dc8f247 100644 --- a/CODE/desktop/wm.c +++ b/CODE/desktop/wm.c @@ -81,8 +81,9 @@ desktop_window *win_create(int app_id, const char *title, int x, int y, int w, i void win_destroy(desktop_window *w) { int idx = -1; + int id = w->id; for (int i = 0; i < g_win_count; i++) { - if (g_windows[i].id == w->id) { idx = i; break; } + if (g_windows[i].id == id) { idx = i; break; } } if (idx < 0) return; @@ -92,9 +93,10 @@ void win_destroy(desktop_window *w) { if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_destroy && w->app_state) { g_apps[w->app_id].on_destroy(w->app_state); } + /* 挪位之后 w 指向的是下一扇窗口,关闭判断必须用挪位前记下的 id。 */ for (int i = idx; i < g_win_count - 1; i++) g_windows[i] = g_windows[i+1]; g_win_count--; - if (g_focused_win == w->id) { + if (g_focused_win == id) { g_focused_win = -1; for (int i = g_win_count - 1; i >= 0; i--) { if (g_windows[i].ws == g_ws_cur && g_windows[i].visible && !g_windows[i].minimized) { @@ -148,7 +150,8 @@ static int wm_fluent_hit_btn(desktop_window *w, int mx, int my, int btn) { static int g_wm_btn_hover = 0; int win_hit_close_btn(desktop_window *w, int mx, int my) { - if (g_fluent) return wm_fluent_hit_btn(w, mx, my, 3); + /* 绘制顺序是 min=*3、max=*2、close=*1(最右)。命中必须跟绘制同一序号。 */ + if (g_fluent) return wm_fluent_hit_btn(w, mx, my, 1); int bx = w->x + w->w - CLOSE_BTN_OFFSET; int by = w->y + (KATE_TITLEBAR_H - CLOSE_BTN_SIZE) / 2; return mx >= bx && mx < bx + CLOSE_BTN_SIZE && @@ -156,7 +159,7 @@ int win_hit_close_btn(desktop_window *w, int mx, int my) { } int win_hit_min_btn(desktop_window *w, int mx, int my) { - return g_fluent ? wm_fluent_hit_btn(w, mx, my, 1) : 0; + return g_fluent ? wm_fluent_hit_btn(w, mx, my, 3) : 0; } int win_hit_max_btn(desktop_window *w, int mx, int my) { diff --git a/CODE/dsk/ext4.c b/CODE/dsk/ext4.c index cd81a70..17c4d08 100644 --- a/CODE/dsk/ext4.c +++ b/CODE/dsk/ext4.c @@ -7,9 +7,22 @@ static ext4_block_read_fn rd_fn; static ext4_block_write_fn wr_fn; static void (*fatalf)(const char *sym); +static void (*user_fatal)(const char *sym); static void (*logl_fn)(const char *s); static void (*logh_fn)(const char *p, ext4_u64 v); +/* panic fallback:全部 fatalf 经 ext4_fail 中转。strict 时 user_fatal 不返回; + * fallback 时 user_fatal 返回,ext4_fail 长跳回最外层公开入口(EXT4_ENTER), + * 该入口关闭 ext4 并返回失败,DSK 走 FAT32 ESP 路径。 */ +static void *g_ext4_jb[5]; +static int g_ext4_depth; + +static void ext4_fail(const char *sym) { + user_fatal(sym); + if (g_ext4_depth > 0) __builtin_longjmp(g_ext4_jb, 1); + for (;;) __asm__ volatile("cli; hlt"); +} + /* ---------------- 小端读取 ---------------- */ static ext4_u16 r16(const ext4_u8 *p) { return (ext4_u16)(p[0] | (p[1] << 8)); } static ext4_u32 r32(const ext4_u8 *p) { @@ -21,6 +34,7 @@ static void w32(ext4_u8 *p, ext4_u32 v) { p[0]=v&0xFF; p[1]=(v>>8)&0xFF; p[2]=(v static void w16(ext4_u8 *p, ext4_u16 v) { p[0]=v&0xFF; p[1]=(v>>8)&0xFF; } /* ---------------- 状态 ---------------- */ +static ext4_u32 g_dev; /* 块设备号:优先卷标 DESHAB,而不是注册表第 0 号 */ static ext4_u64 g_part_lba; /* ext4 分区起始 LBA */ static ext4_u32 g_block_size; /* 4096 */ static ext4_u32 g_sec_per_block; /* 8 */ @@ -42,6 +56,7 @@ static ext4_u8 g_blk2[4096]; /* 第二 scratch(inode 表与目录并行 #define EXT4_ROOT_INO 2 /* incompat feature 位 */ +#define COMPAT_HAS_JOURNAL 0x0004u #define INCOMPAT_FILETYPE 0x0002u #define INCOMPAT_RECOVER 0x0004u #define INCOMPAT_META_BG 0x0010u @@ -59,31 +74,121 @@ static ext4_u8 g_blk2[4096]; /* 第二 scratch(inode 表与目录并行 /* ---------------- 底层 IO(失败即 fatal) ---------------- */ static void rd_sec(ext4_u64 lba, ext4_u32 cnt, void *buf) { - if (rd_fn(0, lba, cnt, buf) != 0) fatalf("FS-E20 EXT4 BLOCK READ FAILED"); + if (rd_fn(g_dev, lba, cnt, buf) != 0) fatalf("FS-E20 EXT4 BLOCK READ FAILED"); } static void wr_sec(ext4_u64 lba, ext4_u32 cnt, const void *buf) { - if (wr_fn(0, lba, cnt, buf) != 0) fatalf("FS-E21 EXT4 BLOCK WRITE FAILED"); + if (wr_fn(g_dev, lba, cnt, buf) != 0) fatalf("FS-E21 EXT4 BLOCK WRITE FAILED"); } /* 块号 → LBA */ static ext4_u64 blk_lba(ext4_u32 blk) { return g_part_lba + (ext4_u64)blk * g_sec_per_block; } static void rd_blk(ext4_u32 blk, void *buf) { rd_sec(blk_lba(blk), g_sec_per_block, buf); } static void wr_blk(ext4_u32 blk, const void *buf) { wr_sec(blk_lba(blk), g_sec_per_block, buf); } +/* 第一份对不上的 ext4,供 FS-E24 屏上读出。 */ +static ext4_u32 g_miss_dev; +static ext4_u64 g_miss_lba; +static ext4_u8 g_miss_name[6]; +static ext4_u8 g_miss_label[6]; +static ext4_u32 g_miss_feat; +static int g_miss_boot; +static char g_probe_map[8]; +static int g_boot_dev = -1; +static int g_ext4_ok; +static ext4_u32 g_ndev = 8; + +void ext4_set_device_count(unsigned int count) { + g_ndev = count > 8 ? 8 : (ext4_u32)count; +} + +static void ext4_note_miss(ext4_u32 dev, ext4_u64 lba, const ext4_u8 *name, + const ext4_u8 *label, ext4_u32 feat, int boot) { + g_miss_dev = dev; + g_miss_lba = lba; + g_miss_feat = feat; + g_miss_boot = boot; + for (int j = 0; j < 6; j++) { + g_miss_name[j] = name[j * 2]; + g_miss_label[j] = label[j]; + } +} + +static void ext4_fatal_not_found(void) { + static char msg[72]; + static const char hex[] = "0123456789ABCDEF"; + const char *p = "FS-E24 DEV "; + int n = 0; + while (p[n]) { msg[n] = p[n]; n++; } + msg[n++] = (char)('0' + (g_miss_dev % 10)); + p = " LBA "; + while (*p) msg[n++] = *p++; + for (int s = 28; s >= 0; s -= 4) msg[n++] = hex[(g_miss_lba >> s) & 0xF]; + msg[n++] = ' '; + for (int j = 0; j < 6; j++) { + ext4_u8 c = g_miss_name[j]; + msg[n++] = (c >= 32 && c < 127) ? (char)c : '.'; + } + msg[n++] = '/'; + for (int j = 0; j < 6; j++) { + ext4_u8 c = g_miss_label[j]; + msg[n++] = (c >= 32 && c < 127) ? (char)c : '.'; + } + msg[n++] = ' '; + for (int s = 28; s >= 0; s -= 4) msg[n++] = hex[(g_miss_feat >> s) & 0xF]; + msg[n++] = g_miss_boot ? 'B' : '-'; + msg[n++] = ' '; + for (int j = 0; j < 8; j++) msg[n++] = g_probe_map[j] ? g_probe_map[j] : '-'; + msg[n] = 0; + fatalf(msg); +} + /* ---------------- GPT 探测 ext4 分区 ---------------- */ -static void gpt_find_ext4(void) { +/* 返回 2 = 卷标 DESHAB,1 = 其它 ext4,0 = 这块盘没有可用 ext4。探测失败不 panic。 */ +static int probe_ext4_dev(ext4_u32 dev, ext4_u64 *out_lba, ext4_u8 *out_sb) { ext4_u8 gpt[512]; - rd_sec(1, 1, gpt); - if (gpt[0] != 'E' || gpt[1] != 'F' || gpt[2] != 'I' || gpt[3] != ' ') - fatalf("FS-E22 GPT SIGNATURE NOT FOUND"); + if (dev >= g_ndev) return 0; + if (dev < 8) g_probe_map[dev] = 'r'; + if (rd_fn(dev, 1, 1, gpt) != 0) return 0; + if (gpt[0] != 'E' || gpt[1] != 'F' || gpt[2] != 'I' || gpt[3] != ' ') { + if (dev < 8) { + g_probe_map[dev] = 'n'; + ext4_u8 mbr[512]; + if (rd_fn(dev, 0, 1, mbr) == 0 && mbr[510] == 0x55 && mbr[511] == 0xAA) + g_probe_map[dev] = 'm'; + } + return 0; + } + if (dev < 8) g_probe_map[dev] = 'g'; ext4_u64 ent_lba = r64(gpt + 0x48); ext4_u32 ent_num = r32(gpt + 0x50); ext4_u32 ent_sz = r32(gpt + 0x54); - if (ent_sz < 128 || ent_num == 0 || ent_num > 128) - fatalf("FS-E23 GPT ENTRY TABLE INVALID"); - static ext4_u8 ents[128 * 128]; - rd_sec(ent_lba, (ent_num * ent_sz + 511) / 512, ents); - for (ext4_u32 i = 0; i < ent_num; i++) { - const ext4_u8 *e = ents + i * ent_sz; + if (ent_sz < 128 || ent_num == 0 || ent_num > 128) return 0; + /* 逐扇区读分区表。U 盘驱动对一次几十扇区的 READ 会失败, + * 整表读取失败会被当成“没有启动盘”(FS-E24)。 */ + int boot_disk = 0; + { + ext4_u8 bpb[512]; + if (rd_fn(dev, 2048, 1, bpb) == 0 && bpb[510] == 0x55 && bpb[511] == 0xAA) { + static const char fatlab[] = "DESHABBOOT"; + int ok = 1; + for (int j = 0; j < 10; j++) if (bpb[71 + j] != (ext4_u8)fatlab[j]) ok = 0; + if (ok) { + boot_disk = 1; + if (g_boot_dev < 0) g_boot_dev = (int)dev; + } + } + } + int best = 0; + ext4_u32 ent_secs = (ent_num * ent_sz + 511) / 512; + if (ent_secs > 32) ent_secs = 32; + for (ext4_u32 sec = 0; sec < ent_secs; sec++) { + ext4_u8 secbuf[512]; + if (rd_fn(dev, ent_lba + sec, 1, secbuf) != 0) break; + ext4_u32 per = 512 / ent_sz; + if (per == 0) per = 1; + for (ext4_u32 k = 0; k < per; k++) { + ext4_u32 i = sec * per + k; + if (i >= ent_num) break; + const ext4_u8 *e = secbuf + k * ent_sz; static const ext4_u8 linux_guid[16] = { 0xAF,0x3D,0xC6,0x0F, 0x83,0x84, 0x72,0x47, 0x8E,0x79, 0x3D,0x69,0xD8,0x47,0x7D,0xE4 }; @@ -92,24 +197,76 @@ static void gpt_find_ext4(void) { if (!is_linux) continue; ext4_u64 start = r64(e + 0x20); if (start == 0) continue; - /* 探测 superblock 魔数(offset 1024 处 0xEF53) */ + /* GPT 分区名 UTF-16LE,打包时为 DESHAB。不依赖超级块卷标。 */ + int name_ok = 1; + static const char want[] = "DESHAB"; + for (int j = 0; j < 6; j++) { + if (e[0x38 + j * 2] != (ext4_u8)want[j] || e[0x39 + j * 2] != 0) name_ok = 0; + } ext4_u8 sb_probe[1024]; - rd_sec(start + 2, 2, sb_probe); - if (r16(sb_probe + 0x38) == EXT4_SUPER_MAGIC) { - g_part_lba = start; - for (int j = 0; j < 1024; j++) g_sb[j] = sb_probe[j]; - return; + if (rd_fn(dev, start + 2, 1, sb_probe) != 0) continue; + if (rd_fn(dev, start + 3, 1, sb_probe + 512) != 0) continue; + if (r16(sb_probe + 0x38) != EXT4_SUPER_MAGIC) continue; + int label_ok = 1; + for (int j = 0; j < 6; j++) if (sb_probe[0x78 + j] != (ext4_u8)want[j]) label_ok = 0; + /* 分区名和卷标都在各自扇区的前 512 字节里。读全了就必然对上 DESHAB。 + * 对不上仍接受打包特征:4K 块且 incompat 恰好是 FILETYPE|EXTENTS|FLEX_BG。 + * 普通发行版 ext4 带 64bit/metadata_csum,不会是 0x242。 */ + ext4_u32 log_bs = r32(sb_probe + 0x18); + ext4_u32 incompat = r32(sb_probe + 0x60); + int feat_ok = (log_bs == 2 && incompat == 0x242u); + if (name_ok || label_ok || boot_disk || feat_ok) { + *out_lba = start; + for (int j = 0; j < 1024; j++) out_sb[j] = sb_probe[j]; + if (dev < 8) g_probe_map[dev] = 'D'; + return 2; + } + if (best == 0) { + best = 1; + if (dev < 8) g_probe_map[dev] = 'o'; + ext4_note_miss(dev, start, e + 0x38, sb_probe + 0x78, incompat, boot_disk); + } } } - fatalf("FS-E24 EXT4 PARTITION NOT FOUND IN GPT"); + if (dev < 8 && boot_disk && g_probe_map[dev] != 'D') g_probe_map[dev] = 'B'; + return best; +} + +static void gpt_find_ext4(void) { + int saw_other = 0; + g_boot_dev = -1; + g_ext4_ok = 0; + for (int j = 0; j < 8; j++) g_probe_map[j] = '-'; + for (ext4_u32 dev = 0; dev < 8; dev++) { + ext4_u64 lba = 0; + static ext4_u8 sb[1024]; + int hit = probe_ext4_dev(dev, &lba, sb); + if (hit == 2) { + g_dev = dev; + g_part_lba = lba; + for (int j = 0; j < 1024; j++) g_sb[j] = sb[j]; + logh_fn("[EXT4] DESHAB disk=", dev); + g_ext4_ok = 1; + return; + } + if (hit == 1) saw_other = 1; + } + /* 机器里那份 primary ext4 不能挂。U 盘如果已经露出 DESHABBOOT, + * 桌面仍从它的 FAT 启动,不因为 ext4 身份对不上而停在莲花屏。 */ + if (g_boot_dev >= 0) { + g_dev = (ext4_u32)g_boot_dev; + g_ext4_ok = 0; + logh_fn("[EXT4] FAT boot disk, ext4 skipped, dev=", (ext4_u64)g_boot_dev); + return; + } + (void)saw_other; + ext4_fatal_not_found(); } /* ---------------- 挂载 ---------------- */ -int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr, - void (*fatal)(const char *sym), void (*logl)(const char *s), - void (*logh)(const char *p, ext4_u64 v)) { - rd_fn = rd; wr_fn = wr; fatalf = fatal; logl_fn = logl; logh_fn = logh; +static int ext4_mount_impl(void) { gpt_find_ext4(); + if (!g_ext4_ok) return 0; logh_fn("[EXT4] partition LBA=", g_part_lba); if (r16(g_sb + 0x38) != EXT4_SUPER_MAGIC) fatalf("FS-E25 SUPERBLOCK MAGIC MISMATCH"); @@ -125,15 +282,44 @@ int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr, if (g_desc_size < 32) g_desc_size = 32; g_blocks_count = r32(g_sb + 0x04); + ext4_u32 compat = r32(g_sb + 0x5C); ext4_u32 incompat = r32(g_sb + 0x60); - if (incompat & INCOMPAT_RECOVER) fatalf("FS-E27 JOURNAL DIRTY (RECOVER SET)"); - if (incompat & (INCOMPAT_64BIT | INCOMPAT_METADATA_CSUM | INCOMPAT_MMP | - INCOMPAT_INLINE_DATA | INCOMPAT_META_BG)) - fatalf("FS-E28 UNSUPPORTED EXT4 FEATURE"); - if (incompat & ~INCOMPAT_SUPPORTED) fatalf("FS-E28 UNSUPPORTED EXT4 FEATURE"); + int is_deshab = 1; + { + static const char want[] = "DESHAB"; + for (int j = 0; j < 6; j++) if (g_sb[0x78 + j] != (ext4_u8)want[j]) is_deshab = 0; + } + /* 打包卷没有 journal,实机扫到的其它 ext4 也经常带着 needs_recovery。 + * 驱动不做 journal 回放:两个标志都清掉再挂载,避免 FS-E27 挡住启动。 */ + (void)is_deshab; + if (incompat & INCOMPAT_RECOVER) { + incompat &= ~INCOMPAT_RECOVER; + w32(g_sb + 0x60, incompat); + logl_fn("[EXT4] cleared needs_recovery"); + } + if (compat & COMPAT_HAS_JOURNAL) { + compat &= ~COMPAT_HAS_JOURNAL; + w32(g_sb + 0x5C, compat); + logl_fn("[EXT4] cleared has_journal"); + } + if ((incompat & (INCOMPAT_64BIT | INCOMPAT_METADATA_CSUM | INCOMPAT_MMP | + INCOMPAT_INLINE_DATA | INCOMPAT_META_BG)) || + (incompat & ~INCOMPAT_SUPPORTED)) { + static char msg[28]; + static const char hex[] = "0123456789ABCDEF"; + const char *p = "FS-E28 FEAT "; + int n = 0; + while (p[n]) { msg[n] = p[n]; n++; } + for (int s = 28; s >= 0; s -= 4) msg[n++] = hex[(incompat >> s) & 0xF]; + msg[n] = 0; + fatalf(msg); + } ext4_u16 state = r16(g_sb + 0x3A); - if (state != 1) fatalf("FS-E29 FILESYSTEM NOT CLEAN"); + if (state != 1) { + w16(g_sb + 0x3A, 1); + logl_fn("[EXT4] forced clean state"); + } if (g_inode_size != 256) fatalf("FS-E2A UNSUPPORTED INODE SIZE"); if (g_first_data_block != 0) fatalf("FS-E2B UNEXPECTED FIRST DATA BLOCK"); @@ -162,6 +348,28 @@ int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr, return 0; } +static int ext4_abort(void) { + g_ext4_depth = 0; + g_ext4_ok = 0; + logl_fn("[EXT4] fallback: operation aborted, ext4 disabled (FAT32 ESP only)"); + return -1; +} + +int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr, + void (*fatal)(const char *sym), void (*logl)(const char *s), + void (*logh)(const char *p, ext4_u64 v)) { + rd_fn = rd; wr_fn = wr; logl_fn = logl; logh_fn = logh; + user_fatal = fatal; + fatalf = ext4_fail; + if (__builtin_setjmp(g_ext4_jb)) return ext4_abort(); + g_ext4_depth = 1; + int rc = ext4_mount_impl(); + g_ext4_depth = 0; + return rc; +} + +unsigned int ext4_device_index(void) { return g_dev; } + /* ---------------- 组描述符访问 ---------------- */ /* 32B 组描述符:0x00 block_bitmap, 0x04 inode_bitmap, 0x08 inode_table, * 0x0C free_blocks_lo(u16), 0x0E free_inodes_lo(u16), 0x10 used_dirs(u16) */ @@ -445,7 +653,7 @@ static void resolve_parent(const char *path, ext4_u32 *parent_ino, } /* ---------------- 读文件 ---------------- */ -int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size) { +static int ext4_read_file_impl(const char *path, ext4_u8 **out, ext4_u32 *size) { ext4_u32 parent; const char *name; ext4_u32 name_len; resolve_parent(path, &parent, &name, &name_len); ext4_u8 dir_ino[256]; @@ -465,6 +673,15 @@ int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size) { return 0; } +int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size) { + if (g_ext4_depth > 0) return ext4_read_file_impl(path, out, size); + if (__builtin_setjmp(g_ext4_jb)) return ext4_abort(); + g_ext4_depth = 1; + int rc = ext4_read_file_impl(path, out, size); + g_ext4_depth = 0; + return rc; +} + /* ---------------- 目录项插入 ---------------- */ /* 在 dir inode 的现有块中找空隙插入。返回 0 成功;1 无空隙(需扩块)。 */ static int dir_insert_existing(ext4_u8 *dir_ino, const char *name, ext4_u32 name_len, ext4_u32 target) { @@ -556,7 +773,7 @@ static void dir_insert_new_block(ext4_u8 *dir_ino, const char *name, ext4_u32 na } /* ---------------- 写文件(覆写/创建) ---------------- */ -int ext4_write_file(const char *path, const ext4_u8 *data, ext4_u32 size) { +static int ext4_write_file_impl(const char *path, const ext4_u8 *data, ext4_u32 size) { ext4_u32 parent; const char *name; ext4_u32 name_len; resolve_parent(path, &parent, &name, &name_len); ext4_u8 dir_ino[256]; @@ -723,8 +940,30 @@ root_done: return 0; } +int ext4_write_file(const char *path, const ext4_u8 *data, ext4_u32 size) { + if (g_ext4_depth > 0) return ext4_write_file_impl(path, data, size); + if (__builtin_setjmp(g_ext4_jb)) return ext4_abort(); + g_ext4_depth = 1; + int rc = ext4_write_file_impl(path, data, size); + g_ext4_depth = 0; + return rc; +} + /* ---------------- 启动自检 ---------------- */ +static void ext4_boot_selftest_impl(void); + void ext4_boot_selftest(void) { + if (!g_ext4_ok) { + logl_fn("[EXT4] selftest skipped, no DESHAB volume"); + return; + } + if (__builtin_setjmp(g_ext4_jb)) { ext4_abort(); return; } + g_ext4_depth = 1; + ext4_boot_selftest_impl(); + g_ext4_depth = 0; +} + +static void ext4_boot_selftest_impl(void) { /* 读测试:p2 上的 FUCK 配置副本 */ ext4_u8 *d; ext4_u32 n; ext4_read_file("/system/deshab64/FUCK", &d, &n); diff --git a/CODE/dsk/ext4.h b/CODE/dsk/ext4.h index 452a456..d9df7d9 100644 --- a/CODE/dsk/ext4.h +++ b/CODE/dsk/ext4.h @@ -2,6 +2,8 @@ * * 严格错误策略:任何错误(挂载失败/feature 不支持/IO 失败/校验不符) * 一律经 fatal(sym) 整屏 panic 停机,错误码体系 FS-E1x / FS-E2x。 + * panic fallback 模式(fatal 返回):当前公开调用中止并返回 -1,ext4 置为 + * 不可用,后续只走 FAT32 ESP。 * * 支持范围(mkfs.ext4 由 pack_system_image.sh 以 -O * ^has_journal,^metadata_csum,^64bit,^resize_inode -b 4096 -I 256 创建): @@ -29,6 +31,11 @@ int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr, void (*fatal)(const char *sym), void (*logl)(const char *s), void (*logh)(const char *p, ext4_u64 v)); +/* ext4_mount 选中的块设备号。FAT 必须读写同一块盘。 */ +unsigned int ext4_device_index(void); +/* 已注册块设备个数。探测不会把“没有这块盘”记成读失败。 */ +void ext4_set_device_count(unsigned int count); + /* 按路径读文件("/system/deshab64/FUCK"),返回内部静态缓冲。 * 任何失败 fatal。成功返回 0 并填充 out/size。 */ int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size); diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c index 4664ea3..a0cc17f 100644 --- a/CODE/dsk/main.c +++ b/CODE/dsk/main.c @@ -40,7 +40,8 @@ typedef int (*block_write_fn)(u32 index, u64 lba, u32 count, const void *buf); * 在此定义镜像结构体并用 _Static_assert 校验 block 字段偏移 * 与 dkm.h 中 dkm_kernel_api.block (0xA8) 一致。 */ typedef struct dsk_block_api_mirror { - void *pad0; void *pad1; /* register_device, device_count */ + void *pad0; + u32 (*device_count)(void); block_read_fn read; block_write_fn write; void *pad2; void *pad3; void *pad4; /* sector_size, device_name, set_write_fn */ @@ -132,17 +133,18 @@ DSK_STAT_DECL(fat32_write); DSK_STAT_DECL(elf_load); DSK_STAT_DECL(mod_schedule); +static u32 g_fat_dev; static int fat32_read_sectors(u32 lba, u32 count, u8 *out) { DSK_STAT_INC(fat32_read); - return g_block_read ? g_block_read(0, (u64)lba + g_fat_lba_off, count, out) : -1; + return g_block_read ? g_block_read(g_fat_dev, (u64)lba + g_fat_lba_off, count, out) : -1; } static int fat32_write_sectors(u32 lba, u32 count, const u8 *buf) { DSK_STAT_INC(fat32_write); - return g_block_write ? g_block_write(0, (u64)lba + g_fat_lba_off, count, buf) : -1; + return g_block_write ? g_block_write(g_fat_dev, (u64)lba + g_fat_lba_off, count, buf) : -1; } static void fat32_probe_esp(void) { - g_fat_lba_off = (u32)fat32_part_probe(g_block_read, 0); + g_fat_lba_off = (u32)fat32_part_probe(g_block_read, g_fat_dev); logh("[DSK] FAT32 volume LBA=", (u64)g_fat_lba_off); } @@ -637,8 +639,6 @@ static void dsk_draw_string(u32 *fb, const char *s, i64 x, i64 y, u32 fg, int sp * 不再静默跳过,必须整屏 fatal 显示错误码后停机。 * 错误码体系:FS-E(文件系统层)。 * =================================================================== */ -static u64 dsk_fat_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; } - /* 莲花 panic 界面:深蓝底 + ohMyLogo 莲花居中 + 错误码 + 停机落款。 * 与 login.elf / UTSM F1 panic 同一视觉体系。 */ static void dsk_fatal_lotus(u32 *fb, const char *sym) { @@ -647,14 +647,23 @@ static void dsk_fatal_lotus(u32 *fb, const char *sym) { u32 *line = (u32 *)((u8 *)fb + y * g_fb_p); for (u64 x = 0; x < g_fb_w; x++) line[x] = 0xFF020F22u; } - /* 莲花居中(偏上,给错误码留空间) */ - i64 lx = ((i64)g_fb_w - g_panic_logo_w) / 2; - i64 ly = ((i64)g_fb_h - g_panic_logo_h) / 2 - 60; - if (ly < 0) ly = 0; - for (i64 r = 0; r < g_panic_logo_h; r++) { - for (i64 c = 0; c < g_panic_logo_w; c++) { + /* 莲花按步进缩小,错误码折行钉在屏内(长串不再被左右裁掉) */ + i64 spacing = g_ascii_w; + i64 text_need = (g_ascii_h + 6) * 4; + i64 step = 1; + while (step < 8 && + (i64)g_panic_logo_h / step + text_need + 24 > (i64)g_fb_h) + step++; + i64 dw = (i64)g_panic_logo_w / step; + i64 dh = (i64)g_panic_logo_h / step; + i64 lx2 = ((i64)g_fb_w - dw) / 2; + i64 ly2 = ((i64)g_fb_h - dh - text_need) / 2; + if (ly2 < 4) ly2 = 4; + for (i64 r = 0; r < dh; r++) { + for (i64 c = 0; c < dw; c++) { const unsigned char *px4 = - g_panic_logo_rgba + ((u64)r * g_panic_logo_w + (u64)c) * 4; + g_panic_logo_rgba + (((u64)r * (u64)step) * (u64)g_panic_logo_w + + (u64)c * (u64)step) * 4; u32 a = px4[3]; if (a < 8) continue; u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2]; @@ -666,24 +675,48 @@ static void dsk_fatal_lotus(u32 *fb, const char *sym) { u32 bb = ((bg & 0xFF) * na + (col & 0xFF) * a) >> 8; col = 0xFF000000u | (rr << 16) | (gg << 8) | bb; } - i64 xx = lx + c, yy = ly + r; + i64 xx = lx2 + c, yy = ly2 + r; if (xx < 0 || (u64)xx >= g_fb_w || yy < 0 || (u64)yy >= g_fb_h) continue; *(u32 *)((u8 *)fb + (u64)yy * g_fb_p + (u64)xx * 4) = col; } } - /* 错误码 + 停机落款(莲花下方) */ - i64 ty = ly + g_panic_logo_h + 24; - i64 spacing = g_ascii_w + 1; - dsk_draw_string(fb, sym, - ((i64)g_fb_w - (i64)dsk_fat_strlen(sym) * spacing) / 2, ty, - 0xFFF2D8D8u, (int)spacing); - dsk_draw_string(fb, "DESHAB FS FATAL - SYSTEM HALTED", - ((i64)g_fb_w - 31 * spacing) / 2, ty + g_ascii_h + 16, - 0xFFB08890u, (int)spacing); + i64 ty = ly2 + dh + 12; + i64 y_limit = (i64)g_fb_h - 4; + if (ty + text_need > y_limit) ty = y_limit - text_need; + if (ty < 4) ty = 4; + i64 cols = (i64)g_fb_w / spacing - 2; + if (cols < 8) cols = 8; + if (cols > 120) cols = 120; + { + const char *s = sym ? sym : ""; + while (*s && ty + g_ascii_h <= y_limit) { + char line[121]; + i64 n = 0; + while (s[n] && n < cols) { line[n] = s[n]; n++; } + line[n] = 0; + s += n; + dsk_draw_string(fb, line, ((i64)g_fb_w - n * spacing) / 2, ty, + 0xFFF2D8D8u, (int)spacing); + ty += g_ascii_h + 6; + } + } + if (ty + g_ascii_h <= y_limit) { + dsk_draw_string(fb, "DESHAB FS FATAL - SYSTEM HALTED", + ((i64)g_fb_w - 31 * spacing) / 2, ty, + 0xFFB08890u, (int)spacing); + } } +/* DSK_BOOT_FLAG_PANIC_FALLBACK(FUCK [panic] mode=fallback):dsk_fatal 只留 + * 串口坐标后返回,调用点紧跟备用路径;未置位 = strict,整屏莲花后停机。 */ +static int g_panic_fallback; + static void dsk_fatal(const char *sym) { logl(sym); + if (g_panic_fallback) { + logl("[DSK:FALLBACK] fatal point hit, taking backup path"); + return; + } if (g_fb_addr) { dsk_fatal_lotus((u32 *)(u64)g_fb_addr, sym); } @@ -1411,7 +1444,7 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) { logh("[DSK] ELF: min_vaddr=", min_vaddr); logh("[DSK] ELF: isize=", isize); dsk_fatal("ELF-E01 RELA OUT OF BOUNDS"); - continue; + return -1; /* fallback:拒绝加载未重定位的镜像 */ } if (rela_off + rela_sz > min_vaddr + isize) { logl("[DSK] ELF: rela table extends beyond image, clamping"); @@ -1813,17 +1846,28 @@ static void dsk_persist_userconf(const dsk_boot_context *ctx) { logl("[DSK] writing USER.CONF"); int wrc = fat32_write_root_file(cn, conf, (u32)conf_sz); logh("[DSK] USER.CONF write rc=", (u64)(i64)wrc); - if (wrc != 0) dsk_fatal("FS-E02 USER.CONF WRITE FAILED"); + if (wrc != 0) { + dsk_fatal("FS-E02 USER.CONF WRITE FAILED"); + /* fallback:不翻 firstInit.txt,下次启动重跑向导 */ + return; + } /* 1b. Write-verify: 回读 USER.CONF 逐字节比对,确保 FAT32 写路径真的落盘 */ { u8 *vdata = 0; u32 vsize = 0; - if (fat32_read_root_file(cn, &vdata, &vsize) != 0) + if (fat32_read_root_file(cn, &vdata, &vsize) != 0) { dsk_fatal("FS-E04 USER.CONF VERIFY READ FAILED"); - if (vsize != (u32)conf_sz) + return; + } + if (vsize != (u32)conf_sz) { dsk_fatal("FS-E04 USER.CONF VERIFY SIZE MISMATCH"); + return; + } for (u32 i = 0; i < (u32)conf_sz; i++) { - if (vdata[i] != conf[i]) dsk_fatal("FS-E04 USER.CONF VERIFY DATA MISMATCH"); + if (vdata[i] != conf[i]) { + dsk_fatal("FS-E04 USER.CONF VERIFY DATA MISMATCH"); + return; + } } logl("[DSK] USER.CONF verify ok"); } @@ -1923,6 +1967,7 @@ static int dsk_run_login(const dsk_boot_context *ctx) { * FUCK config controls: show_logo, skip_login, mouse_init, netman, dev_mode. */ static void dsk_spinner_and_check(const void *boot_ctx_ptr) { const dsk_boot_context *ctx = (const dsk_boot_context *)boot_ctx_ptr; + u32 (*block_dev_count)(void) = 0; { u64 api_addr = ctx->dkm_kernel_api; logh("[DSK] kernel_api addr=", api_addr); if (!api_addr) { logl("[DSK] FATAL: kernel_api is NULL"); for(;;)__asm__("hlt"); } @@ -1931,17 +1976,20 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) { logh("[DSK] block_api addr=", (u64)(u64)blk); g_block_read = blk ? blk->read : 0; g_block_write = blk ? blk->write : 0; + block_dev_count = blk ? blk->device_count : 0; logh("[DSK] block_read fn=", (u64)g_block_read); logh("[DSK] block_write fn=", (u64)g_block_write); - fat32_probe_esp(); } - milestone_write(2, "block API ready"); - + } g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width; g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch; if (!g_fb_addr) { logl("[DSK] FATAL: framebuffer address is NULL"); for(;;)__asm__("hlt"); } - /* ext4 p2 数据分区挂载 + 启动读写自检(任何失败 fatal panic,FS-E2x/FS-E3x) */ + /* 先选中卷标 DESHAB 的盘,FAT 与 ext4 共用该设备号。 */ + if (block_dev_count) ext4_set_device_count(block_dev_count()); ext4_mount(g_block_read, g_block_write, dsk_fatal, logl, logh); + g_fat_dev = ext4_device_index(); + fat32_probe_esp(); + milestone_write(2, "block API ready"); ext4_boot_selftest(); u32 *sfb=(u32*)(u64)g_fb_addr; logh("[DSK] block_read ptr=", (u64)g_block_read); @@ -2161,23 +2209,24 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) { en[6]='~';en[7]='1';en[8]='E';en[9]='L';en[10]='F';en[11]=0; logl("[DSK] loading FirstInit"); if (dsk_load_and_run(en, ctx) != 0) { - /* 严格错误策略:首次启动却没有 FirstInit.elf = 未实现路径,零降级 */ + /* 严格错误策略:首次启动却没有 FirstInit.elf = 未实现路径,零降级。 + * fallback:跳过向导与登录,直接进桌面。 */ dsk_fatal("FS-E07 FIRSTINIT.ELF NOT FOUND"); - return; - } - logl("[DSK] FirstInit returned, persisting user.conf"); - dsk_persist_userconf(ctx); - logh("[DSK:STAT] fat32_read_count=", (u64)fat32_read_count); - logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count); - logh("[DSK:STAT] elf_load_count=", (u64)elf_load_count); - logh("[DSK:STAT] mod_schedule_count=", (u64)mod_schedule_count); + } else { + logl("[DSK] FirstInit returned, persisting user.conf"); + dsk_persist_userconf(ctx); + logh("[DSK:STAT] fat32_read_count=", (u64)fat32_read_count); + logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count); + logh("[DSK:STAT] elf_load_count=", (u64)elf_load_count); + logh("[DSK:STAT] mod_schedule_count=", (u64)mod_schedule_count); - /* 向导完成后强制重新登录:进桌面前必须再走一次 login.elf。 - * 零降级策略:登录被跳过/失败(如 Esc 跳过)不允许静默进桌面。 */ - if (skip_login) { - logl("[DSK:SCHED] first-boot login skipped by FUCK config"); - } else if (!dsk_run_login(ctx)) { - dsk_fatal("FS-E06 LOGIN REQUIRED AFTER FIRSTINIT"); + /* 向导完成后强制重新登录:进桌面前必须再走一次 login.elf。 + * 零降级策略:登录被跳过/失败(如 Esc 跳过)不允许静默进桌面。 */ + if (skip_login) { + logl("[DSK:SCHED] first-boot login skipped by FUCK config"); + } else if (!dsk_run_login(ctx)) { + dsk_fatal("FS-E06 LOGIN REQUIRED AFTER FIRSTINIT"); + } } logl("[DSK] loading desktop"); @@ -2192,6 +2241,8 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[DSK] boot"); if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");} logl("[DSK] context ok"); + g_panic_fallback = (ctx->flags & DSK_BOOT_FLAG_PANIC_FALLBACK) ? 1 : 0; + if (g_panic_fallback) logl("[DSK] panic mode=fallback"); dsk_spinner_and_check(ctx); logl("[DSK] SELFTEST PASS"); for(;;)__asm__("hlt"); diff --git a/CODE/firstInit/main.c b/CODE/firstInit/main.c index a24a4a2..880d3d9 100644 --- a/CODE/firstInit/main.c +++ b/CODE/firstInit/main.c @@ -189,31 +189,6 @@ static i64 fb_text_width(const char *s) { return n * ASCII_STEP; } -/* Alpha-blended ASCII text — same as fb_text but multiplied by a global alpha, - * used to fade English subtitles in/out alongside the Chinese bitmaps. */ -static void fb_text_alpha(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) { - for (i64 i=0; s[i]; i++) { - u32 ch = (u32)(u8)s[i]; - if (ch < ' ' || ch > '~') ch = ' '; - u32 idx = ch - ' '; - const u8 *g = g_ascii[idx]; - for (i64 r=0; r= fb_h) continue; - u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); - for (i64 c=0; c> 8; - if (eff_a == 0) continue; - i64 xx = x + i*ASCII_STEP + c; - if (xx < 0 || (u64)xx >= fb_w) continue; - line[(u64)xx] = (eff_a == 255) ? fg : blend(bg, fg, eff_a); - } - } - } -} - /* draw pre-rendered grayscale bitmap – writes only, no framebuffer read */ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) { for (i64 r=0; r (i64)fb_h) + step++; + i64 dw = (i64)g_panic_logo_w / step; + i64 dh = (i64)g_panic_logo_h / step; + i64 lx = ((i64)fb_w - dw) / 2; + i64 ly = ((i64)fb_h - dh - text_need) / 2; + if (ly < 4) ly = 4; + for (i64 r = 0; r < dh; r++) { + for (i64 c = 0; c < dw; c++) { const unsigned char *px4 = - g_panic_logo_rgba + ((u64)r * g_panic_logo_w + (u64)c) * 4; + g_panic_logo_rgba + (((u64)r * (u64)step) * (u64)g_panic_logo_w + + (u64)c * (u64)step) * 4; u32 a = px4[3]; if (a < 8) continue; u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2]; @@ -267,11 +254,28 @@ static void firstinit_fatal(const char *sym) { *(u32 *)((u8 *)fb + (u64)yy * fb_p + (u64)xx * 4) = col; } } - i64 ty = ly + g_panic_logo_h + 24; - fb_text(fb, sym, ((i64)fb_w - (i64)fi_fat_strlen(sym) * ASCII_STEP) / 2, - ty, 0xFFF2D8D8u, 0); - fb_text(fb, "DESHAB NET FATAL - SYSTEM HALTED", - ((i64)fb_w - 32 * ASCII_STEP) / 2, ty + ASCII_H + 16, 0xFFB08890u, 0); + i64 ty = ly + dh + 12; + i64 y_limit = (i64)fb_h - 4; + if (ty + text_need > y_limit) ty = y_limit - text_need; + if (ty < 4) ty = 4; + i64 cols = (i64)fb_w / ASCII_STEP - 2; + if (cols < 8) cols = 8; + if (cols > 120) cols = 120; + { + const char *s = sym ? sym : ""; + while (*s && ty + ASCII_H <= y_limit) { + char line[121]; + i64 n = 0; + while (s[n] && n < cols) { line[n] = s[n]; n++; } + line[n] = 0; + s += n; + fb_text(fb, line, ((i64)fb_w - n * ASCII_STEP) / 2, ty, 0xFFF2D8D8u, 0); + ty += ASCII_H + 6; + } + } + if (ty + ASCII_H <= y_limit) + fb_text(fb, "DESHAB NET FATAL - SYSTEM HALTED", + ((i64)fb_w - 32 * ASCII_STEP) / 2, ty, 0xFFB08890u, 0); } for (;;) __asm__("cli; hlt"); } @@ -296,23 +300,21 @@ static u32 bg_at_y(i64 y) { return blend(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (fb_h - 1))); } -static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { - for (i64 r=0; r= fb_h) continue; - u32 color = bg_at_y(yy); - u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); - for (i64 c=0; c= fb_w) continue; - line[(u64)xx] = color; - } - } -} - /* 模糊化的桌面壁纸(与 login 共用,构建期盒模糊 back.rgba) */ #include "../login/back_blur_data.c" +static u32 wallpaper_at(i64 x, i64 y) { + if (fb_w == 0 || fb_h == 0) return BG_MID; + if (x < 0) x = 0; + if (y < 0) y = 0; + if ((u64)x >= fb_w) x = (i64)fb_w - 1; + if ((u64)y >= fb_h) y = (i64)fb_h - 1; + u64 sy = (u64)y * WALLPAPER_BLUR_H / fb_h; + u64 sx = (u64)x * WALLPAPER_BLUR_W / fb_w; + const unsigned char *px = g_wall_blur_rgba + (sy * WALLPAPER_BLUR_W + sx) * 4; + return 0xFF000000u | ((u32)px[0] << 16) | ((u32)px[1] << 8) | px[2]; +} + static void draw_blur_wallpaper(u32 *fb) { for (i64 y = 0; y < (i64)fb_h; y++) { u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); @@ -326,6 +328,20 @@ static void draw_blur_wallpaper(u32 *fb) { } } +/* 卡片重绘不能再用深色渐变铺底,否则圆角外会留下一圈黑框。 */ +static void restore_wallpaper_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { + for (i64 r = 0; r < h; r++) { + i64 yy = y + r; + if (yy < 0 || (u64)yy >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c = 0; c < w; c++) { + i64 xx = x + c; + if (xx < 0 || (u64)xx >= fb_w) continue; + line[(u64)xx] = wallpaper_at(xx, yy); + } + } +} + static int rounded_rect_contains(i64 px, i64 py, i64 x, i64 y, i64 w, i64 h, i64 radius) { if (px < x || py < y || px >= x + w || py >= y + h) return 0; if (radius <= 0) return 1; @@ -383,11 +399,54 @@ static void delay_frame(void) { #include "text_bitmaps.c" +/* 开场文字画在壁纸上。透明字素跳过,抗锯齿边缘按该点壁纸颜色混合, + * 不再铺一块灰渐变当底。 */ +static void fb_bitmap_on_wall(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y, u32 fg, u32 global_alpha) { + for (i64 r = 0; r < h; r++) { + i64 yy = y + r; + if (yy < 0 || (u64)yy >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c = 0; c < w; c++) { + i64 xx = x + c; + if (xx < 0 || (u64)xx >= fb_w) continue; + u8 val = data[r * (u64)w + (u64)c]; + if (val == 0) continue; + u32 a = ((u32)val * global_alpha) >> 8; + if (a == 0) continue; + u32 bg = wallpaper_at(xx, yy); + line[(u64)xx] = (a >= 255) ? fg : blend(bg, fg, a); + } + } +} + +static void fb_text_on_wall(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 global_alpha) { + for (i64 i = 0; s[i]; i++) { + u32 ch = (u32)(u8)s[i]; + if (ch < ' ' || ch > '~') ch = ' '; + const u8 *g = g_ascii[ch - ' ']; + for (i64 r = 0; r < ASCII_H; r++) { + i64 yy = y + r; + if (yy < 0 || (u64)yy >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c = 0; c < ASCII_W; c++) { + u32 a = g[r * ASCII_W + c]; + if (a == 0) continue; + u32 eff = (a * global_alpha) >> 8; + if (eff == 0) continue; + i64 xx = x + i * ASCII_STEP + c; + if (xx < 0 || (u64)xx >= fb_w) continue; + u32 bg = wallpaper_at(xx, yy); + line[(u64)xx] = (eff >= 255) ? fg : blend(bg, fg, eff); + } + } + } +} + static void fade_ring(u32 *fb, i64 cx, i64 cy, u32 bg) { - /* Erase the spinner sprite area with gradient background in one frame. */ + /* 擦掉 DSK 加载环,露出壁纸,不再盖一块灰渐变。 */ (void)bg; i64 r = 72; - fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1); + restore_wallpaper_rect(fb, cx - r, cy - r, r * 2 + 1, r * 2 + 1); } static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) { @@ -535,7 +594,7 @@ static void redraw_setup_card(u32 *fb, i64 card_x, i64 card_y, char *pc, char *u u32 card = CARD_BG; u32 sub_fg = 0xFF9098A0; /* dimmer English subtitle color on card */ cursor_erase(fb); - fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + restore_wallpaper_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, card, 255); fb_text(fb, "Account Setup", card_x+40, card_y+40+g_txt_title_h+2, sub_fg, card); @@ -763,7 +822,7 @@ static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h, static void redraw_prefs_card(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, int active, i64 mx, i64 my, u32 bg, u32 fg) { u32 card = CARD_BG; cursor_erase(fb); - fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + restore_wallpaper_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_prefs_title, g_txt_prefs_title_w, g_txt_prefs_title_h, card_x+40, card_y+40, fg, card, 255); fb_text(fb, "Preferences", card_x+40, card_y+40+g_txt_prefs_title_h+2, 0xFF9098A0, card); @@ -1154,7 +1213,7 @@ static void draw_wifi_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int r static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int active, i64 mx, i64 my, u32 bg, u32 fg) { u32 card = CARD_BG; cursor_erase(fb); - fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + restore_wallpaper_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_network_title, g_txt_network_title_w, g_txt_network_title_h, card_x+40, card_y+40, fg, card, 255); fb_text(fb, "Network", card_x+40, card_y+40+g_txt_network_title_h+2, 0xFF9098A0, card); @@ -1273,8 +1332,9 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i if (*mx >= card_x + 680 && *mx < card_x + 860 && *my >= card_y + 310 && *my < card_y + 354) { /* 严格错误策略:无 wlan 驱动栈 —— 零降级 panic */ firstinit_fatal("NET-E01 WLAN CONNECT NOT IMPLEMENTED"); + /* fallback:不连接,按"保存配置"处理(connected=配置已存,非已联网) */ net->connected = 1; - logl("[FirstInit] network connect requested"); + logl("[FirstInit] network connect unavailable, profile saved"); redraw_network_card(fb, card_x, card_y, net, NET_ACT_CONNECT, *mx, *my, bg, fg); for (int i=0;i<30;i++) delay_frame(); return; @@ -1328,8 +1388,9 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i } else if (active == NET_ACT_CONNECT) { /* 严格错误策略:无 wlan 驱动栈,"连接"是未实现路径 —— 零降级 panic */ firstinit_fatal("NET-E01 WLAN CONNECT NOT IMPLEMENTED"); + /* fallback:不连接,按"保存配置"处理(connected=配置已存,非已联网) */ net->connected = 1; - logl("[FirstInit] network connect requested"); + logl("[FirstInit] network connect unavailable, profile saved"); redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); for (int i=0;i<30;i++) delay_frame(); return; @@ -1443,6 +1504,7 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[FirstInit] bad context"); for(;;) __asm__("hlt"); } + g_panic_fallback = (ctx->flags & DSK_BOOT_FLAG_PANIC_FALLBACK) ? 1 : 0; fb_a = ctx->framebuffer_address; fb_w = ctx->framebuffer_width; @@ -1479,9 +1541,9 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[FirstInit] fading in welcome"); for (u32 a=0; a<=255; a+=17) { - fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h + 6 + ASCII_H + 4); - fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a); - fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a); + restore_wallpaper_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h + 6 + ASCII_H + 4); + fb_bitmap_on_wall(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, a); + fb_text_on_wall(fb, welcome_en, en_x, en_y, sub_fg, a); delay_frame(); } logl("[FirstInit] welcome drawn"); @@ -1505,11 +1567,11 @@ void dsk_entry(const dsk_boot_context *ctx) { for (u32 step=0; step<=15; step++) { u32 a2 = step * 17; u32 a1 = 255 - a2; - fill_gradient_rect(fb, area_x-4, ty-4, area_w+8, area_h); - fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a1); - fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a1); - fb_bitmap_alpha(fb, g_txt_setup, tw2, g_txt_setup_h, tx2, ty, WELCOME_FG, bg_at_y(ty + g_txt_setup_h / 2), a2); - fb_text_alpha(fb, setup_en, en2_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a2); + restore_wallpaper_rect(fb, area_x-4, ty-4, area_w+8, area_h); + fb_bitmap_on_wall(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, a1); + fb_text_on_wall(fb, welcome_en, en_x, en_y, sub_fg, a1); + fb_bitmap_on_wall(fb, g_txt_setup, tw2, g_txt_setup_h, tx2, ty, WELCOME_FG, a2); + fb_text_on_wall(fb, setup_en, en2_x, en_y, sub_fg, a2); delay_frame(); } logl("[FirstInit] setup message drawn"); @@ -1673,17 +1735,17 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[FirstInit] config ready for DSK to write"); cursor_erase(fb); - fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); - fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 30, fg, bg_at_y(cy), 255); + restore_wallpaper_rect(fb, card_x - 24, card_y - 24, 948, 648); + fb_bitmap_on_wall(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 30, fg, 255); /* English subtitle below the Chinese done bitmap */ { const char *done_en = "Setup Complete"; i64 dw = fb_text_width(done_en); - fb_text(fb, done_en, cx - dw/2, cy + 2, 0xFFA8B8C8, bg_at_y(cy + 10)); + fb_text_on_wall(fb, done_en, cx - dw/2, cy + 2, 0xFFA8B8C8, 255); } /* status line below the subtitle */ { const char *status = "System starting..."; i64 sw = fb_text_width(status); - fb_text(fb, status, cx - sw/2, cy + 28, 0xFF808088, bg_at_y(cy + 36)); + fb_text_on_wall(fb, status, cx - sw/2, cy + 28, 0xFF808088, 255); } logl("[FirstInit] user setup finished"); diff --git a/CODE/linux/pack_system_image.sh b/CODE/linux/pack_system_image.sh index bf0ef8e..c47420a 100644 --- a/CODE/linux/pack_system_image.sh +++ b/CODE/linux/pack_system_image.sh @@ -62,7 +62,18 @@ efi_bytes="$(bytes_of "$SYSTEM/EFI")" limine_bytes="$(bytes_of "$SYSTEM/limine")" driver_bytes="$(bytes_of "$SYSTEM/driver")" sys_bytes="$(bytes_of "$SYSTEM/system")" -esp_content=$((boot_bytes + efi_bytes + limine_bytes + driver_bytes + sys_bytes)) +# M3: 桌面/开始菜单经 DSK FAT32 只读 ESP —— PE 应用目录与配置必须落 ESP。 +bin_bytes="$(bytes_of "$SYSTEM/bin")" +program_bytes="$(bytes_of "$SYSTEM/program")" +lib_bytes="$(bytes_of "$SYSTEM/lib")" +esp_cnf_bytes=0 +for cnf in PEAPPS.CNF LINUXAPP.CNF STARTMENU.CNF; do + if [[ -f "$SYSTEM/$cnf" ]]; then + esp_cnf_bytes=$((esp_cnf_bytes + $(bytes_of "$SYSTEM/$cnf"))) + fi +done +esp_content=$((boot_bytes + efi_bytes + limine_bytes + driver_bytes + sys_bytes \ + + bin_bytes + program_bytes + lib_bytes + esp_cnf_bytes)) rest_bytes=0 shopt -s dotglob nullglob @@ -257,6 +268,29 @@ if [[ -d "$SYSTEM/user" ]]; then echo "[pack] copying user/ onto FAT32 ESP for native desktop icons" rsync -rltD --info=progress2 "$SYSTEM/user/" "$MNT_ESP/user/" fi +# M3: PE 应用/系统工具与注册表配置 —— DSK FAT32 路径只看得见 ESP: +# bin/ CLI exe(cmd.elf PATH 二级)+ ELF 工具(开始菜单 bin/xxx.elf) +# program/ GUI exe(开始菜单 program/xxx.exe,run_windowed 嵌入) +# lib/ PE 真实 DLL(pe_dll_manager 经 fat32_read_path "lib/..." 加载) +# *.CNF 桌面注册表(PEAPPS/LINUXAPP/STARTMENU) +# 没有这几份拷贝,开始菜单/PE 应用在 GPT 镜像里全部静默失效。 +if [[ -d "$SYSTEM/bin" ]]; then + echo "[pack] copying bin/ onto FAT32 ESP (CLI exes + ELF tools)" + rsync -rltD --info=progress2 "$SYSTEM/bin/" "$MNT_ESP/bin/" +fi +if [[ -d "$SYSTEM/program" ]]; then + echo "[pack] copying program/ onto FAT32 ESP (GUI PE apps)" + rsync -rltD --info=progress2 "$SYSTEM/program/" "$MNT_ESP/program/" +fi +if [[ -d "$SYSTEM/lib" ]]; then + echo "[pack] copying lib/ onto FAT32 ESP (PE DLLs)" + rsync -rltD --info=progress2 "$SYSTEM/lib/" "$MNT_ESP/lib/" +fi +for cnf in PEAPPS.CNF LINUXAPP.CNF STARTMENU.CNF; do + if [[ -f "$SYSTEM/$cnf" ]]; then + cp -f "$SYSTEM/$cnf" "$MNT_ESP/$cnf" + fi +done echo "[pack] copying p2 ext4 data partition" shopt -s dotglob nullglob diff --git a/CODE/login/main.c b/CODE/login/main.c index 7fce029..2c26a8d 100644 --- a/CODE/login/main.c +++ b/CODE/login/main.c @@ -137,8 +137,15 @@ static u64 lf_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; } * 必须整屏 fatal 显示错误码后停机。"疯但好使" 的反面保障: * 错也错得有落款。每个插桩点一个唯一 symbol(LOGIN-E)。 * =================================================================== */ +/* DSK_BOOT_FLAG_PANIC_FALLBACK:login_fatal 只留串口坐标后返回,调用点走备用路径 */ +static int g_panic_fallback; + static void login_fatal(const char *sym) { logl(sym); + if (g_panic_fallback) { + logl("[login:FALLBACK] fatal point hit, taking backup path"); + return; + } if (fb_a) { u32 *fb = (u32 *)(u64)fb_a; fill_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h, 0xFF3A0E14u); @@ -228,6 +235,23 @@ static u32 bg_at_y(i64 y) { return blend(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (fb_h - 1))); } +static void restore_wallpaper_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { + for (i64 r = 0; r < h; r++) { + i64 yy = y + r; + if (yy < 0 || (u64)yy >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + u64 sy = (u64)yy * WALLPAPER_BLUR_H / fb_h; + const unsigned char *srow = g_wall_blur_rgba + sy * WALLPAPER_BLUR_W * 4; + for (i64 c = 0; c < w; c++) { + i64 xx = x + c; + if (xx < 0 || (u64)xx >= fb_w) continue; + u64 sx = (u64)xx * WALLPAPER_BLUR_W / fb_w; + const unsigned char *px = srow + sx * 4; + line[(u64)xx] = 0xFF000000u | ((u32)px[0] << 16) | ((u32)px[1] << 8) | px[2]; + } + } +} + static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { for (i64 r=0; rmagic == 0x44534B31424F4F54ULL && + (ctx->flags & DSK_BOOT_FLAG_PANIC_FALLBACK)) ? 1 : 0; - /* 防篡改:第一时间校验 .text 段完整性。失败 = 系统被篡改,严格 panic。 */ + /* 防篡改:第一时间校验 .text 段完整性。失败 = 系统被篡改,严格 panic。 + * fallback:继续登录流程(密码校验仍生效)。 */ if (!login_verify_integrity()) { login_fatal("LOGIN-E01-INTEGRITY-FAIL"); } @@ -819,6 +846,7 @@ void dsk_entry(const dsk_boot_context *ctx) { if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { login_fatal("LOGIN-E02-BADCONTEXT"); + return; /* fallback:无上下文无法登录,返回 DSK(视为未登录) */ } fb_a = ctx->framebuffer_address; @@ -833,6 +861,7 @@ void dsk_entry(const dsk_boot_context *ctx) { u32 conf_size = (u32)ctx->reserved[1]; if (!conf_buf || conf_size == 0) { login_fatal("LOGIN-E03-NOUSERCONF"); + return; /* fallback:reserved[2] 保持 0,DSK 按未登录处理 */ } logl("[login] conf data received"); @@ -852,12 +881,14 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[login] username found"); } else { login_fatal("LOGIN-E04-NO-USERNAME"); + return; } if (conf_get_value(conf_buf, conf_size, "passwordSha256", g_password_hash, sizeof(g_password_hash)) == 0) { g_has_hash = 1; logl("[login] password hash found"); } else { login_fatal("LOGIN-E05-NO-PASSWORD-HASH"); + return; } } else { logl("[login] conf appears encrypted, will verify by password decryption"); diff --git a/CODE/sdk/docs/SDK.md b/CODE/sdk/docs/SDK.md index 17f3d88..261b5d8 100644 --- a/CODE/sdk/docs/SDK.md +++ b/CODE/sdk/docs/SDK.md @@ -381,6 +381,47 @@ int driver_exit(dkm_driver_handle *handle) { 如需 bootloader 预加载(stage0/stage1),还需在 `SYSTEM/boot/limine.conf` 添加 `module_path`。 +### 4.6 服务总线(dkm_bus · D6 万物互联) + +模块间通信不再走 `dsk_boot_context.reserved[]` 硬偏移,统一经 `api->bus` 按名注册-消费。**driver_init 里注册服务是模块的立身之本**: + +```c +/* ---- 提供方:在 driver_init 里注册 ---- */ +static const mouse_state_t *mouse_poll_fn(void) { return &g_mouse_state; } + +int driver_init(const dkm_kernel_api *api, dkm_driver_handle *handle) { + /* ... 硬件初始化 ... */ + /* 注册服务:名字必须 srv. 前缀,version 是你的契约版本 */ + api->bus->register_service("srv.input.mouse.poll", 1, (void *)mouse_poll_fn); + return 0; +} + +/* ---- 消费方:lookup(硬依赖,缺席即 panic 莲花屏) ---- */ +void *fn; u32 ver; +api->bus->lookup("srv.input.mouse.poll", &fn, &ver); +const mouse_state_t *(*poll)(void) = (const mouse_state_t *(*)(void))fn; + +/* ---- 消费方:probe(可选能力,缺席返回 -1 自己处理) ---- */ +if (api->bus->probe("srv.fs.ext4.read", &fn, &ver) != 0) { + /* 无 ext4 服务——走设计内回退路径 */ +} +``` + +**命名空间**:`kern.*` 仅内核可注册(log/dma/irq/mm/apic/drr/block/pe/lxc/config/load_by_name 等 22 条起步);`srv.*` 仅模块可注册。 + +**失败策略(零降级铁律)**: + +| 操作 | 失败时 | 语义 | +|------|--------|------| +| `register_service` 名字非法/重名/表满 | BUS-E01/E02/E03 panic | 编程错误 | +| `lookup` 未命中 | BUS-E04 panic | 硬依赖未满足=编程错误 | +| `probe` 未命中 | 返回 -1 | 能力缺席=设计内双路径 | +| `unregister_service` 违规 | BUS-E05 panic | 生命周期违规 | + +**"运行"也是服务**:长期模块把主循环入口注册为 `srv..run`,由会话排序者(session.drv)按总线调用拉起。 + +完整设计见 [docs/RE/万物互联与服务总线设计.md](../../../docs/RE/万物互联与服务总线设计.md)。 + --- ## 5. 头文件索引 diff --git a/CODE/sdk/include/deshab/boot_context.h b/CODE/sdk/include/deshab/boot_context.h index 3bd13c1..8c9307a 100644 --- a/CODE/sdk/include/deshab/boot_context.h +++ b/CODE/sdk/include/deshab/boot_context.h @@ -21,6 +21,7 @@ #define DSK_BOOT_FLAG_DKM_READY (1ULL << 1) #define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2) #define DSK_BOOT_FLAG_PROBE_INFO (1ULL << 3) +#define DSK_BOOT_FLAG_PANIC_FALLBACK (1ULL << 4) /* FUCK [panic] mode=fallback */ /* ---- reserved 槽位索引(dsk_boot_context.reserved[]) ---- */ #define DSB_BOOT_RESERVED_PE_SERVICE 4u diff --git a/CODE/sdk/include/deshab/kernel_api.h b/CODE/sdk/include/deshab/kernel_api.h index 36a2505..83fec46 100644 --- a/CODE/sdk/include/deshab/kernel_api.h +++ b/CODE/sdk/include/deshab/kernel_api.h @@ -133,6 +133,71 @@ typedef struct dkm_mmio_api { u64 (*cr3)(void); /* 当前 CR3 物理地址(诊断用) */ } dkm_mmio_api; +/* ---- SAS-R0-PCQ 调度器服务表(M4/Phase7) ---- */ +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; + +/* ---- D3: 内核按名导出表(kapi,只读) ---- */ +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; + const dkm_kapi_entry *entries; + int (*lookup)(const char *name, void **out_fn); /* miss 返回 -1 */ +} dkm_kapi_table; + +#define DKM_KAPI_MAGIC 0x4b415049u /* "KAPI" */ +#define DKM_KAPI_VERSION 1u + +/* ---- D6 万物互联: 双向服务总线(dkm_bus) ---- + * kapi 是内核→模块单向只读;bus 双向:模块在 driver_init 里注册 + * srv.* 服务,任何模块按名消费。失败策略: + * lookup miss = BUS-E04 panic(硬依赖=编程错误) + * probe miss = 返回 -1(能力缺席=设计内双路径) + * 设计文档: docs/RE/万物互联与服务总线设计.md */ +struct dkm_driver_desc; + +#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 终止;kern.* / srv. 前缀 */ + u32 version; /* 服务契约版本 */ + u32 flags; /* DKM_BUS_F_* */ + void *fn; /* 服务入口,签名由服务契约定义 */ + const struct dkm_driver_desc *provider; /* NULL = 内核 */ +} dkm_bus_service; + +typedef struct dkm_bus_api { + u32 magic; + u32 version; + /* 注册:模块只能 srv.*(kern.* 仅内核);BUS-E01/E02/E03 全 panic,成功 0 */ + int (*register_service)(const char *name, u32 version, void *fn); + /* 注销:仅 provider 本人;违规 BUS-E05 panic */ + int (*unregister_service)(const char *name); + /* 必存在查找:miss → BUS-E04 panic */ + int (*lookup)(const char *name, void **out_fn, u32 *out_version); + /* 可选探测:miss 返回 -1(调用方显式处理缺席) */ + int (*probe)(const char *name, void **out_fn, u32 *out_version); + u32 (*count)(void); + const dkm_bus_service *(*at)(u32 index); +} dkm_bus_api; + /* ---- kernel_api 主结构 ---- * 字段顺序/类型不可调整 - UTSM 端按固定偏移填充。 * DSK 以 api+0xA8 硬偏移读取 block,net_stack 以 api+0x48 读取 net。 */ @@ -171,6 +236,12 @@ typedef struct dkm_kernel_api { /* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),池 0x40-0xDF。 */ int (*irq_vector_alloc)(void); void (*irq_vector_free)(int vector); + /* M4/Phase7-8: 调度器服务(task_create/uptime/sleep/yield)。 */ + const utsm_sched_api *sched; + /* D3: 内核按名导出表(只读)。 */ + const dkm_kapi_table *kapi; + /* D6 万物互联: 双向服务总线(kern.* 与 srv.* 按名注册-消费)。 */ + const dkm_bus_api *bus; } dkm_kernel_api; /* ---- kernel_api 字段偏移常量(消除散落的硬编码偏移) ---- diff --git a/CODE/tools/diskinfo/MAKEFILE b/CODE/tools/diskinfo/MAKEFILE new file mode 100644 index 0000000..bec9ae7 --- /dev/null +++ b/CODE/tools/diskinfo/MAKEFILE @@ -0,0 +1,25 @@ +CC ?= clang +LD ?= ld.lld + +ROOT := ../../.. +OUT := $(ROOT)/SYSTEM/bin/diskinfo.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../../sdk/include -I../.. -O2 +LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := main.c +OBJECTS := $(C_SOURCES:.c=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @mkdir -p "$(dir $(OUT))" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/tools/diskinfo/linker.ld b/CODE/tools/diskinfo/linker.ld new file mode 100644 index 0000000..ffdf62d --- /dev/null +++ b/CODE/tools/diskinfo/linker.ld @@ -0,0 +1,26 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); +} + +SECTIONS +{ + . = 0x1000; + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic + .bss : ALIGN(4K) { + *(.bss*) + } :data +} diff --git a/CODE/tools/diskinfo/main.c b/CODE/tools/diskinfo/main.c new file mode 100644 index 0000000..0bfe74b --- /dev/null +++ b/CODE/tools/diskinfo/main.c @@ -0,0 +1,253 @@ +/* diskinfo — Deshab 磁盘工具(diskpart 风格,ELF 全屏工具) + * + * 展示:block 设备清单、GPT 分区表(类型/起始/大小)、ESP FAT32 + * 容量统计(总簇/空闲簇/已用%)。只读,不写盘。 + * + * 构建: cd CODE/tools/diskinfo ; make + * 输出: SYSTEM/bin/diskinfo.elf(开始菜单 bin/xxx.elf 条目启动) + */ +#include "../../firstInit/ascii_bitmaps.c" +#include "../../sdk/include/deshab/deshab.h" +#include "../../sdk/include/deshab/font.h" +#include "../../tools/fat32_part.h" + +static dsb_app_context g_ac; + +/* ---- 简易格式化 ---- */ +static char *fmt_hex(u64 v, char *buf) { + static const char hx[] = "0123456789ABCDEF"; + int n = 0; + char tmp[18]; + if (!v) { buf[0] = '0'; buf[1] = 0; return buf; } + while (v && n < 16) { tmp[n++] = hx[v & 0xF]; v >>= 4; } + buf[0] = '0'; buf[1] = 'x'; + for (int i = 0; i < n; i++) buf[2 + i] = tmp[n - 1 - i]; + buf[2 + n] = 0; + return buf; +} +static char *fmt_dec(u64 v, char *buf) { + char tmp[24]; int n = 0; + if (!v) { buf[0] = '0'; buf[1] = 0; return buf; } + while (v && n < 23) { tmp[n++] = (char)('0' + v % 10); v /= 10; } + for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i]; + buf[n] = 0; + return buf; +} +/* 拼接:a " / " b */ +static char *fmt_pair(u64 a, u64 b, char *buf) { + char t1[24], t2[24]; + int i = 0; + const char *s = fmt_dec(a, t1); + while (s[i] && i < 40) { buf[i] = s[i]; i++; } + buf[i++] = ' '; buf[i++] = '/'; buf[i++] = ' '; + const char *s2 = fmt_dec(b, t2); + int j = 0; + while (s2[j] && i < 44) buf[i++] = s2[j++]; + buf[i] = 0; + return buf; +} + +#define INFO_X 12 +#define INFO_Y_START (DSB_TITLEBAR_H + 8) +#define LINE_H 20 +#define COL_LABEL_W 190 + +static int g_line = 0; + +static void info_line(const char *label, const char *value, u32 val_color) { + i64 y = INFO_Y_START + (i64)g_line * LINE_H; + dsb_draw_string(&g_ac, label, INFO_X, y, DSB_TEXT_DIM, DSB_BG_PRIMARY, 12); + dsb_draw_string(&g_ac, value, INFO_X + COL_LABEL_W, y, val_color, DSB_BG_PRIMARY, 12); + g_line++; +} +static void info_section(const char *title) { + i64 y = INFO_Y_START + (i64)g_line * LINE_H; + dsb_draw_string(&g_ac, title, INFO_X, y, DSB_ACCENT_LIGHT, DSB_BG_PRIMARY, 12); + dsb_fill_rect(&g_ac, INFO_X, y + 16, (i64)g_ac.fb_w - INFO_X * 2, 1, DSB_BORDER); + g_line += 2; +} + +/* ---- 静态读缓冲 ---- */ +static u8 g_sec[512]; /* MBR / GPT 头 / BPB */ +static u8 g_ptes[16384]; /* GPT 分区项(128 项 × 128B = 16KB) */ +static u8 g_fat[65536]; /* FAT 扫描块(128 扇区) */ + +static u64 rd64(const u8 *p) { + return (u64)p[0] | ((u64)p[1] << 8) | ((u64)p[2] << 16) | ((u64)p[3] << 24) + | ((u64)p[4] << 32) | ((u64)p[5] << 40) | ((u64)p[6] << 48) + | ((u64)p[7] << 56); +} +static u32 rd32(const u8 *p) { + return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24); +} + +/* block read 直通(fat32_part_probe 回调签名) */ +static int blk_read(u32 index, u64 lba, u32 count, void *buf) { + return g_ac.block.read ? g_ac.block.read(index, lba, count, buf) : -1; +} + +void dsk_entry(const dsk_boot_context *ctx) { + if (ctx->magic != DSK_BOOT_MAGIC) return; + dsb_app_init(&g_ac, ctx); + char buf[64]; + + dsb_fill_bg(&g_ac, DSB_BG_PRIMARY); + dsb_draw_titlebar(&g_ac, "DiskInfo - diskpart style", (i64)g_ac.fb_w); + dsb_draw_statusbar(&g_ac, "read-only tool, Esc to exit", + (i64)g_ac.fb_w, (i64)g_ac.fb_h); + + /* ---- Block 设备 ---- */ + const dkm_kernel_api *api = g_ac.api; + if (!api || !api->block || !api->block->device_count) { + info_section("== Block Devices =="); + info_line("block api", "unavailable", DSB_ERROR); + goto wait_esc; + } + { + u32 ndev = api->block->device_count(); + info_section("== Block Devices =="); + info_line("device_count", fmt_dec(ndev, buf), DSB_TEXT_PRIMARY); + for (u32 i = 0; i < ndev && i < 4; i++) { + char label[16]; + label[0] = 'd'; label[1] = 'e'; label[2] = 'v'; label[3] = '['; + label[4] = (char)('0' + i); label[5] = ']'; label[6] = 0; + const char *name = api->block->device_name + ? api->block->device_name(i) : "?"; + info_line(label, name ? name : "?", DSB_ACCENT_LIGHT); + if (api->block->sector_size) + info_line(" sector_size", fmt_dec(api->block->sector_size(i), buf), + DSB_TEXT_PRIMARY); + if (api->block->sector_count) + info_line(" sector_count", fmt_dec(api->block->sector_count(i), buf), + DSB_TEXT_PRIMARY); + } + } + + /* ---- GPT 分区表 ---- */ + info_section("== GPT Partitions (disk 0) =="); + if (blk_read(0, 1, 1, g_sec) != 0) { + info_line("read LBA1", "failed", DSB_ERROR); + goto wait_esc; + } + if (!(g_sec[0]=='E' && g_sec[1]=='F' && g_sec[2]=='I' && g_sec[3]==' ' && + g_sec[4]=='P' && g_sec[5]=='A' && g_sec[6]=='R' && g_sec[7]=='T')) { + info_line("signature", "not GPT (MBR raw?)", DSB_WARNING); + goto wait_esc; + } + { + u64 pte_lba = rd64(g_sec + 72); + u32 nent = rd32(g_sec + 80); + u32 esz = rd32(g_sec + 84); + if (esz == 0 || esz > 512) esz = 128; + if (nent > 128) nent = 128; + info_line("entry_lba", fmt_dec(pte_lba, buf), DSB_TEXT_PRIMARY); + info_line("entries", fmt_dec(nent, buf), DSB_TEXT_PRIMARY); + /* 读 128 项 × 128B = 32 扇区 */ + if (blk_read(0, pte_lba, 32, g_ptes) != 0) { + info_line("read PTEs", "failed", DSB_ERROR); + goto wait_esc; + } + int shown = 0; + for (u32 i = 0; i < nent; i++) { + const u8 *e = g_ptes + (u64)i * esz; + int used = 0; + for (int k = 0; k < 16; k++) if (e[k]) { used = 1; break; } + if (!used) continue; + u64 first = rd64(e + 32), last = rd64(e + 40); + int is_esp = 1; + for (int k = 0; k < 16; k++) + if (e[k] != fat32_part_esp_guid[k]) { is_esp = 0; break; } + char lb[24]; + lb[0] = 'p'; lb[1] = (char)('1' + i); + lb[2] = is_esp ? '*' : ' '; + lb[3] = 0; + /* 类型 GUID 前 4 字节 + 起始 LBA */ + char tv[20]; + static const char hx[] = "0123456789ABCDEF"; + tv[0] = 't'; tv[1] = 'y'; tv[2] = 'p'; tv[3] = 'e'; tv[4] = '='; + for (int k = 0; k < 4; k++) { + tv[5 + k * 2] = hx[e[k] >> 4]; + tv[6 + k * 2] = hx[e[k] & 0xF]; + } + tv[13] = 0; + info_line(lb, tv, is_esp ? DSB_ACCENT_LIGHT : DSB_TEXT_PRIMARY); + info_line(" first_lba", fmt_dec(first, buf), DSB_TEXT_DIM); + info_line(" sectors", fmt_dec(last - first + 1, buf), DSB_TEXT_DIM); + shown++; + if (shown >= 8) break; + } + if (!shown) info_line("(no partitions)", "", DSB_TEXT_DIM); + } + + /* ---- ESP FAT32 容量 ---- */ + info_section("== FAT32 Volume (ESP) =="); + { + u64 base = fat32_part_probe(blk_read, 0); + /* probe 语义:0 = superfloppy(BPB 在 LBA0)或失败;>0 = ESP LBA */ + if (blk_read(0, base, 1, g_sec) != 0) { + info_line("read BPB", "failed", DSB_ERROR); + goto wait_esc; + } + if (!fat32_part_bpb_ok(g_sec)) { + info_line("BPB", "invalid", DSB_ERROR); + goto wait_esc; + } + u32 spc = g_sec[13]; + u32 rsv = (u32)g_sec[14] | ((u32)g_sec[15] << 8); + u32 nfat = g_sec[16]; + u32 spf = rd32(g_sec + 36); /* BPB_FATSz32 */ + u32 rootc = rd32(g_sec + 44); /* BPB_RootClus */ + u64 tot = rd32(g_sec + 32); /* BPB_TotSec32 */ + u64 data_sec = rsv + (u64)nfat * spf; + u64 nclus = (tot > data_sec) ? (tot - data_sec) / spc : 0; + + { + char lb[16]; + for (int i = 0; i < 11; i++) lb[i] = (char)g_sec[71 + i]; + lb[11] = 0; + info_line("label", lb, DSB_ACCENT_LIGHT); + } + info_line("bytes/sector", "512", DSB_TEXT_PRIMARY); + info_line("sectors/cluster", fmt_dec(spc, buf), DSB_TEXT_PRIMARY); + info_line("FAT size(sec x n)", fmt_pair(spf, nfat, buf), DSB_TEXT_PRIMARY); + info_line("root_cluster", fmt_dec(rootc, buf), DSB_TEXT_PRIMARY); + info_line("total clusters", fmt_dec(nclus, buf), DSB_TEXT_PRIMARY); + + /* 空闲簇扫描:FAT 区按 64KB 块读,统计 (i>=2 && entry==0) */ + if (spf) { + u64 fat_lba = base + rsv; + u64 fat_ent = (u64)spf * 512 / 4; + if (fat_ent > nclus + 2) fat_ent = nclus + 2; + u64 free_c = 0; + u32 chunk_sec = sizeof(g_fat) / 512; /* 128 扇区/块 */ + u32 ents_per_chunk = chunk_sec * 128; /* FAT 项/块 */ + u64 chunks = (fat_ent + ents_per_chunk - 1) / ents_per_chunk; + for (u64 ch = 0; ch < chunks; ch++) { + if (blk_read(0, fat_lba + ch * chunk_sec, chunk_sec, g_fat) != 0) + break; + for (u32 k = 0; k < ents_per_chunk; k++) { + u64 idx = ch * ents_per_chunk + k; + if (idx < 2 || idx >= fat_ent) continue; + u32 v = rd32(g_fat + k * 4) & 0x0FFFFFFFu; + if (v == 0) free_c++; + } + } + info_line("free clusters", fmt_dec(free_c, buf), DSB_SUCCESS); + if (nclus) { + u64 used_pct = (nclus - free_c) * 100 / nclus; + info_line("used", fmt_pair(used_pct, 100, buf), DSB_WARNING); + } + } + } + +wait_esc: + /* 等待 Esc 退出 */ + for (;;) { + u8 st = dsb_inb(0x64); + if (st & 1) { + u8 sc = dsb_inb(0x60); + if (sc == 1) break; /* Esc make code */ + } + __asm__ volatile("pause"); + } +} diff --git a/CODE/tools/fat32_io.h b/CODE/tools/fat32_io.h index aa03360..3d3341f 100644 --- a/CODE/tools/fat32_io.h +++ b/CODE/tools/fat32_io.h @@ -102,6 +102,17 @@ static inline int f32_disk_load(void) { return 0; } +/* FAT 项。卷首 256 扇区缓存盖住时直接读;否则按扇区补读。 + * 读失败当成链结束,避免把缓存尾部垃圾当成下一簇。 */ +static inline u32 f32_fat_entry(u32 fo) { + if (fo + 4u <= 256u * 512u) + return f32_r32(f32_disk + fo) & 0x0FFFFFFFu; + u8 fat_sec[512]; + if (f32_read_sectors(fo / 512u, 1, fat_sec) != 0) + return 0x0FFFFFF8u; + return f32_r32(fat_sec + (fo % 512u)) & 0x0FFFFFFFu; +} + /* ---- 8.3 名字转换 ---- */ /* "file.txt" / "NAME" → 11 字符 8.3 名(大写,空格填充) @@ -688,9 +699,7 @@ static inline int f32_list_root(f32_list_cb emit, void *user_data) { f32_name_from_83(dir[e].name, disp); if (emit(disp, dir[e].fsize, dir[e].attr, user_data) != 0) goto done; } - u32 fo = fat_byte_off + clus * 4; - if (fo + 4 > sizeof(f32_disk)) break; - clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF; + clus = f32_fat_entry(fat_byte_off + clus * 4); } done: return 0; @@ -755,9 +764,7 @@ static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_ /* 短名项处理完毕,重置 LFN 缓冲等待下一条链 */ fat32_lfn_init(&lfn); } - u32 fo = fat_byte_off + clus * 4; - if (fo + 4 > sizeof(f32_disk)) break; - clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF; + clus = f32_fat_entry(fat_byte_off + clus * 4); } done: return 0; @@ -879,9 +886,7 @@ static inline int f32_find_in_dir_lfn(u32 dir_clus, const char *long_name, } fat32_lfn_init(&lfn); } - u32 fo = fat_byte_off + clus * 4; - if (fo + 4 > sizeof(f32_disk)) break; - clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF; + clus = f32_fat_entry(fat_byte_off + clus * 4); } not_found: return -1; diff --git a/CODE/tools/pe-tools/build.ps1 b/CODE/tools/pe-tools/build.ps1 new file mode 100644 index 0000000..0236b9f --- /dev/null +++ b/CODE/tools/pe-tools/build.ps1 @@ -0,0 +1,60 @@ +# build.ps1 — 编译 Deshab PE 系统工具(mingw-w64 g++) +# +# GUI 工具 → SYSTEM/program/*.exe(开始菜单 STARTMENU.CNF 启动,窗口嵌入桌面) +# CLI 工具 → SYSTEM/bin/*.exe(CMD 里敲名字运行,输出走串口) +# +# 编译模式与 CODE/tools/pe-samples/hello64.c 相同: +# -nostartfiles -Wl,--entry,main -Wl,--image-base,0x140000000 +# (无 CRT 启动;只准调 pe_shim 表内的 Win32 面) +# +# 用法: powershell -File build.ps1 [-Gxx ] + +param([string]$GxxPath = "") + +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path "$PSScriptRoot\..\..\..").Path +$ProgDir = Join-Path $Root "SYSTEM\program" +$BinDir = Join-Path $Root "SYSTEM\bin" + +# 定位 g++:参数 > PATH > msys2 默认位置 +$gxx = $null +if ($GxxPath -and (Test-Path $GxxPath)) { + $gxx = $GxxPath +} elseif (Get-Command g++ -ErrorAction SilentlyContinue) { + $gxx = (Get-Command g++).Source +} elseif (Test-Path "C:\msys64\mingw64\bin\g++.exe") { + $gxx = "C:\msys64\mingw64\bin\g++.exe" +} else { + throw "g++ not found (install msys2 mingw-w64 or pass -GxxPath)" +} +Write-Host "[pe-tools] g++ = $gxx" + +$Flags = @( + "-O2", "-Wall", "-std=c++17", + "-nostartfiles", + "-Wl,--entry,main", + "-Wl,--image-base,0x140000000" +) + +# GUI 工具(user32 消息循环 + gdi32 DIB/BitBlt) +$GuiTools = @("sysinfo", "clock", "paint", "notes") +# CLI 工具(putchar/puts/ExitProcess) +$CliTools = @("pciscan", "portio") + +foreach ($t in $GuiTools) { + $src = Join-Path $PSScriptRoot "$t.cpp" + $out = Join-Path $ProgDir "$t.exe" + & $gxx @Flags $src -o $out -lgdi32 -luser32 + if ($LASTEXITCODE -ne 0) { throw "build failed: $t" } + Write-Host "[pe-tools] GUI $t -> SYSTEM\program\$t.exe" +} + +foreach ($t in $CliTools) { + $src = Join-Path $PSScriptRoot "$t.cpp" + $out = Join-Path $BinDir "$t.exe" + & $gxx @Flags $src -o $out + if ($LASTEXITCODE -ne 0) { throw "build failed: $t" } + Write-Host "[pe-tools] CLI $t -> SYSTEM\bin\$t.exe" +} + +Write-Host "[pe-tools] done. (imports must stay within pe_shim table; verify with objdump -p)" diff --git a/CODE/tools/pe-tools/clock.cpp b/CODE/tools/pe-tools/clock.cpp new file mode 100644 index 0000000..ccf5c89 --- /dev/null +++ b/CODE/tools/pe-tools/clock.cpp @@ -0,0 +1,109 @@ +/* clock.cpp — Deshab 时钟(GUI PE64 工具) + * + * 模拟表盘(时/分/秒针)+ 数字时间/日期,RTC 实时刷新。 + * 渲染循环每帧自驱(无 WM_TIMER,SetTimer 在 shim 中是 no-op)。 + * Esc 关闭。 + */ +#include "pe_tool.h" + +static void draw_hand(PT_Canvas *cv, int cx, int cy, + double angle, int len, int width, u32 col) { + /* 角度 0 = 12 点方向,顺时针 */ + int ex = cx + (int)(len * angle_sin(angle)); + int ey = cy - (int)(len * angle_cos(angle)); + pt_line(cv, cx, cy, ex, ey, width, col); +} + +/* sin/cos:角度单位 = 圈(0..1)。表盘刻度只需粗精度, + * 用 0.1° 查表 + 线性插值避免引入浮点库调用。 */ +static const short S_SIN[91] = { /* sin(0..90°)*10000 */ + 0, 175, 349, 523, 698, 872, 1045, 1219, 1392, 1564, + 1736, 1908, 2079, 2250, 2419, 2588, 2756, 2924, 3090, 3256, + 3420, 3584, 3746, 3907, 4067, 4226, 4384, 4540, 4695, 4848, + 5000, 5150, 5299, 5446, 5592, 5736, 5878, 6018, 6157, 6293, + 6428, 6561, 6691, 6820, 6947, 7071, 7193, 7314, 7431, 7547, + 7660, 7771, 7880, 7986, 8090, 8192, 8290, 8387, 8480, 8572, + 8660, 8746, 8829, 8910, 8988, 9063, 9135, 9205, 9272, 9336, + 9397, 9455, 9511, 9563, 9613, 9659, 9703, 9744, 9781, 9816, + 9848, 9877, 9903, 9925, 9945, 9962, 9976, 9986, 9994, 9998, + 10000, +}; + +static double sin_deg(double deg) { + /* 折叠到 [0,90] 再查表插值;deg ∈ [0,360) */ + int neg = 0; + if (deg >= 180.0) { deg -= 180.0; neg = 1; } + if (deg >= 90.0) deg = 180.0 - deg; + int i = (int)deg; + if (i > 89) i = 89; /* deg==90 → [89]→[90] 插值 1.0 */ + double frac = deg - i; + double v = (S_SIN[i] + (S_SIN[i + 1] - S_SIN[i]) * frac) / 10000.0; + return neg ? -v : v; +} + +static double angle_sin(double turns) { + double d = turns * 360.0; + d -= (long)(d / 360.0) * 360.0; + if (d < 0) d += 360.0; + return sin_deg(d); +} + +static double angle_cos(double turns) { + return angle_sin(turns + 0.25); +} + +static void frame(PT_Canvas *cv) { + pt_fill(cv, 0, 0, cv->w, cv->h, PT_BG); + PT_Time t = pt_rtc_now(); + + /* 表盘卡 */ + int cx = cv->w / 2; + int cy = cv->h / 2 - 20; + int r = cv->h / 3; + if (r > cv->w / 3) r = cv->w / 3; + if (r > 200) r = 200; + if (r < 60) r = 60; + + pt_disc(cv, cx, cy, r + 14, PT_CARD); + pt_ring(cv, cx, cy, r + 14, 2, PT_STROKE); + pt_ring(cv, cx, cy, r, 2, PT_STROKE); + + /* 刻度:12 大格 */ + for (int i = 0; i < 12; i++) { + double a = i / 12.0; + int sx = cx + (int)((r - 14) * angle_sin(a)); + int sy = cy - (int)((r - 14) * angle_cos(a)); + int ex = cx + (int)((r - 4) * angle_sin(a)); + int ey = cy - (int)((r - 4) * angle_cos(a)); + pt_line(cv, sx, sy, ex, ey, i % 3 == 0 ? 4 : 2, PT_TEXT2); + } + + /* 指针 */ + double h_turns = (t.hour % 12) / 12.0 + t.min / 720.0; + double m_turns = t.min / 60.0 + t.sec / 3600.0; + double s_turns = t.sec / 60.0; + draw_hand(cv, cx, cy, h_turns, r / 2, 7, PT_TEXT); + draw_hand(cv, cx, cy, m_turns, r * 3 / 4, 5, PT_TEXT); + draw_hand(cv, cx, cy, s_turns, r - 18, 2, PT_ERROR); + pt_disc(cv, cx, cy, 6, PT_TEXT); + pt_disc(cv, cx, cy, 3, PT_CARD); + + /* 数字时间/日期 */ + char b[16]; + pt_fmt_time(t, b); + pt_text_center(cv, b, cx, cy + r + 26, PT_TEXT); + pt_fmt_date(t, b); + pt_text_center(cv, b, cx, cy + r + 50, PT_TEXT_DIM); + + pt_text(cv, "Esc = close", 16, cv->h - 26, PT_TEXT_DIM); +} + +int main(void) { + PT_Desc d; + d.w = 420; d.h = 560; + d.title = L"Clock"; + d.frame = frame; + d.key = 0; + d.mouse = 0; + return pt_run(&d); +} diff --git a/CODE/tools/pe-tools/notes.cpp b/CODE/tools/pe-tools/notes.cpp new file mode 100644 index 0000000..22d43dc --- /dev/null +++ b/CODE/tools/pe-tools/notes.cpp @@ -0,0 +1,117 @@ +/* notes.cpp — Deshab 记事本(GUI PE64 工具) + * + * 单缓冲文本编辑:WM_CHAR 键入、Backspace 删、Enter 换行、 + * 自动折行 + 尾部自动滚动;底部状态栏显示字符数。 + * 无文件 IO(shim CreateFileA → PE-E05),纯内存便签。 + * Esc 关闭。 + */ +#include "pe_tool.h" + +#define NOTE_CAP 4096 +#define LINE_H 20 +#define MARGIN 10 + +static char g_buf[NOTE_CAP + 1]; +static int g_len; +static int g_view_line; /* 顶部可见行 */ + +/* 折行游标走查:返回总行数,*end_line/*end_col 为缓冲末尾(光标)位置 */ +static int layout_walk(int cols, int *end_line, int *end_col) { + int line = 0, col = 0; + for (int i = 0; i < g_len; i++) { + if (g_buf[i] == '\n') { line++; col = 0; continue; } + col++; + if (col >= cols) { line++; col = 0; } + } + *end_line = line; + *end_col = col; + return line + 1; +} + +/* shim 键盘合成契约:可打印字符(>=0x20)走 WM_CHAR;Backspace/Enter/Esc + * 等控制键只走 WM_KEYDOWN(vk),WM_CHAR 不合成。 */ +static void key(unsigned vk, unsigned ch) { + if (ch) { + if (ch >= 32 && ch < 127 && g_len < NOTE_CAP) + g_buf[g_len++] = (char)ch; + return; + } + if (vk == 8) { /* Backspace */ + if (g_len > 0) g_len--; + return; + } + if (vk == 13) { /* Enter */ + if (g_len < NOTE_CAP) g_buf[g_len++] = '\n'; + return; + } +} + +static void frame(PT_Canvas *cv) { + pt_fill(cv, 0, 0, cv->w, cv->h, PT_BG); + + int cols = (cv->w - MARGIN * 2) / PT_STEP; + if (cols < 8) cols = 8; + int view_h = cv->h - LINE_H; /* 底部状态栏高度 LINE_H */ + int rows = view_h / LINE_H; + if (rows < 1) rows = 1; + + int cur_line, cur_col; + int total = layout_walk(cols, &cur_line, &cur_col); + (void)total; + + /* 自动滚动:光标保持可见 */ + if (cur_line >= g_view_line + rows) g_view_line = cur_line - rows + 1; + if (cur_line < g_view_line) g_view_line = cur_line; + if (g_view_line < 0) g_view_line = 0; + + /* 文本区卡片 */ + pt_round_fill(cv, MARGIN / 2, MARGIN / 2, cv->w - MARGIN, view_h - MARGIN, + PT_CARD, 8); + + /* 折行绘制(只画可见行窗口) */ + { + int line = 0, col = 0; + for (int i = 0; i < g_len; i++) { + char c = g_buf[i]; + if (c == '\n') { line++; col = 0; continue; } + if (line >= g_view_line && line < g_view_line + rows) + pt_char(cv, c, MARGIN + col * PT_STEP, + MARGIN + (line - g_view_line) * LINE_H, PT_TEXT); + col++; + if (col >= cols) { line++; col = 0; } + if (line >= g_view_line + rows && line > cur_line) break; + } + } + + /* 光标(RTC 秒闪烁) */ + { + PT_Time t = pt_rtc_now(); + if ((t.sec & 1) == 0 && cur_line >= g_view_line && + cur_line < g_view_line + rows) { + pt_fill(cv, MARGIN + cur_col * PT_STEP, + MARGIN + (cur_line - g_view_line) * LINE_H, + 2, LINE_H - 4, PT_ACCENT); + } + } + + /* 状态栏 */ + char b[24]; + int n = pt_dec((u32)g_len, b); + b[n++] = '/'; + n += pt_dec(NOTE_CAP, b + n); + b[n] = 0; + pt_fill(cv, 0, cv->h - LINE_H, cv->w, LINE_H, PT_BG); + pt_text(cv, b, MARGIN, cv->h - LINE_H + 2, PT_TEXT_DIM); + pt_text(cv, "Esc = close", cv->w - MARGIN - 11 * PT_STEP, + cv->h - LINE_H + 2, PT_TEXT_DIM); +} + +int main(void) { + PT_Desc d; + d.w = 560; d.h = 480; + d.title = L"Notes"; + d.frame = frame; + d.key = key; + d.mouse = 0; + return pt_run(&d); +} diff --git a/CODE/tools/pe-tools/paint.cpp b/CODE/tools/pe-tools/paint.cpp new file mode 100644 index 0000000..e47d8a0 --- /dev/null +++ b/CODE/tools/pe-tools/paint.cpp @@ -0,0 +1,136 @@ +/* paint.cpp — Deshab 画板(GUI PE64 工具) + * + * 顶栏:色板(10 色)+ 笔刷(2/6/12px)+ Clear。 + * 客户区:左键拖拽画线(独立持久层,UI 每帧叠加)。 + * 键盘:C = 清空;Esc 关闭。 + * 无文件 IO(shim CreateFileA → PE-E05),本工具为纯画布。 + */ +#include "pe_tool.h" + +#define TB_H 52 +#define SWATCH_N 10 +#define SWATCH_S 24 + +static const u32 g_swatches[SWATCH_N] = { + PTCOL(0x1A, 0x1A, 0x1A), PTCOL(0xFF, 0xFF, 0xFF), + PTCOL(0xC4, 0x2B, 0x1C), PTCOL(0xF7, 0x63, 0x16), + PTCOL(0xFF, 0xB9, 0x00), PTCOL(0x0F, 0x7B, 0x0F), + PTCOL(0x00, 0x67, 0xC0), PTCOL(0x8B, 0x5C, 0xF6), + PTCOL(0xE3, 0x00, 0x8B), PTCOL(0x8A, 0x8A, 0x8A), +}; + +static u32 *g_layer; /* 持久绘画层(画布区) */ +static int g_layer_w, g_layer_h; +static int g_cvw; /* 实际画布宽(窗口可能被 surface 钳位) */ +static int g_color = 6; /* 默认 Win11 蓝 */ +static int g_brush = 2; /* 笔刷索引:0=2px 1=6px 2=12px */ +static const int g_brush_px[3] = { 2, 6, 12 }; +static int g_drawing; +static int g_last_x, g_last_y; + +static void layer_alloc(int w, int h) { + g_layer_w = w; + g_layer_h = h; + for (long i = 0; i < (long)w * h; i++) g_layer[i] = PTCOL(0xFF, 0xFF, 0xFF); +} + +static void stroke_to(int x, int y) { + int px = g_brush_px[g_brush]; + /* 持久层画线(构造视图 canvas) */ + PT_Canvas cv; + cv.w = g_layer_w; cv.h = g_layer_h; cv.bits = g_layer; + pt_line(&cv, g_last_x, g_last_y, x, y, px, g_swatches[g_color]); + pt_disc(&cv, x, y, px / 2, g_swatches[g_color]); + g_last_x = x; g_last_y = y; +} + +static void mouse(int x, int y, int buttons, int edge) { + int cw = g_cvw; + + /* 顶栏命中 */ + if (edge & 1) { /* 左键按下 */ + if (y < TB_H) { + /* 色板 */ + for (int i = 0; i < SWATCH_N; i++) { + int sx = 10 + i * (SWATCH_S + 6); + if (x >= sx && x < sx + SWATCH_S && + y >= (TB_H - SWATCH_S) / 2 && y < (TB_H - SWATCH_S) / 2 + SWATCH_S) { + g_color = i; + return; + } + } + /* 笔刷 */ + for (int i = 0; i < 3; i++) { + int bx = 10 + SWATCH_N * (SWATCH_S + 6) + 12 + i * 40; + if (x >= bx && x < bx + 36 && y >= 8 && y < TB_H - 8) { + g_brush = i; + return; + } + } + /* Clear */ + int cx = cw - 80; + if (x >= cx && x < cx + 72 && y >= 12 && y < TB_H - 12) { + layer_alloc(g_layer_w, g_layer_h); + return; + } + return; + } + /* 画布起笔 */ + g_drawing = 1; + g_last_x = x; g_last_y = y - TB_H; + stroke_to(x, y - TB_H); + return; + } + if (edge & 2) g_drawing = 0; + if (buttons & 1 && g_drawing && y >= TB_H) + stroke_to(x, y - TB_H); +} + +static void key(unsigned vk, unsigned ch) { + if (ch == 'c' || ch == 'C') layer_alloc(g_layer_w, g_layer_h); +} + +static void frame(PT_Canvas *cv) { + g_cvw = cv->w; + /* 画布层 → DIB */ + for (int r = 0; r < g_layer_h && r + TB_H < cv->h; r++) + for (int q = 0; q < g_layer_w && q < cv->w; q++) + cv->bits[(long)(r + TB_H) * cv->w + q] = g_layer[(long)r * g_layer_w + q]; + + /* 顶栏 */ + pt_fill(cv, 0, 0, cv->w, TB_H, PT_BG); + pt_fill(cv, 0, TB_H - 1, cv->w, 1, PT_STROKE); + for (int i = 0; i < SWATCH_N; i++) { + int sx = 10 + i * (SWATCH_S + 6); + int sy = (TB_H - SWATCH_S) / 2; + pt_round_fill(cv, sx, sy, SWATCH_S, SWATCH_S, g_swatches[i], 6); + if (g_color == i) + pt_rect(cv, sx - 2, sy - 2, SWATCH_S + 4, SWATCH_S + 4, PT_ACCENT); + } + for (int i = 0; i < 3; i++) { + int bx = 10 + SWATCH_N * (SWATCH_S + 6) + 12 + i * 40; + int bc = g_brush == i ? PT_ACCENT : PT_TEXT2; + pt_disc(cv, bx + 18, TB_H / 2, g_brush_px[i] / 2 + 1, bc); + if (g_brush == i) + pt_rect(cv, bx, 8, 36, TB_H - 16, PT_ACCENT); + } + pt_round_fill(cv, cv->w - 80, 12, 72, TB_H - 24, PT_CARD, 4); + pt_rect(cv, cv->w - 80, 12, 72, TB_H - 24, PT_STROKE); + pt_text_center(cv, "Clear", cv->w - 44, TB_H / 2 - 9, PT_TEXT); + + pt_text(cv, "C = clear Esc = close", 10, cv->h - 24, PT_TEXT_DIM); +} + +int main(void) { + static u32 layer_mem[900 * 700]; /* 静态层:msvcrt malloc shim 可用,但静态最稳 */ + g_layer = layer_mem; + PT_Desc d; + d.w = 700; d.h = 560; + d.title = L"Paint"; + d.frame = frame; + d.key = key; + d.mouse = mouse; + /* 层尺寸按期望值预分配;pt_run 钳位后窗口只会更小(绘制有界) */ + layer_alloc(700, 560 - TB_H); + return pt_run(&d); +} diff --git a/CODE/tools/pe-tools/pciscan.cpp b/CODE/tools/pe-tools/pciscan.cpp new file mode 100644 index 0000000..124fd3f --- /dev/null +++ b/CODE/tools/pe-tools/pciscan.cpp @@ -0,0 +1,136 @@ +/* pciscan.cpp — Deshab PCI/CPU 扫描(控制台 PE64 工具) + * + * 在 CMD 里敲 "pciscan" 运行(cmd.elf PATH:FAT32 根 → bin/)。 + * 输出走串口(shim putchar/puts → COM1;QEMU -serial stdio 可见)。 + * 注意 shim printf 仅支持 3 个格式参数 —— 全部输出用自制助手拼行。 + * + * 内容:CPUID 厂商/型号串/核心数/hypervisor 位 + PCI 配置空间枚举 + * (bus 0-3,0xCF8/0xCFC 直读,Ring0 原生执行)+ RTC 日期时间。 + */ +#include + +typedef unsigned int u32; +typedef unsigned char u8; +typedef unsigned short u16; + +static inline void outp(u16 port, u32 v) { + __asm__ volatile("outl %0,%1" :: "a"(v), "Nd"(port)); +} +static inline u32 inp(u16 port) { + u32 v; + __asm__ volatile("inl %1,%0" : "=a"(v) : "Nd"(port)); + return v; +} +static inline u8 inpb(u16 port) { + u8 v; + __asm__ volatile("inb %1,%0" : "=a"(v) : "Nd"(port)); + return v; +} +static inline void outpb(u16 port, u8 v) { + __asm__ volatile("outb %0,%1" :: "a"(v), "Nd"(port)); +} + +/* ---- 输出助手(shim printf 参数受限,逐段 puts/putchar 拼行) ---- */ +static void ps(const char *s) { while (*s) putchar(*s++); } +static void pch(char c) { putchar(c); } +static void pd(u32 v) { + char b[12]; + int n = 0; + if (v == 0) { pch('0'); return; } + while (v > 0) { b[n++] = (char)('0' + v % 10); v /= 10; } + while (n > 0) pch(b[--n]); +} +static void ph(u32 v) { + static const char hd[] = "0123456789ABCDEF"; + ps("0x"); + for (int i = 7; i >= 0; i--) pch(hd[(v >> (i * 4)) & 0xF]); +} +static void ph4(u32 v) { + static const char hd[] = "0123456789ABCDEF"; + for (int i = 3; i >= 0; i--) pch(hd[(v >> (i * 4)) & 0xF]); +} + +static u32 pci_read(u16 bus, u16 dev, u16 fn, u8 off) { + outp(0xCF8, 0x80000000u | ((u32)bus << 16) | ((u32)dev << 11) + | ((u32)fn << 8) | off); + return inp(0xCFC); +} + +static void cpuid_(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) { + __asm__ volatile("cpuid" : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) + : "a"(leaf), "c"(0)); +} + +static int bcd(u8 v) { return (v >> 4) * 10 + (v & 0xF); } + +int main(void) { + /* ---- CPU ---- */ + u32 a, b, c, d; + cpuid_(0, &a, &b, &c, &d); + char vendor[13]; + ((u32 *)vendor)[0] = b; ((u32 *)vendor)[1] = d; ((u32 *)vendor)[2] = c; + vendor[12] = 0; + ps("Deshab pciscan\r\n"); + ps("CPU vendor: "); ps(vendor); ps("\r\n"); + ps("CPUID max leaf: "); ph(a); ps("\r\n"); + + char brand[49]; + brand[0] = 0; + if (a >= 0x80000004u) { + for (u32 leaf = 0x80000002u; leaf <= 0x80000004u; leaf++) { + cpuid_(leaf, &a, &b, &c, &d); + int off = (int)(leaf - 0x80000002u) * 16; + ((u32 *)brand)[off / 4 + 0] = a; + ((u32 *)brand)[off / 4 + 1] = b; + ((u32 *)brand)[off / 4 + 2] = c; + ((u32 *)brand)[off / 4 + 3] = d; + } + brand[48] = 0; + ps("CPU brand: "); ps(brand); ps("\r\n"); + } + + cpuid_(1, &a, &b, &c, &d); + ps("CPU cores (max APIC id): "); pd((b >> 16) & 0xFF); ps("\r\n"); + ps("Hypervisor: "); ps(((c >> 31) & 1) ? "yes" : "no"); ps("\r\n"); + + /* ---- PCI ---- */ + ps("\r\nPCI devices (bus 0-3):\r\n"); + int found = 0; + for (u16 bus = 0; bus < 4; bus++) { + for (u16 dev = 0; dev < 32; dev++) { + for (u16 fn = 0; fn < 8; fn++) { + u32 vd = pci_read(bus, dev, fn, 0); + u16 ven = vd & 0xFFFF, dev_id = vd >> 16; + if (ven == 0xFFFF) continue; + u32 cl = pci_read(bus, dev, fn, 8); + u32 ht = pci_read(bus, dev, fn, 12); + ps(" "); + pd(bus); pch(':'); pd(dev); pch('.'); pch((char)('0' + fn)); + ps(" "); + ph4(ven); pch(':'); ph4(dev_id); + ps(" class "); ph4((cl >> 16) & 0xFFFF); + ps(" rev "); ph4(cl & 0xFF); + pch('\r'); pch('\n'); + found++; + /* 非 multifunction 设备跳过 fn>0 */ + if (fn == 0 && !((ht >> 16) & 0x80)) break; + } + } + } + ps("Total PCI functions: "); pd((u32)found); ps("\r\n"); + + /* ---- RTC ---- */ + outpb(0x70, 0x09); int yy = bcd(inpb(0x71)); + outpb(0x70, 0x08); int mo = bcd(inpb(0x71)); + outpb(0x70, 0x07); int dd = bcd(inpb(0x71)); + outpb(0x70, 0x04); int hh = bcd(inpb(0x71)); + outpb(0x70, 0x02); int mi = bcd(inpb(0x71)); + outpb(0x70, 0x00); int ss = bcd(inpb(0x71)); + if (yy < 100) yy += 2000; + ps("RTC: "); pd((u32)yy); pch('-'); pd((u32)mo); pch('-'); pd((u32)dd); + pch(' '); pd((u32)hh); pch(':'); pd((u32)mi); pch(':'); pd((u32)ss); + ps("\r\n"); + ps("done.\r\n"); + ExitProcess(0); + return 0; +} diff --git a/CODE/tools/pe-tools/pe_tool.h b/CODE/tools/pe-tools/pe_tool.h new file mode 100644 index 0000000..0241076 --- /dev/null +++ b/CODE/tools/pe-tools/pe_tool.h @@ -0,0 +1,341 @@ +/* pe_tool.h — Deshab PE GUI 工具公共运行时 + * + * 目标环境:Deshab UTSM PE 兼容层(pe_shim 窗口模式 run_windowed)。 + * 编译:mingw-w64 g++ -nostartfiles -Wl,--entry,main -Wl,--image-base,0x140000000 + * + * 可用 Win32 面(pe_shim 表内子集): + * user32: RegisterClassExW/CreateWindowExW/PeekMessageW/TranslateMessage/ + * DispatchMessageW/PostQuitMessage/DefWindowProcW/GetSystemMetrics + * gdi32: CreateCompatibleDC/CreateDIBSection/SelectObject/BitBlt + * kernel32: ExitProcess/GetTickCount/QueryPerformanceCounter/Frequency + * + * 不可用(调了即未实现 stub / panic): + * TextOut*/SetPixel 等 GDI 绘制(自带 11x18 位图字体 + DIB 直写像素) + * 文件 IO(CreateFileA → PE-E05 panic) + * WM_TIMER(SetTimer 为 no-op;渲染循环每帧自驱) + * + * PE32+ 在 Ring0 原生执行 → 端口 I/O(CMOS RTC)直接可用。 + */ +#ifndef DESHAB_PE_TOOL_H +#define DESHAB_PE_TOOL_H + +#define WIN32_LEAN_AND_MEAN +#include + +typedef unsigned int u32; +typedef unsigned char u8; +typedef unsigned short u16; + +/* ---- 端口 I/O(Ring0 原生) ---- */ +static inline void pt_outb(u16 port, u8 v) { + __asm__ volatile("outb %0,%1" :: "a"(v), "Nd"(port)); +} +static inline u8 pt_inb(u16 port) { + u8 v; + __asm__ volatile("inb %1,%0" : "=a"(v) : "Nd"(port)); + return v; +} + +/* ---- 11x18 ASCII 位图字体(与 firstInit/desktop 同表) ---- */ +#include "../../firstInit/ascii_bitmaps.c" + +/* ---- 画布(32bpp top-down DIB) ---- */ +struct PT_Canvas { + int w, h; + u32 *bits; +}; + +#define PTCOL(r, g, b) (0xFF000000u | ((u32)(r) << 16) | ((u32)(g) << 8) | (u32)(b)) + +/* Win11 Light 主题令牌(与 fluent_ui.h g_wf_light 对齐) */ +#define PT_BG PTCOL(0xF3, 0xF3, 0xF3) /* 页面底 */ +#define PT_CARD PTCOL(0xFF, 0xFF, 0xFF) /* 卡片底 */ +#define PT_STROKE PTCOL(0xD6, 0xD6, 0xD6) /* 控件描边 */ +#define PT_TEXT PTCOL(0x1A, 0x1A, 0x1A) /* 主文本 */ +#define PT_TEXT2 PTCOL(0x5D, 0x5D, 0x5D) /* 次文本 */ +#define PT_TEXT_DIM PTCOL(0x8A, 0x8A, 0x8A) /* 弱文本 */ +#define PT_ACCENT PTCOL(0x00, 0x67, 0xC0) /* Win11 蓝 */ +#define PT_ACCENT_LT PTCOL(0x00, 0x78, 0xD4) +#define PT_ERROR PTCOL(0xC4, 0x2B, 0x1C) +#define PT_SUCCESS PTCOL(0x0F, 0x7B, 0x0F) + +static inline void pt_px(PT_Canvas *c, int x, int y, u32 col) { + if (x < 0 || x >= c->w || y < 0 || y >= c->h) return; + c->bits[(long)y * c->w + x] = col; +} + +static inline void pt_fill(PT_Canvas *c, int x, int y, int w, int h, u32 col) { + for (int r = 0; r < h; r++) + for (int q = 0; q < w; q++) + pt_px(c, x + q, y + r, col); +} + +static inline void pt_rect(PT_Canvas *c, int x, int y, int w, int h, u32 col) { + for (int q = 0; q < w; q++) { pt_px(c, x + q, y, col); pt_px(c, x + q, y + h - 1, col); } + for (int r = 0; r < h; r++) { pt_px(c, x, y + r, col); pt_px(c, x + w - 1, y + r, col); } +} + +static inline void pt_round_fill(PT_Canvas *c, int x, int y, int w, int h, + u32 col, int rad) { + if (rad > w / 2) rad = w / 2; + if (rad > h / 2) rad = h / 2; + for (int r = 0; r < h; r++) + for (int q = 0; q < w; q++) { + int dx = 0, dy = 0, corner = 0; + if (q < rad && r < rad) { dx = rad - 1 - q; dy = rad - 1 - r; corner = 1; } + else if (q >= w - rad && r < rad) { dx = q - (w - rad); dy = rad - 1 - r; corner = 1; } + else if (q < rad && r >= h - rad) { dx = rad - 1 - q; dy = r - (h - rad); corner = 1; } + else if (q >= w - rad && r >= h - rad){ dx = q - (w - rad); dy = r - (h - rad); corner = 1; } + if (corner && dx * dx + dy * dy > rad * rad) continue; + pt_px(c, x + q, y + r, col); + } +} + +/* Bresenham 直线(带厚度) */ +static inline void pt_line(PT_Canvas *c, int x0, int y0, int x1, int y1, + int w, u32 col) { + int dx = x1 > x0 ? x1 - x0 : x0 - x1; + int dy = y1 > y0 ? y1 - y0 : y0 - y1; + int sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1; + int err = dx - dy, half = w / 2; + for (;;) { + pt_fill(c, x0 - half, y0 - half, w, w, col); + if (x0 == x1 && y0 == y1) break; + int e2 = 2 * err; + if (e2 > -dy) { err -= dy; x0 += sx; } + if (e2 < dx) { err += dx; y0 += sy; } + } +} + +/* 圆盘 / 圆环 */ +static inline void pt_disc(PT_Canvas *c, int cx, int cy, int r, u32 col) { + for (int dy = -r; dy <= r; dy++) + for (int dx = -r; dx <= r; dx++) + if (dx * dx + dy * dy <= r * r) + pt_px(c, cx + dx, cy + dy, col); +} +static inline void pt_ring(PT_Canvas *c, int cx, int cy, int r, int s, u32 col) { + for (int dy = -r; dy <= r; dy++) + for (int dx = -r; dx <= r; dx++) { + int d2 = dx * dx + dy * dy; + if (d2 <= r * r && d2 > (r - s) * (r - s)) + pt_px(c, cx + dx, cy + dy, col); + } +} + +/* ---- 文本(11x18 位图,透明底) ---- */ +#define PT_FONT_W 11 +#define PT_FONT_H 18 +#define PT_STEP 10 /* 字距(与 DU_ASCII_STEP 一致) */ + +static inline int pt_strw(const char *s) { + int n = 0; + while (s[n]) n++; + return n * PT_STEP; +} + +static inline void pt_char(PT_Canvas *c, char ch, int x, int y, u32 fg) { + u32 idx = (u32)(ch - ' '); + if (idx > 94) idx = 0; + const u8 *glyph = g_ascii[idx]; + for (int r = 0; r < PT_FONT_H; r++) + for (int q = 0; q < PT_FONT_W; q++) { + u8 a = glyph[r * PT_FONT_W + q]; + if (a) pt_px(c, x + q, y + r, fg); + } +} + +static inline void pt_text(PT_Canvas *c, const char *s, int x, int y, u32 fg) { + int cx = x; + while (*s) { pt_char(c, *s, cx, y, fg); cx += PT_STEP; s++; } +} + +static inline void pt_text_center(PT_Canvas *c, const char *s, int cx, int y, u32 fg) { + pt_text(c, s, cx - pt_strw(s) / 2, y, fg); +} + +/* 十进制格式化(无 CRT) */ +static inline int pt_dec(u32 v, char *out) { + char tmp[10]; + int n = 0; + if (v == 0) { out[0] = '0'; out[1] = 0; return 1; } + while (v > 0) { tmp[n++] = (char)('0' + v % 10); v /= 10; } + for (int i = 0; i < n; i++) out[i] = tmp[n - 1 - i]; + out[n] = 0; + return n; +} +static inline int pt_hex32(u32 v, char *out) { + static const char hd[] = "0123456789ABCDEF"; + out[0] = '0'; out[1] = 'x'; + for (int i = 0; i < 8; i++) + out[2 + i] = hd[(v >> (28 - i * 4)) & 0xF]; + out[10] = 0; + return 10; +} + +/* ---- CMOS RTC(Ring0 端口直读,BCD) ---- */ +static inline u8 pt_rtc_reg(int reg) { + pt_outb(0x70, (u8)reg); + return pt_inb(0x71); +} +static inline int pt_rtc_bcd(u8 v) { return (v >> 4) * 10 + (v & 0xF); } + +struct PT_Time { + int year, month, day, hour, min, sec; +}; +static inline PT_Time pt_rtc_now(void) { + PT_Time t; + t.year = pt_rtc_bcd(pt_rtc_reg(0x09)) + (pt_rtc_bcd(pt_rtc_reg(0x32)) * 100); + t.month = pt_rtc_bcd(pt_rtc_reg(0x08)); + t.day = pt_rtc_bcd(pt_rtc_reg(0x07)); + t.hour = pt_rtc_bcd(pt_rtc_reg(0x04)); + t.min = pt_rtc_bcd(pt_rtc_reg(0x02)); + t.sec = pt_rtc_bcd(pt_rtc_reg(0x00)); + if (t.year < 2000) t.year += 2000; /* 无世纪寄存器的 RTC */ + return t; +} +static inline void pt_fmt_time(PT_Time t, char *out) { + out[0] = (char)('0' + t.hour / 10); out[1] = (char)('0' + t.hour % 10); + out[2] = ':'; + out[3] = (char)('0' + t.min / 10); out[4] = (char)('0' + t.min % 10); + out[5] = ':'; + out[6] = (char)('0' + t.sec / 10); out[7] = (char)('0' + t.sec % 10); + out[8] = 0; +} +static inline void pt_fmt_date(PT_Time t, char *out) { + out[0] = (char)('0' + t.year / 1000 % 10); + out[1] = (char)('0' + t.year / 100 % 10); + out[2] = (char)('0' + t.year / 10 % 10); + out[3] = (char)('0' + t.year % 10); + out[4] = '-'; + out[5] = (char)('0' + t.month / 10); out[6] = (char)('0' + t.month % 10); + out[7] = '-'; + out[8] = (char)('0' + t.day / 10); out[9] = (char)('0' + t.day % 10); + out[10] = 0; +} + +/* ---- CPUID ---- */ +static inline void pt_cpuid(u32 leaf, u32 sub, + u32 *a, u32 *b, u32 *c, u32 *d) { + __asm__ volatile("cpuid" + : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) + : "a"(leaf), "c"(sub)); +} + +/* ============================================================ + * 事件循环运行时 + * ============================================================ */ + +struct PT_Desc { + int w, h; /* 期望窗口尺寸(自动钳位到 surface) */ + const wchar_t *title; + void (*frame)(PT_Canvas *cv); /* 每帧绘制(DIB 直写) */ + void (*key)(unsigned vk, unsigned ch); /* ch!=0 → 可打印字符 */ + void (*mouse)(int x, int y, int buttons, int edge); + /* edge: bit0=L down bit1=L up bit2=R down bit3=R up */ +}; + +static PT_Desc pt_g_desc; +static PT_Canvas pt_g_cv; +static int pt_g_buttons; +static int pt_g_last_x, pt_g_last_y; + +static LRESULT CALLBACK pt_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { + switch (msg) { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: { + int x = (int)(short)LOWORD(lp), y = (int)(short)HIWORD(lp); + int btn = 0, edge = 0; + if (msg == WM_MOUSEMOVE) btn = (wp & 1) ? 1 : 0; + else if (msg == WM_LBUTTONDOWN) { btn = 1; edge = 1; } + else if (msg == WM_LBUTTONUP) { btn = 0; edge = 2; } + else if (msg == WM_RBUTTONDOWN) { btn = 2; edge = 4; } + else { btn = 0; edge = 8; } + pt_g_buttons = btn; + if (pt_g_desc.mouse) pt_g_desc.mouse(x, y, btn, edge); + pt_g_last_x = x; pt_g_last_y = y; + return 0; + } + case WM_KEYDOWN: + if (pt_g_desc.key) pt_g_desc.key((unsigned)wp, 0); + return 0; + case WM_CHAR: + if (pt_g_desc.key) pt_g_desc.key(0, (unsigned)wp); + return 0; + case WM_CLOSE: + DestroyWindow(hwnd); + return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProcW(hwnd, msg, wp, lp); +} + +/* 渲染循环:PeekMessage 泵(空转时 shim 让出给 desktop 宿主帧)+ 每帧绘制 */ +int pt_run(const PT_Desc *desc) { + pt_g_desc = *desc; + + int sw = GetSystemMetrics(0); /* 窗口模式 = 宿主 surface 尺寸 */ + int sh = GetSystemMetrics(1); + if (sw > 0 && desc->w > sw) pt_g_desc.w = sw; + if (sh > 0 && desc->h > sh) pt_g_desc.h = sh; + + WNDCLASSEXW wc; + for (unsigned i = 0; i < sizeof(wc); i++) ((u8 *)&wc)[i] = 0; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = pt_wndproc; + wc.hInstance = 0; + wc.lpszClassName = L"DESHABPT"; + RegisterClassExW(&wc); + + HWND hwnd = CreateWindowExW(0, L"DESHABPT", pt_g_desc.title, 0, + CW_USEDEFAULT, CW_USEDEFAULT, + pt_g_desc.w, pt_g_desc.h, + 0, 0, 0, 0); + if (!hwnd) return 1; + ShowWindow(hwnd, SW_SHOW); + + RECT rc; + rc.right = 0; rc.bottom = 0; + GetClientRect(hwnd, &rc); + pt_g_cv.w = rc.right ? rc.right : pt_g_desc.w; + pt_g_cv.h = rc.bottom ? rc.bottom : pt_g_desc.h; + + /* DIB backbuffer(top-down 32bpp) */ + BITMAPINFO bmi; + for (unsigned i = 0; i < sizeof(bmi); i++) ((u8 *)&bmi)[i] = 0; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = pt_g_cv.w; + bmi.bmiHeader.biHeight = -pt_g_cv.h; /* top-down */ + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + HDC wnd_dc = GetDC(hwnd); + HDC mem_dc = CreateCompatibleDC(wnd_dc); + void *bits = 0; + HBITMAP dib = CreateDIBSection(wnd_dc, &bmi, DIB_RGB_COLORS, &bits, 0, 0); + if (!dib || !bits) return 2; + SelectObject(mem_dc, dib); + pt_g_cv.bits = (u32 *)bits; + + MSG msg; + for (;;) { + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + ExitProcess((unsigned)msg.wParam); + } + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + if (pt_g_desc.frame) pt_g_desc.frame(&pt_g_cv); + BitBlt(wnd_dc, 0, 0, pt_g_cv.w, pt_g_cv.h, mem_dc, 0, 0, SRCCOPY); + } + /* not reached */ +} + +#endif /* DESHAB_PE_TOOL_H */ diff --git a/CODE/tools/pe-tools/portio.cpp b/CODE/tools/pe-tools/portio.cpp new file mode 100644 index 0000000..3200ee6 --- /dev/null +++ b/CODE/tools/pe-tools/portio.cpp @@ -0,0 +1,121 @@ +/* portio.cpp — Deshab 端口读写工具(控制台 PE64,diskpart 风格硬件调试) + * + * 用法(在 CMD 里运行,输出走串口): + * portio 读一字节(十六进制端口) + * portio 写一字节 + * portio 用法帮助 + * + * 例: + * portio 70 → 读 CMOS 地址口 + * portio 70 0 → CMOS 寄存器 0(秒) + * portio 71 → 读出秒(BCD) + * + * Ring0 原生执行,IOPL 无限制。危险提示:写错端口可能挂机。 + */ +#include + +typedef unsigned int u32; +typedef unsigned char u8; +typedef unsigned short u16; + +static inline u8 inpb(u16 port) { + u8 v; + __asm__ volatile("inb %1,%0" : "=a"(v) : "Nd"(port)); + return v; +} +static inline void outpb(u16 port, u8 v) { + __asm__ volatile("outb %0,%1" :: "a"(v), "Nd"(port)); +} + +static void ps(const char *s) { while (*s) putchar(*s++); } +static void pch(char c) { putchar(c); } +static void ph(u32 v) { + static const char hd[] = "0123456789ABCDEF"; + ps("0x"); + for (int i = 3; i >= 0; i--) pch(hd[(v >> (i * 4)) & 0xF]); +} + +/* 解析十进制/0x 前缀十六进制;成功返回 0 */ +static int parse_num(const char *s, u32 *out) { + u32 v = 0; + if (!s || !s[0]) return -1; + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; + if (!s[0]) return -1; + for (;;) { + char c = *s++; + if (!c) break; + int d; + if (c >= '0' && c <= '9') d = c - '0'; + else if (c >= 'a' && c <= 'f') d = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') d = c - 'A' + 10; + else return -1; + v = v * 16 + (u32)d; + } + } else { + for (;;) { + char c = *s++; + if (!c) break; + if (c < '0' || c > '9') return -1; + v = v * 10 + (u32)(c - '0'); + } + } + *out = v; + return 0; +} + +/* 从 cmdline 提取第 n 个空格分隔 token(0 = 程序名) */ +static int token(const char *cmd, int n, char *out, int cap) { + int idx = 0, ti = 0; + const char *p = cmd; + while (*p) { + while (*p == ' ') p++; + if (!*p) break; + ti = 0; + while (*p && *p != ' ') { + if (idx == n && ti < cap - 1) out[ti++] = *p; + p++; + } + if (idx == n) { + out[ti] = 0; + return ti > 0 ? 0 : -1; + } + idx++; + } + return -1; +} + +int main(void) { + const char *cmd = GetCommandLineA(); + char t1[32], t2[32]; + u32 port, val; + + if (token(cmd, 1, t1, sizeof(t1)) != 0) { + ps("Deshab portio - port read/write tool (serial console)\r\n"); + ps("usage: portio [value] (hex with 0x, or decimal)\r\n"); + ps(" portio 70 read CMOS address port\r\n"); + ps(" portio 70 0 select CMOS reg 0 (seconds)\r\n"); + ps(" portio 71 read seconds (BCD)\r\n"); + ps("WARNING: wrong port writes can hang the machine.\r\n"); + ExitProcess(0); + } + if (parse_num(t1, &port) != 0 || port > 0xFFFF) { + ps("portio: bad port\r\n"); + ExitProcess(1); + } + + if (token(cmd, 2, t2, sizeof(t2)) == 0) { + /* 写 */ + if (parse_num(t2, &val) != 0 || val > 0xFF) { + ps("portio: bad value\r\n"); + ExitProcess(1); + } + outpb((u16)port, (u8)val); + ps("portio: wrote "); ph(val); ps(" -> port "); ph(port); ps("\r\n"); + } else { + u8 v = inpb((u16)port); + ps("portio: port "); ph(port); ps(" = "); ph(v); ps("\r\n"); + } + ExitProcess(0); + return 0; +} diff --git a/CODE/tools/pe-tools/sysinfo.cpp b/CODE/tools/pe-tools/sysinfo.cpp new file mode 100644 index 0000000..00affdd --- /dev/null +++ b/CODE/tools/pe-tools/sysinfo.cpp @@ -0,0 +1,128 @@ +/* sysinfo.cpp — Deshab 系统信息(GUI PE64 工具) + * + * 显示:CPUID 厂商/型号串/核心数、TSC 频率、RTC 实时时钟、 + * 开机时长(TSC)、宿主 surface 分辨率、PE 兼容层标识。 + * Esc 关闭(shim 合成 WM_CLOSE)。 + */ +#include "pe_tool.h" + +static char g_vendor[16]; +static char g_brand[52]; +static u32 g_cores; +static int g_hv; +static u64 g_tsc_hz; + +static void probe(void) { + /* vendor */ + u32 a, b, c, d; + pt_cpuid(0, 0, &a, &b, &c, &d); + ((u32 *)g_vendor)[0] = b; + ((u32 *)g_vendor)[1] = d; + ((u32 *)g_vendor)[2] = c; + g_vendor[12] = 0; + + /* brand string */ + if (a >= 0x80000004u) { + for (u32 leaf = 0x80000002u; leaf <= 0x80000004u; leaf++) { + pt_cpuid(leaf, 0, &a, &b, &c, &d); + int off = (int)(leaf - 0x80000002u) * 16; + ((u32 *)g_brand)[off / 4 + 0] = a; + ((u32 *)g_brand)[off / 4 + 1] = b; + ((u32 *)g_brand)[off / 4 + 2] = c; + ((u32 *)g_brand)[off / 4 + 3] = d; + } + g_brand[48] = 0; + } else { + g_brand[0] = 0; + } + + pt_cpuid(1, 0, &a, &b, &c, &d); + g_cores = (b >> 16) & 0xFF; + g_hv = (int)((c >> 31) & 1); + + LARGE_INTEGER f; + f.QuadPart = 0; + QueryPerformanceFrequency(&f); + g_tsc_hz = (u64)f.QuadPart; +} + +/* 画一行 label: value,返回下一行 y */ +static int row(PT_Canvas *cv, int y, const char *label, const char *value) { + pt_text(cv, label, 28, y, PT_TEXT2); + pt_text(cv, value, 170, y, PT_TEXT); + return y + 30; +} + +static void frame(PT_Canvas *cv) { + pt_fill(cv, 0, 0, cv->w, cv->h, PT_BG); + + /* 顶部标题卡 */ + pt_round_fill(cv, 16, 16, cv->w - 32, 64, PT_CARD, 8); + pt_text(cv, "Deshab SysInfo", 32, 22, PT_TEXT); + pt_text(cv, "PE compat layer / native Ring0", 32, 44, PT_TEXT_DIM); + + /* 信息卡 */ + int ch = 0; + int rows = 8; + ch = 16 + rows * 30 + 24; + pt_round_fill(cv, 16, 92, cv->w - 32, ch, PT_CARD, 8); + + int y = 108; + y = row(cv, y, "CPU vendor", g_vendor); + y = row(cv, y, "CPU brand", g_brand[0] ? g_brand : "(not supported)"); + { + char b[16]; + pt_dec(g_cores, b); + row(cv, y, "CPU cores (max APIC)", b); + y += 30; + } + y = row(cv, y, "Hypervisor", g_hv ? "yes" : "no"); + { + char b[24]; + u32 mhz = g_tsc_hz ? (u32)(g_tsc_hz / 1000000u) : 0; + pt_dec(mhz, b); + int n = 0; + while (b[n]) n++; + b[n] = ' '; b[n+1] = 'M'; b[n+2] = 'H'; b[n+3] = 'z'; b[n+4] = 0; + y = row(cv, y, "TSC frequency", b); + } + { + PT_Time t = pt_rtc_now(); + char b[16]; + pt_fmt_date(t, b); + int n = 0; + while (b[n]) n++; + b[n] = ' '; + pt_fmt_time(t, b + n + 1); + y = row(cv, y, "RTC now", b); + } + { + char b[16]; + pt_dec(GetTickCount() / 1000, b); + int n = 0; + while (b[n]) n++; + b[n] = ' '; b[n+1] = 's'; b[n+2] = 0; + y = row(cv, y, "Uptime (boot)", b); + } + { + char b[16]; + int n = pt_dec((u32)cv->w, b); + b[n] = 'x'; + pt_dec((u32)cv->h, b + n + 1); + y = row(cv, y, "Surface", b); + } + + /* 底部提示 */ + pt_text(cv, "Esc = close", 16, cv->h - 26, PT_TEXT_DIM); +} + +int main(void) { + probe(); + PT_Desc d; + d.w = 560; d.h = 460; + d.title = L"SysInfo"; + d.frame = frame; + d.key = 0; + d.mouse = 0; + return pt_run(&d); +} diff --git a/ISO/run_qemu.bat b/ISO/run_qemu.bat index 218ef22..910a418 100644 --- a/ISO/run_qemu.bat +++ b/ISO/run_qemu.bat @@ -3,25 +3,28 @@ setlocal enabledelayedexpansion set "SCRIPT_DIR=%~dp0" -REM ===== Variant selection: dev / release ===== +REM ===== Variant selection: dev / release / realtest ===== REM Priority: argument > QEMU_VARIANT env > interactive menu > default dev -REM Usage: run_qemu.bat [dev|release] +REM Usage: run_qemu.bat [dev|release|realtest] set "VARIANT=" if not "%~1"=="" set "VARIANT=%~1" if not defined VARIANT if defined QEMU_VARIANT set "VARIANT=%QEMU_VARIANT%" if not defined VARIANT ( echo [qemu] Select image variant to boot: - echo 1. dev ^(deshab-dev.img, debug all on, auto test^) - echo 2. release ^(deshab-release.img, desktop + login^) - set /p CHOICE="Choice [1-2, Enter=dev]: " + echo 1. dev ^(deshab-dev.img, debug all on, auto test, panic=strict^) + echo 2. release ^(deshab-release.img, desktop + login, panic=fallback^) + echo 3. realtest ^(deshab-realtest.img, release options, panic=strict^) + set /p CHOICE="Choice [1-3, Enter=dev]: " if "!CHOICE!"=="2" set "VARIANT=release" + if "!CHOICE!"=="3" set "VARIANT=realtest" if not defined VARIANT set "VARIANT=dev" ) -REM Validate variant name (also accepts passing 1/2 as argument) +REM Validate variant name (also accepts passing 1/2/3 as argument) if "%VARIANT%"=="1" set "VARIANT=dev" if "%VARIANT%"=="2" set "VARIANT=release" -if not "%VARIANT%"=="dev" if not "%VARIANT%"=="release" ( - echo [qemu] Unknown variant: %VARIANT% ^(expected dev / release^) +if "%VARIANT%"=="3" set "VARIANT=realtest" +if not "%VARIANT%"=="dev" if not "%VARIANT%"=="release" if not "%VARIANT%"=="realtest" ( + echo [qemu] Unknown variant: %VARIANT% ^(expected dev / release / realtest^) exit /b 1 ) @@ -31,7 +34,7 @@ set "NVME_IMG=%SCRIPT_DIR%..\.build_tmp\nvme_test.img" if not exist "%IMG%" ( echo [qemu] Image not found: %IMG% - echo [qemu] Run ..\build.bat first -- default builds both dev and release. + echo [qemu] Run ..\build.bat first -- default builds dev, release and realtest. echo [qemu] Or build selected only: build.bat -Variant %VARIANT% exit /b 1 ) diff --git a/Makefile b/Makefile index e636acb..2d2ae56 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ # Deshab-OS cross-platform build (F3: 疯不应绑 Windows) # # Linux/macOS (native or WSL): -# make dev # build kernels+apps+drivers, inject dev config, pack ISO/deshab-dev.img -# make release # same, release config -> ISO/deshab-release.img -# make realtest # real-machine test config +# make dev # build kernels+apps+drivers, inject dev config, pack ISO/deshab-dev.img (panic=strict) +# make release # same, release config -> ISO/deshab-release.img (panic=fallback) +# make realtest # real-machine test config, release options + panic=strict # make both # dev + release +# make all # dev + release + realtest (SYSTEM restored to dev config) # make kernels apps drivers # compile only # # Requirements: clang + ld.lld (LLVM), GNU make, python3 + Pillow (logo), @@ -53,9 +54,17 @@ DRV_OUTS := $(foreach s,$(DRV_SRCS),$(ROOT)/$(word 2,$(subst :, ,$(s)))) inject-dev inject-release inject-realtest \ iso-dev iso-release iso-realtest clean -all: both +# iso-* 共享 SYSTEM/ 注入点,-j 下必须串行打包 +all: kernels apps drivers + @$(MAKE) --no-print-directory iso-dev + @$(MAKE) --no-print-directory iso-release + @$(MAKE) --no-print-directory iso-realtest + @$(MAKE) --no-print-directory inject-dev -both: dev release +both: kernels apps drivers + @$(MAKE) --no-print-directory iso-dev + @$(MAKE) --no-print-directory iso-release + @$(MAKE) --no-print-directory inject-dev kernels: @for d in $(KERNELS); do echo "[build] $$d"; $(MAKE) -C $$d -f MAKEFILE CC="$(CC)" LD="$(LD)" || exit 1; done diff --git a/README.md b/README.md index 8cc34e2..eeb7ec2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # Deshab-OS - +# 致力于打造极差的用户体验 > 实验内核 · QEMU 可启动演示 · 非生产系统 Deshab-OS 是一个从零编写的独立 64 位操作系统,基于 x86_64 long mode,采用单地址空间 Ring0(SAS-R0)内核架构,配套封缄内存(UTSM)、专用恢复根(DRR)与可加载驱动模块系统(DKM)。 -当前已完成可启动的实验内核闭环:Limine 引导 → UTSM 首阶段内核 → 14 个 DKM 驱动加载 → DSK 主内核 → 桌面/登录/向导。 +当前已完成可启动的实验内核闭环:Limine 引导 → UTSM 首阶段内核 → DKM 驱动加载 → DSK 主内核 → 桌面/登录/向导。 + +**进行中的大改造(万物互联 / D6-D7)**:正在把系统从"UTSM→DSK→app"层级结构改造成**没有主内核**的形态——一切(文件系统、向导、登录、桌面、shell)皆平等 DKM 内核模块,模块间只经**按名服务总线**(`kern.*` / `srv.*` 注册-消费)互联,DSK 将退役,调度器转常驻。设计定稿见 [万物互联与服务总线设计](docs/RE/万物互联与服务总线设计.md),P0-P6 六阶段逐步落地(进度见 [TASKBOARD.md](docs/TASKBOARD.md))。 ## 项目特质 @@ -25,7 +27,9 @@ SAS-R0-PCQ 单地址空间 Ring0 + Per-CPU O(1) 位图调度器(当前 BSP-on UTSM UUID 调谐封缄内存(明文须经 UTRW 读写器) UTRW 带标记读写器(Fast/Slow Path ✅,RFC 8439 KAT 对拍) DRR 专用恢复根(看门狗 / checkpoint / A-B 回滚 / 独立 Emergency Pool) -DKM 驱动模块系统(ELF64 .drv ABI,4 stage 分阶段加载) +DKM 驱动模块系统(ELF64 .drv ABI,分 stage 加载,manifest depends 依赖图强制) +DKM-BUS 按名服务总线(kern.* / srv.* 双向注册-消费)——万物互联的神经, + 目标形态:没有主内核,一切皆内核模块(进行中,见设计文档) ``` ## 项目状态 @@ -39,6 +43,7 @@ DKM 驱动模块系统(ELF64 .drv ABI,4 stage 分阶段加载) | AHCI / e1000 / FAT32 写盘 | ✅ | | NVMe / virtio-net / FAT32 完整路径 | 🔶 | | PCQ 调度器 / DRR 恢复根 | ✅(BSP-only / 演示级) | +| 万物互联改造(服务总线 / 应用 DKM 化 / DSK 退役) | 🔶 P0-P6 进行中,见 [设计文档](docs/RE/万物互联与服务总线设计.md) | | 五期填补计划进度 | 一期二期全勾,见 [docs/TASKBOARD.md](docs/TASKBOARD.md) | ### 已知边界(Non-Goals) @@ -52,6 +57,7 @@ DKM 驱动模块系统(ELF64 .drv ABI,4 stage 分阶段加载) | 文档 | 内容 | |------|------| | [STATUS.md](docs/STATUS.md) | **单一状态源**(全子系统三色表 + 证据) | +| [万物互联与服务总线设计.md](docs/RE/万物互联与服务总线设计.md) | 万物互联架构:服务总线 / DSK 溶解 / P0-P6 路线 | | [ROADMAP_INSANE.md](docs/ROADMAP_INSANE.md) | 五期填补计划(F/U/D/R/E 唯一任务编号) | | [TASKBOARD.md](docs/TASKBOARD.md) | 可勾选任务表 | | [PHILOSOPHY.md](docs/PHILOSOPHY.md) | 疯圣典:矛盾美学与「疯但好使」物理学 | diff --git a/SYSTEM/STARTMENU.CNF b/SYSTEM/STARTMENU.CNF new file mode 100644 index 0000000..2b4da3a --- /dev/null +++ b/SYSTEM/STARTMENU.CNF @@ -0,0 +1,29 @@ +# Deshab 开始菜单注册表(desktop 启动时解析,FAT32 根目录) +# 格式: NAME|显示名|path|args ('args' 可省略) +# +# path 四种: +# builtin: → 已注册内置应用(editor/calc/fileman/settings/shell/cmd/browser/proedit) +# program/xxx.exe → GUI PE 应用(SYSTEM/program/,g++ 编译,桌面窗口嵌入运行) +# bin/xxx.exe → PE 应用(同上;纯控制台 exe 请从 CMD 运行,不入菜单) +# bin/xxx.elf → ELF 工具(SYSTEM/bin/,全屏加载,Esc/退出返回桌面) +# /guest/path → Linux guest 应用(需 Linux 兼容层在线) +# +# 显示名允许 UTF-8 中文(开始菜单瓦片标签);NAME 须 ASCII(窗口标题用)。 +# 文件缺失/为空时回退:全部已注册应用进开始菜单。 +# CLI 工具(bin/sysinfo.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行。 + +SHELL|终端|builtin:shell +CMD|CMD|builtin:cmd +EDITOR|编辑器|builtin:editor +FILEMAN|文件管理|builtin:fileman +CALC|计算器|builtin:calc +SETTINGS|设置|builtin:settings +BROWSER|浏览器|builtin:browser +PROEDIT|ProEdit|builtin:proedit +SYSINFO|系统信息|program/sysinfo.exe +CLOCK|时钟|program/clock.exe +PAINT|画板|program/paint.exe +NOTES|记事本|program/notes.exe +DISKINFO|磁盘工具|bin/diskinfo.elf +DEAICUP|Deaicup|program/Deaicup-Software.exe +VSCODE|VSCode|/opt/vscode/bin/code|--no-sandbox --disable-gpu diff --git a/SYSTEM/driver/fw/mediatek/MT7920PA.BIN b/SYSTEM/driver/fw/mediatek/MT7920PA.BIN new file mode 100644 index 0000000..dda9457 Binary files /dev/null and b/SYSTEM/driver/fw/mediatek/MT7920PA.BIN differ diff --git a/SYSTEM/driver/fw/mediatek/MT7920RAM.BIN b/SYSTEM/driver/fw/mediatek/MT7920RAM.BIN new file mode 100644 index 0000000..76cd0b3 Binary files /dev/null and b/SYSTEM/driver/fw/mediatek/MT7920RAM.BIN differ diff --git a/SYSTEM/driver/manifest.json b/SYSTEM/driver/manifest.json index e7723cf..dbdca25 100644 --- a/SYSTEM/driver/manifest.json +++ b/SYSTEM/driver/manifest.json @@ -171,6 +171,14 @@ "depends": ["pci", "irq"], "provides": ["netdev"] }, + { + "name": "mt7920", + "path": "driver/net/mt7920.drv", + "class": "net", + "required": false, + "depends": ["pci"], + "provides": ["netdev"] + }, { "name": "ath9k", "path": "driver/net/ath9k.drv", diff --git a/SYSTEM/limine/limine.conf b/SYSTEM/limine/limine.conf index 8a72014..7b85f63 100644 --- a/SYSTEM/limine/limine.conf +++ b/SYSTEM/limine/limine.conf @@ -44,6 +44,12 @@ verbose: yes module_cmdline: dkm:virtio_net module_path: boot():/driver/net/ath9k.drv module_cmdline: dkm:ath9k + module_path: boot():/driver/net/mt7920.drv + module_cmdline: dkm:mt7920 + module_path: boot():/driver/fw/mediatek/MT7920PA.BIN + module_cmdline: fw:mt7920_patch + module_path: boot():/driver/fw/mediatek/MT7920RAM.BIN + module_cmdline: fw:mt7920_ram module_path: boot():/driver/input/ps2kbd.drv module_cmdline: dkm:ps2kbd module_path: boot():/system/deshab64/deshab.elf diff --git a/SYSTEM/system/deshab64/FUCK b/SYSTEM/system/deshab64/FUCK index d647560..94c7e9c 100644 --- a/SYSTEM/system/deshab64/FUCK +++ b/SYSTEM/system/deshab64/FUCK @@ -33,6 +33,7 @@ devfs=1 e1000=1 virtio_net=1 ath9k=0 +mt7920=1 ps2kbd=1 ioapic_init=1 lapic_enable=1 @@ -61,6 +62,15 @@ demo_fault=0 [drr] watchdog_enabled=1 +# === panic 策略 === +# mode=strict :未实现/异常一律 panic(莲花屏停机),STUB 规约默认行为。 +# mode=fallback :panic 点只记串口坐标 [PANIC:FALLBACK] 后走备用路径继续启动 +# (跳过该能力/驱动/页面);CPU 异常、LA57、DSK 跳转失败仍停机。 +# 非 fallback 的任何值均按 strict 处理。 +[panic] +# dev:启用 panic(strict),零降级 +mode=strict + # === SAS-R0 启动期能力证明(attest) === # require_=1(默认)而能力未就绪 → 启动即莲花屏 panic。 # require_=0 是设计内白名单:仅限"该构建环境不可能具备"或 @@ -96,6 +106,30 @@ mouse_init=1 netman=1 session=native +# === "极差用户体验" 整蛊系统 === +# enable=1 打开总开关;下列各项 =0 可单独关闭。 +# 触发:开机进桌面后 boot_demo_sec 秒串烧演示 + 使用中每 interval_sec 秒随机一个。 +[prank] +enable=1 +# 进桌面后串烧演示秒数(用于验证效果),0=关 +boot_demo_sec=5 +# 使用中自动整蛊间隔(秒),0=关 +interval_sec=60 +# 屏幕压暗 + 特大 FUCK 跟随鼠标后消失 +fuck_cursor=1 +# 假蓝屏(Win 风格) +fake_bsod=1 +# 全屏反色闪烁 +invert_flash=1 +# 假"程序未响应"对话框 +fake_hang=1 +# 打字乱码(按键被随机重映射) +garble_typing=1 +# 任务栏图标乱飞 +taskbar_fly=1 +# 假"系统即将关机"倒计时 +fake_shutdown=1 + [debug] enabled=1 log_level=1 @@ -116,20 +150,20 @@ lib_path=lib linux_lib_guest_path=/usr/lib/deshab linux_lib_sync=1 pe_dll_search=1 - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/SYSTEM/user/desktop/editor.lnk b/SYSTEM/user/desktop/editor.lnk deleted file mode 100644 index c8a18e5..0000000 --- a/SYSTEM/user/desktop/editor.lnk +++ /dev/null @@ -1,4 +0,0 @@ -编辑器 -EDITOR ELF -/system/deshab64/tools/editor.elf -editor \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index 85d4751..1ad8897 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,5 +1,6 @@ param( - [ValidateSet('dev','release','both')][string]$Variant = 'both', + # dev/realtest: panic=strict; release: panic=fallback. both = dev+release, all = all three. + [ValidateSet('dev','release','realtest','both','all')][string]$Variant = 'all', [switch]$Vhd ) @@ -15,6 +16,7 @@ $Output = Join-Path $SystemDir 'boot\utsm.elf' $DskOutput = Join-Path $SystemDir 'system\deshab64\deshab.elf' $DevImagePath = Join-Path $IsoDir 'deshab-dev.img' $ReleaseImagePath = Join-Path $IsoDir 'deshab-release.img' +$RealtestImagePath = Join-Path $IsoDir 'deshab-realtest.img' $BuildTmp = Join-Path $Root '.build_tmp' # Legacy single-FAT32 ESP size helper (New-GptFat32Image). Dual-partition @@ -1055,6 +1057,7 @@ Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv') Build-DkmDriverMulti @((Join-Path $Root 'CODE\DKM\ath9k\ath9k.c'), (Join-Path $Root 'CODE\DKM\ath9k\wpa2_crypto.c')) (Join-Path $SystemDir 'driver\net\ath9k.drv') +Build-DkmDriver (Join-Path $Root 'CODE\DKM\mt7920\mt7920.c') (Join-Path $SystemDir 'driver\net\mt7920.drv') Build-DkmDriver (Join-Path $Root 'CODE\DKM\wlan\wlan.c') (Join-Path $SystemDir 'driver\net\wlan.drv') # stage3: input — ps2kbd 注册 IRQ1。 @@ -1073,18 +1076,23 @@ if (Test-Path $LegacyImg) { } Write-Host '[build] Packaging SYSTEM to GPT images (p1 FAT32 ESP + p2 ext4, not a single FAT32 disk)...' -if ($Variant -in @('dev','both')) { - Write-Host '[build] === Building DEV image (QEMU test, dev_mode=1, debug on) ===' +if ($Variant -in @('dev','both','all')) { + Write-Host '[build] === Building DEV image (QEMU test, dev_mode=1, debug on, panic=strict) ===' Build-ImageVariant 'dev' $DevImagePath Write-Host "[build] DEV image done: $DevImagePath" } -if ($Variant -in @('release','both')) { - Write-Host '[build] === Building RELEASE image (real hardware, dev_mode=0, debug off) ===' +if ($Variant -in @('release','both','all')) { + Write-Host '[build] === Building RELEASE image (real hardware, dev_mode=0, debug off, panic=fallback) ===' Build-ImageVariant 'release' $ReleaseImagePath Write-Host "[build] RELEASE image done: $ReleaseImagePath" } +if ($Variant -in @('realtest','all')) { + Write-Host '[build] === Building REALTEST image (real hardware, release options, panic=strict) ===' + Build-ImageVariant 'realtest' $RealtestImagePath + Write-Host "[build] REALTEST image done: $RealtestImagePath" +} # Restore SYSTEM to dev config (dev is the primary development target) -if ($Variant -eq 'both') { +if ($Variant -in @('both','all')) { Write-Host '[build] Restoring SYSTEM to dev config for development...' Build-ImageVariant 'dev' $null } diff --git a/build/configs/dev/FUCK b/build/configs/dev/FUCK index d647560..94c7e9c 100644 --- a/build/configs/dev/FUCK +++ b/build/configs/dev/FUCK @@ -33,6 +33,7 @@ devfs=1 e1000=1 virtio_net=1 ath9k=0 +mt7920=1 ps2kbd=1 ioapic_init=1 lapic_enable=1 @@ -61,6 +62,15 @@ demo_fault=0 [drr] watchdog_enabled=1 +# === panic 策略 === +# mode=strict :未实现/异常一律 panic(莲花屏停机),STUB 规约默认行为。 +# mode=fallback :panic 点只记串口坐标 [PANIC:FALLBACK] 后走备用路径继续启动 +# (跳过该能力/驱动/页面);CPU 异常、LA57、DSK 跳转失败仍停机。 +# 非 fallback 的任何值均按 strict 处理。 +[panic] +# dev:启用 panic(strict),零降级 +mode=strict + # === SAS-R0 启动期能力证明(attest) === # require_=1(默认)而能力未就绪 → 启动即莲花屏 panic。 # require_=0 是设计内白名单:仅限"该构建环境不可能具备"或 @@ -96,6 +106,30 @@ mouse_init=1 netman=1 session=native +# === "极差用户体验" 整蛊系统 === +# enable=1 打开总开关;下列各项 =0 可单独关闭。 +# 触发:开机进桌面后 boot_demo_sec 秒串烧演示 + 使用中每 interval_sec 秒随机一个。 +[prank] +enable=1 +# 进桌面后串烧演示秒数(用于验证效果),0=关 +boot_demo_sec=5 +# 使用中自动整蛊间隔(秒),0=关 +interval_sec=60 +# 屏幕压暗 + 特大 FUCK 跟随鼠标后消失 +fuck_cursor=1 +# 假蓝屏(Win 风格) +fake_bsod=1 +# 全屏反色闪烁 +invert_flash=1 +# 假"程序未响应"对话框 +fake_hang=1 +# 打字乱码(按键被随机重映射) +garble_typing=1 +# 任务栏图标乱飞 +taskbar_fly=1 +# 假"系统即将关机"倒计时 +fake_shutdown=1 + [debug] enabled=1 log_level=1 @@ -116,20 +150,20 @@ lib_path=lib linux_lib_guest_path=/usr/lib/deshab linux_lib_sync=1 pe_dll_search=1 - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/build/configs/realtest/FUCK b/build/configs/realtest/FUCK new file mode 100644 index 0000000..28ee451 --- /dev/null +++ b/build/configs/realtest/FUCK @@ -0,0 +1,167 @@ +# Deshab Kernel Configuration — realtest 变体(build/configs/realtest/FUCK) +# build.ps1 -Variant realtest 打包前注入 SYSTEM/system/deshab64/FUCK +# 真机测试版:与 release 相同的真机安全选项 + 正常登录路径,但启用 panic(strict), +# 未实现/异常即莲花屏停机,用于真机上定位 panic 坐标。 + +[boot] +vmm=0 +linux_guest=0 +xj380_guest=0 +# F4: 分级自检(快速档,不含 UTSM selftest) +selftest=1 +dev_mode=0 +la57_guard=0 +mcfg_check=0 +max_phys_addr_bits=0 +disk_log=0 + +[drivers] +timer=1 +apic=1 +acpi=1 +pci=1 +console_fb=1 +ahci=1 +nvme=1 +ehci=1 +xhci=1 +bootfs=1 +vfs=1 +fat32=1 +devfs=1 +e1000=1 +virtio_net=1 +# ath9k=0:原生 Atheros 驱动仅支持 AR928x,其他厂商(Intel/Realtek/MediaTek) +# 无匹配设备时 driver_init 返回非零 → DKM-E39 开机 panic。普通网卡走 +# linuxguest 后端(guest 内 mt76/iwlwifi/rtw88 + wpa_supplicant,状态回传)。 +ath9k=0 +mt7920=1 +# wlan=0:框架骨架,在有无线控制器的机器上会注册设备,其 scan/connect +# 调用即 NETWL-E10/E11 panic。等 linuxguest 后端落地前先关闭。 +wlan=0 +ps2kbd=1 +ioapic_init=1 +lapic_enable=1 +apic_route=1 +apic_route_legacy=1 +nvme_msi=1 +apic_diag=0 + +[utsm] +max_segments=1024 +max_capabilities=256 +max_pckc_keys=8 +dirty_shard_pages=1024 +arena_size_mb=64 +drr_emergency_pool_kb=64 +drr_recovery_log_size=4096 +drr_heartbeat_interval=0 + +[sched] +enable=1 +tick_hz=100 +timeslice_ms=10 +demo_ms=0 +demo_fault=0 + +[drr] +watchdog_enabled=1 + +# === panic 策略 === +# mode=strict :未实现/异常一律 panic(莲花屏停机),STUB 规约默认行为。 +# mode=fallback :panic 点只记串口坐标 [PANIC:FALLBACK] 后走备用路径继续启动 +# (跳过该能力/驱动/页面);CPU 异常、LA57、DSK 跳转失败仍停机。 +# 非 fallback 的任何值均按 strict 处理。 +[panic] +# realtest:启用 panic(strict),零降级 +mode=strict + +# === SAS-R0 启动期能力证明(attest) === +# require_=1(默认)而能力未就绪 → 启动即莲花屏 panic。 +# release 真机:未实现能力一律阻断启动(不豁免)——宁可启动即 panic。 +# 当前仍需豁免的项 = STATUS.md ⬜/🔶 未落地项;实现后逐项删除豁免行。 +[attest] +require_sealed_mem=1 +require_pckc_derive=1 +require_sched=1 +require_drr=1 +require_fs_fat32_rw=1 +require_fs_ext4_rw=1 +# require_net_wired=0:当前原生驱动仅覆盖 Intel 8254x(e1000)/virtio-net。 +# 测试机为 Realtek 8168 有线 + MediaTek MT7920 无线,均无原生驱动 → +# net_total=0。属白名单①(该机器不具备受支持网卡),待 linuxguest 后端 +# (guest 内发行版驱动)落地后删除本豁免行。实现后逐项删除。 +require_net_wired=0 +# NET-E01 wlan 栈未实现(linuxguest 后端落地后删除) +require_net_wlan=0 +# VMX 为 guest 能力,真机不强制 +require_vmm_vmx=0 +# DRR-E31 Phase 9 +require_drr_real_mac=0 +# PKG-E10 +require_pkg=0 +# BRAND-E10 +require_brand_anim=0 +# DKM-E60 +require_dkm_hot_unload=0 +# DKM-E61 +require_dkm_api_export=0 +# NVME-E30 +require_nvme_rw=0 +# VNET-E30 +require_virtio_net=0 + +[dsk] +show_logo=1 +skip_login=0 +# F5: release 走正常路径(向导/登录生效) +skip_firstinit=0 +mouse_init=1 +netman=1 +session=native + +# === "极差用户体验" 整蛊系统 === +# enable=1 打开总开关;下列各项 =0 可单独关闭。 +# 触发:开机进桌面后 boot_demo_sec 秒串烧演示 + 使用中每 interval_sec 秒随机一个。 +[prank] +enable=1 +# 进桌面后串烧演示秒数,0=关 +boot_demo_sec=5 +# 使用中自动整蛊间隔(秒),0=关 +interval_sec=120 +# 屏幕压暗 + 特大 FUCK 跟随鼠标后消失 +fuck_cursor=1 +# 假蓝屏(Win 风格) +fake_bsod=1 +# 全屏反色闪烁 +invert_flash=1 +# 假"程序未响应"对话框 +fake_hang=1 +# 打字乱码(按键被随机重映射) +garble_typing=1 +# 任务栏图标乱飞 +taskbar_fly=1 +# 假"系统即将关机"倒计时 +fake_shutdown=1 + +[debug] +enabled=0 +log_level=0 +probe_enable=0 +probe_capacity=4096 +stat_summary_interval=0 +utsm_trace=0 +vmm_trace=0 +dkm_trace=0 +dsk_trace=0 +block_trace=0 +net_trace=0 +input_trace=0 +color_log=0 + +[compat] +lib_path=lib +linux_lib_guest_path=/usr/lib/deshab +linux_lib_sync=0 +pe_dll_search=1 + diff --git a/build/configs/realtest/firstInit.txt b/build/configs/realtest/firstInit.txt new file mode 100644 index 0000000..b261da1 --- /dev/null +++ b/build/configs/realtest/firstInit.txt @@ -0,0 +1,2 @@ +1 +0 diff --git a/build/configs/release/FUCK b/build/configs/release/FUCK index 3657f3f..aa08656 100644 --- a/build/configs/release/FUCK +++ b/build/configs/release/FUCK @@ -34,6 +34,7 @@ virtio_net=1 # 无匹配设备时 driver_init 返回非零 → DKM-E39 开机 panic。普通网卡走 # linuxguest 后端(guest 内 mt76/iwlwifi/rtw88 + wpa_supplicant,状态回传)。 ath9k=0 +mt7920=1 # wlan=0:框架骨架,在有无线控制器的机器上会注册设备,其 scan/connect # 调用即 NETWL-E10/E11 panic。等 linuxguest 后端落地前先关闭。 wlan=0 @@ -65,6 +66,15 @@ demo_fault=0 [drr] watchdog_enabled=1 +# === panic 策略 === +# mode=strict :未实现/异常一律 panic(莲花屏停机),STUB 规约默认行为。 +# mode=fallback :panic 点只记串口坐标 [PANIC:FALLBACK] 后走备用路径继续启动 +# (跳过该能力/驱动/页面);CPU 异常、LA57、DSK 跳转失败仍停机。 +# 非 fallback 的任何值均按 strict 处理。 +[panic] +# release:关闭 panic(fallback),真机/演示带病看全貌 +mode=fallback + # === SAS-R0 启动期能力证明(attest) === # require_=1(默认)而能力未就绪 → 启动即莲花屏 panic。 # release 真机:未实现能力一律阻断启动(不豁免)——宁可启动即 panic。 @@ -114,8 +124,8 @@ session=native # 触发:开机进桌面后 boot_demo_sec 秒串烧演示 + 使用中每 interval_sec 秒随机一个。 [prank] enable=1 -# 发布版不做开机演示(真机不受验证串烧打扰);使用中仍随机整蛊 -boot_demo_sec=0 +# 进桌面后串烧演示秒数,0=关 +boot_demo_sec=5 # 使用中自动整蛊间隔(秒),0=关 interval_sec=120 # 屏幕压暗 + 特大 FUCK 跟随鼠标后消失 @@ -153,4 +163,4 @@ lib_path=lib linux_lib_guest_path=/usr/lib/deshab linux_lib_sync=0 pe_dll_search=1 - + diff --git a/docs/RE/万物互联与服务总线设计.md b/docs/RE/万物互联与服务总线设计.md new file mode 100644 index 0000000..b695e84 --- /dev/null +++ b/docs/RE/万物互联与服务总线设计.md @@ -0,0 +1,281 @@ +# 万物互联与服务总线设计(DKM-BUS) + +> 系列代号:**万物互联(Deshab Interconnect)** +> 路线:P0-P6,六阶段全量改造 +> 状态源:[STATUS.md](../STATUS.md);任务板:[TASKBOARD.md](../TASKBOARD.md)(D6/D7 条目) +> 设计定稿:2026-10-10 + +--- + +## 0. 一句话 + +**没有主内核。utsm.elf 只剩"机制"(中断/内存/调度/恢复/封缄/panic/加载器),其余一切——驱动、文件系统、向导、登录、桌面、shell——都是平等的 DKM 内核模块,彼此只通过按名服务总线(kern.\* / srv.\*)互联。** + +这是 library OS / 万物互联形态:程序即内核,模块间没有 syscall 边界,没有第二次内核交接。性能不是本设计的目标——按名查找、间接调用、注册-消费模式到处用,是刻意的(自由 > 快)。 + +```text +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 槽偏移不受影响): + +```c +#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 SATISFIED` panic。 +- 同 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 REGISTERED` panic。 +- stage0-3 老 14 驱动豁免:它们的 provides 由内核侧注册表兑现(如 ahci → block registry),不经总线。豁免清单 = manifest.json 现有 stage0-3 全部条目,写死在 `dsm_verify_provides` 的注释里。 + +为什么 provides 不自动注册:loader 拿不到服务 fn 指针(注册哪些函数、什么签名是模块的自由)。所以是"模块主动注册 + loader 事后校验契约"——自由也要有地图,但地图由填图人自己画,loader 只查漏。 + +### 1.6 生命周期模型("程序变内核任务") + +```text +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..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 _run(void)` 注册 `srv..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 同步登记) + +1. 无块设备时 `srv.fs.fat32.*` 缺席 → PE/会话路径 warn 跳过(环境能力缺失,与既有"块设备缺失回 boot module"白名单同源)。 +2. FUCK `[drivers]` 关掉的模块 → 其 srv.* 服务缺席,消费方必须用 probe 并显式处理。 +3. QEMU 无 ath9k / 无 wlan → 既有白名单不变。 +4. **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. 风险点 + +1. **desktop 多 TU 重定位集合**(最大技术风险):lld -r 产物可能带出新 relocation 类型 → DKM-E37 开机即炸(至少响亮)。每模块迁移必跑 readobj 并在 phase 记录贴清单。 +2. **DSK-E70(ibuf 尾部写零,未解)**:DKM 化整体绕开 8MB ibuf,但若根因是 AHCI DMA PRDT 越界,会跟到 fat32.drv 缓冲。fat32/ext4 保留写后回读校验,持续观察 STATUS.md 该条目。 +3. **交互式模块阻塞 bootstrap**:与今天行为等价无回归;DRR 看门狗默认关(FUCK),接 P6 时给 desktop 任务显式 register/kick。 +4. **加载器重入**:loader 单线程不变式(§1.6),违反即契约违规。 +5. **attest/FUCK 双面性**:app 模块进 manifest 后受 `[drivers]` 开关与 `require_*` 影响;build/configs/{dev,release}/FUCK 的 attest 豁免行同步增删(release 要求 desktop 必在,QEMU 无 ath9k 等继续豁免)。 +6. **DSM/manifest 容量**:stage4 一期约 8 条 → P3 把上限提到 12/6;name[32]/path[64]/depends 4/provides 4,desktop 的 provides token 控制在 4 内。 + +--- + +> "没有主内核,每个模块都是内核——总线就是万物的神经。疯但互联。" 🪷 diff --git a/docs/RE/驱动模块ABI设计.md b/docs/RE/驱动模块ABI设计.md index 79a6821..7f8cd2f 100644 --- a/docs/RE/驱动模块ABI设计.md +++ b/docs/RE/驱动模块ABI设计.md @@ -609,6 +609,45 @@ quiesce -> UTSM rollback -> device reset -> driver reinit -> resume --- +## 8.5 服务总线 ABI(万物互联 · D6/D7,2026-10-10 追加) + +> 完整设计见 [万物互联与服务总线设计.md](./万物互联与服务总线设计.md)。本节只定 ABI 契约。 + +### 8.5.1 总线访问 + +`dkm_kernel_api` **尾部追加**(ABI 兼容,既有槽位偏移不变): + +```c +const dkm_bus_api *bus; /* kapi 之后 */ +``` + +总线结构(`dkm.h`):`dkm_bus_api { magic, version, register_service, unregister_service, lookup, probe, count, at }`,服务项 `dkm_bus_service { name[48], version, flags, fn, provider }`。 + +### 8.5.2 命名空间与注册规则 + +- `kern.*`:仅内核可注册(模块注册即 BUS-E01)。 +- `srv.*`:任何 .drv 在 driver_init 里注册。**"运行"也是服务**:长期模块把原入口主体改为 `static void _run(void)` 并注册 `srv..run`。 +- 消费:硬依赖用 `lookup`(miss → BUS-E04 panic);能力缺席(设计内双路径)用 `probe`(miss 返回 -1,回退须登记 CLAUDE.md 白名单)。 + +### 8.5.3 生命周期契约 + +```text +driver_init(handle, api) = 初始化 + 注册 srv.*(+ 长期模块 task_create,此时抢占未启用) +运行 = 排序者(session.drv)bus 调 srv.*.run;一次性模块 call-and-return +DKM_F_NO_UNLOAD 模块的服务带 F_NO_UNLOAD,注销即 panic +loader 在 driver_init 前后设/清 current provider(provider 归属判定) +``` + +### 8.5.4 provides 校验(stage≥4) + +stage≥4 模块的每个 manifest provides token `T`,driver_init 成功返回后必须已有 `srv.T` 或 `srv.T.*` 注册,否则 `DKM-E45` panic。stage0-3 老驱动豁免(provides 由内核注册表兑现,如 ahci→block registry)。同时 depends 自 P0 起被 loader 强制(miss → DKM-E44;依赖环 → DKM-E47)。 + +### 8.5.5 新错误码 + +`BUS-E01..E05`(名字非法/重名/表满/lookup 未命中/注销违规)、`DKM-E44..E47`(依赖未满足/未注册 provides/双源皆无/依赖环)——全部零降级 panic。 + +--- + ## 9. 第一版定稿 第一版驱动系统确定: diff --git a/docs/ROADMAP_INSANE.md b/docs/ROADMAP_INSANE.md index 512a4de..d399999 100644 --- a/docs/ROADMAP_INSANE.md +++ b/docs/ROADMAP_INSANE.md @@ -60,11 +60,13 @@ Deshab 最标志性的疯点——给空气上锁。 |------|------|---------|------|-------------------| | D1 | DKM 热卸载(基础) | 从"焊死"到"可拔" | ⬜ | 仅有 `DKM_F_NO_UNLOAD` 标志与状态机文档;需引用计数+资源回收,load/unload 循环 100 次不崩 | | D2 | 裸机器码加载器 | 真·无限自由 | ⬜ | 任意 elf/裸码塞地址即执行;能打印、能崩内核 | -| D3 | 内核 API 导出表 | 自由也要有地图 | ⬜ | 当前为固定偏移 struct ABI(`CODE/sdk/include/deshab/kernel_api.h`);需按名导出表 kapi() | +| D3 | 内核 API 导出表 → **服务总线** | 自由也要有地图 | 🔶 | kapi 按名导出表 ✅(14 条,只读);升级为**双向服务总线 dkm_bus**(kern.\*/srv.\* 注册-消费,BUS-E 系列)进行中——见 [万物互联与服务总线设计](./RE/万物互联与服务总线设计.md) | | D4 | DKM 彩色日志前缀 | 每个驱动有自己的脾气 | ✅ | 驱动名 FNV hash → 8 色 ANSI(确定性可复现);init 期着色,仅串口;FUCK `color_log` 开关 | | D5 | 最小 REPL/shell | 自由的交互面 | ✅ | shell.elf:ls/cat/cp/mv/rm/run/probe/test/pci 等 25+ 命令 | +| D6 | **万物互联:应用模块 DKM 化** | 没有用户态,程序即内核 | 🔶 | 向导/登录/桌面/shell/netman/cmd 全部从 DSK 加载的 PIE ELF 变为 stage4 .drv;dsk_boot_context reserved[] 硬耦合逐槽消灭,改为 srv.* 总线服务;设计定稿 [RE/万物互联与服务总线设计.md](./RE/万物互联与服务总线设计.md),P0-P4 落地中 | +| D7 | **万物互联:DSK 退役 + 常驻调度** | 没有主内核,万般皆平等 | ⬜ | deshab.elf/dsk_loader.c/dsk_boot_context 删除,会话排序变普通 session.drv;PCQ 调度器常驻(bootstrap park 成 idle)、desktop 任务化、IRQ1/12 真 IRQ 输入;P5-P6 | -三期做完:用户能真在系统里"为所欲为",疯得可上手。 +三期做完:用户能真在系统里"为所欲为",疯得可上手。**D6/D7 做完:连"用户"这个概念都消失——万物皆内核,互联皆总线。** --- diff --git a/docs/STATUS.md b/docs/STATUS.md index ae310af..b938bf8 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -16,6 +16,7 @@ | DMA 物理页分配器 | ✅ | bitmap,低 4G 钳位 | e1000 DMA 根治记录 | | 页表映射接口(map/unmap) | ✅ | `kernel_api.mm_map_mmio` / `mm_unmap_mmio`(PCD\|PWT 独立窗口);apic / ehci / xhci / nvme 四个驱动在用,含 BAR>4G 高位映射 | 串口 `BAR mapped via mm_map_mmio window` | | panic 统一兜底(F1) | ✅ | DEAICUP 莲花崩溃屏(ohMyLogo.png 内嵌,背景=图片背景色)+ panic_symbol + DRR 归档;CPU 异常/驱动 panic 统一入口 | `.build_tmp/panic_screen.png` + qemu_serial_f1logo2.log | +| panic 双模式(FUCK `[panic] mode`) | ✅ | strict(默认,零降级)/ fallback(panic 点记 `[PANIC:FALLBACK]`+错误码+计数后走备用路径;经 `DSK_BOOT_FLAG_PANIC_FALLBACK` 下传 DSK/FirstInit/login/desktop/DKM);CPU 异常/VMM-E11/VMM-E15/UTSM-E42 两模式均停机(`panic_halt`) | `.build_tmp/qemu_panic_strict.log`、`.build_tmp/qemu_panic_fallback.log`(attest 全 require=1:DRR-E31 hits=1 → DSK 收到 fallback → FirstInit) | | boot 分级自检(F4) | ✅ | `[boot] selftest=0\|1\|2`,5 项全过 BOOT-OK | `.build_tmp/qemu_serial_f1f4f5.log` | | 帧缓冲控制台抽象 fbcon(F2) | 🔶 | `include/utsm/fbcon.h`:像素/混合/填充/RGBA 图像/文本(panic 路径已用);统一控制台后端 ⬜ | `CODE/UTSM/include/utsm/fbcon.h` | | 构建系统 | ✅ | Windows build.ps1 + 跨平台根 Makefile(Linux/macOS `make dev/release/realtest`,打包走 pack_system_image.sh);macOS 缺 mkfs.ext4 时打包不可用 | `Makefile` | @@ -74,6 +75,23 @@ | DRR 看门狗(R2) | ✅ | timeout 检出→回滚/复位;负向测试 PASS | M4 QEMU 日志 | | Emergency Pool(R3) | ✅ | 64KB 编译期独立池;`drr_emergency_pool_kb`/`drr_recovery_log_size` FUCK 接线 ✅(运行期钳位) | `.build_tmp/qemu_serial_r3b.log` | +## 万物互联(DKM-BUS / D6-D7,2026-10-10 立项) + +> 设计定稿:[RE/万物互联与服务总线设计.md](./RE/万物互联与服务总线设计.md)。目标形态:没有主内核,一切皆 DKM 模块,模块间经按名服务总线(kern.\*/srv.\*)互联;DSK 退役;调度器常驻 + 真 IRQ 输入。 + +| 项 | 状态 | 说明 | 证据 | +|----|------|------|------| +| P0 manifest 依赖图强制 | ⬜ | depends 校验 + 同 stage 不动点排序(DKM-E44/E47) | 设计文档 §4 | +| P1 服务总线 dkm_bus | ⬜ | 双向注册-消费(BUS-E01..E05)、kern.* 播种、provider 上下文、provides 校验(DKM-E45)、/dev/services | 设计文档 §1 | +| P2 fat32.drv 实装 + ext4.drv | ⬜ | srv.fs.fat32.* / srv.fs.ext4.*,DSK 文件系统能力归位 | 设计文档 §3 | +| P3 加载器 FS 化 + stage4 + ps2mouse.drv | ⬜ | 模块可从 FS 加载(DKM-E46)、kern.dkm.load_by_name | 设计文档 §4 | +| P4 应用模块 DKM 化(4a-4f) | ⬜ | netman/shell/firstInit/login/desktop/cmd 全部 .drv 化,reserved[] 消灭 | 设计文档 §2 | +| P5 DSK 退役 + session.drv | ⬜ | 删 deshab.elf/dsk_loader.c/dsk_boot_context,会话排序变普通模块 | 设计文档 §3 | +| P6 常驻调度 + 事件循环 + 真 IRQ | ⬜ | sched_enable 常驻、desktop 任务化、IRQ1/12 解屏蔽、kbd/mouse poll 服务 | 设计文档 §1.6 | + +**错误码登记**:BUS-E01..E05(总线注册/查找/注销违规)、DKM-E44..E48(依赖图/加载源/容量)、SESSION-E01..E03(会话排序模块)、TST-E34..E38(总线 selftest;TST-E01..E33 已被既有用例占用)——全部零降级 panic,详见设计文档 §5。 +**probe 型缺席白名单**(登记于 CLAUDE.md §3):无块设备时 srv.fs.fat32.* 缺席 warn 跳过;FUCK `[drivers]` 关闭模块的服务缺席由消费方 probe 显式处理。 + ## 文档体系 ### FS-E 系列:文件系统读写严格模式(2026-10-02) diff --git a/docs/TASKBOARD.md b/docs/TASKBOARD.md index 1c3d817..83cf173 100644 --- a/docs/TASKBOARD.md +++ b/docs/TASKBOARD.md @@ -24,9 +24,27 @@ - [x] D5 最小 REPL/shell(shell.elf 25+ 命令) - [ ] D1 DKM 热卸载(引用计数 + 资源回收,循环 100 次不崩) - [ ] D2 裸机器码加载器 -- [ ] D3 内核 API 按名导出表(kapi()) +- [ ] D3 内核 API 按名导出表 → 双向服务总线(kapi 只读表 ✅;dkm_bus 注册-消费进行中,见 D6) - [x] D4 DKM 彩色日志前缀(驱动名 hash → 8 色 ANSI,init 期着色,BOOTLOG 保持纯文本) +## 🔗 三期扩展:万物互联(D6/D7,P0-P6) + +> 设计定稿:[RE/万物互联与服务总线设计.md](./RE/万物互联与服务总线设计.md)——没有主内核,一切皆 DKM 模块,模块间经按名服务总线互联。 + +- [ ] P0 manifest 依赖图强制(depends 校验 + 不动点排序,DKM-E44/E47;负向测试莲花屏) +- [ ] P1 dkm_bus 服务总线(kern.*/srv.* 双向注册-消费、BUS-E01..05、provider 上下文、DKM-E45 provides 校验、/dev/services、TST-E25..28) +- [ ] P2 fat32.drv 实装 + ext4.drv(srv.fs.fat32.* / srv.fs.ext4.*;首次启动全流程不回归) +- [ ] P3 加载器 FS 化 + stage4 session + ps2mouse.drv(dsm_read_module_fs、DKM-E46、kern.dkm.load_by_name、srv.input.mouse.poll) +- [ ] P4a netman.drv(srv.net.lease) +- [ ] P4b shell.drv(probe/lease/dev_mode 走总线) +- [ ] P4c firstinit.drv(user.conf 写盘收编,reserved[0/1] 删除,srv.user.config) +- [ ] P4d login.drv(自读 USER.CONF,srv.auth.result) +- [ ] P4e desktop.drv(~20TU ld -r,host SDL #ifdef 隔离,readobj 重定位核对) +- [ ] P4f cmd.drv + 收尾(DSK 只剩排序骨架) +- [ ] P5 DSK 退役 + session.drv(删 deshab.elf/dsk_loader.c/dsk_boot_context;dev/release 双过) +- [ ] P6 常驻调度 + 真 IRQ 输入(sched_enable 常驻 + bootstrap park、desktop 任务化、IRQ1/12 解屏蔽、kbd 事件环) +- [ ] D7 收尾(kapi 静态表退役、kern.block.* 消费者切换、STUB 基线复核) + ## ⏪ 四期:落款(DRR/PCQ) - [x] R1 checkpoint 快照(A/B 双槽 + CRC64 + 原子切换)