feat(panic): FUCK [panic] mode=strict|fallback + realtest image variant

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

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Linehu
2026-10-12 23:57:19 +08:00
co-authored by Cursor
parent a7e5b2bc7a
commit 8554a82677
84 changed files with 7325 additions and 484 deletions
+38 -13
View File
@@ -85,8 +85,21 @@ SYSTEM/
SYSTEM/driver/manifest.json
内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。
SYSTEM/STARTMENU.CNF
M3 开始菜单注册表(FAT32 根目录,desktop 启动时解析)。
格式 NAME|显示名|path|args;path 支持 builtin:<name> / program/xxx.exe(GUI PE,
g++ 编译,run_windowed 嵌入桌面)/ bin/xxx.elf(ELF 工具,全屏加载)/
/guest/path(Linux guest 应用)。显示名可 UTF-8 中文;文件缺失时回退全部已注册应用。
纯 CLI exe(bin/pciscan.exe、bin/portio.exe)不入菜单——在 CMD 里敲名字运行,
输出走串口。源码在 CODE/tools/pe-tools/(build.ps1 用 mingw-w64 g++ 编译),
ELF 工具源码在 CODE/tools/diskinfo/(→ SYSTEM/bin/diskinfo.elf)。
SYSTEM/program/ + SYSTEM/bin/ + SYSTEM/lib/ + PEAPPS.CNF + LINUXAPP.CNF + STARTMENU.CNF
桌面 PE/工具链全部经 DSK FAT32 只读 ESP——pack_system_image.sh 会把这几项
额外拷到 FAT32 ESP(p1)。漏拷则开始菜单/PE 应用在 GPT 镜像里静默失效。
SYSTEM/system/deshab64/FUCK
内核启动配置文件(INI 分区格式),控制启动行为开关、驱动加载开关、UTSM 算法参数、DSK 桌面行为。
内核启动配置文件(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_<name>=1`(默认)而能力未就绪 → 交接 DSK 前 panic(莲花屏,错误码=该能力登记码)。`require_<name>=0` 豁免行是设计内白名单,仅限两类:① 该构建环境不可能具备(QEMU 无 wlan/嵌套 VMX);② STATUS.md ⬜/🔶 未落地项(实现后逐项删除豁免行)。当前豁免清单见 build/configs/*/FUCK [attest]。
### 4. 自检命令(验收只认这个输出,不认自然语言保证)
+58
View File
@@ -26,6 +26,26 @@ struct dkm_log_api {
void (*panic)(const char *msg);
};
/* D6 万物互联:dkm_bus 镜像(布局对齐 UTSM include/utsm/dkm.h) */
struct dkm_bus_service {
char name[48];
u32 version;
u32 flags;
void *fn;
const void *provider;
};
struct dkm_bus_api {
u32 magic;
u32 version;
int (*register_service)(const char *name, u32 version, void *fn);
int (*unregister_service)(const char *name);
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void);
const struct dkm_bus_service *(*at)(u32 index);
};
struct dkm_kernel_api {
u32 version;
u32 size;
@@ -47,6 +67,19 @@ struct dkm_kernel_api {
u64 fb_pitch;
u16 fb_bpp;
const void *boot_modules_response;
/* —— 尾部追加字段镜像(DKM_ABI v1;到 bus 为止) —— */
void *irq_register; /* int (*)(u8, void*) */
u64 hhdm_offset;
const void *block;
const void *mmio;
void *mm_map_mmio;
void *mm_unmap_mmio;
void *register_apic_eoi;
void *irq_vector_alloc;
void *irq_vector_free;
const void *sched;
const void *kapi;
const struct dkm_bus_api *bus;
};
struct dkm_driver_handle;
@@ -185,6 +218,31 @@ int driver_init(const struct dkm_kernel_api *api,
devfs_file("fb0", info);
}
/* /dev/services — D6 万物互联:总线已注册服务清单 */
if (api->bus && api->bus->magic == 0x53425553u /* "SBUS" */) {
char cnt[24];
cnt[0] = '0'; cnt[1] = 'x';
u64 v = api->bus->count();
u32 p = 2;
if (v == 0) cnt[p++] = '0';
else {
char r[18]; u32 q = 0;
while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; }
while (q) cnt[p++] = r[--q];
}
cnt[p] = 0;
devfs_file("services", cnt);
g_log->info("[devfs] /dev/services/");
for (u32 i = 0; i < api->bus->count(); i++) {
const struct dkm_bus_service *s = api->bus->at(i);
if (s && s->name[0]) {
g_log->info("[devfs] ");
g_log->info(s->name);
}
}
}
/* /dev/modules — count from boot_modules */
const struct limine_module_response *rsp =
(const struct limine_module_response *)api->boot_modules_response;
+3
View File
@@ -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);
+5 -2
View File
@@ -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) {
+721
View File
@@ -0,0 +1,721 @@
/* DKM mt7920 — MediaTek MT7920 / Wi-Fi 6 (PCI 14c3:7920), ConnAC2 PCIe
*
* 本机无线网卡。驱动分阶段落地,当前阶段 = MCU 固件下载(patch + RAM),
* 到 "N9 firmware ready" 为止。扫描/关联仍为显式 panic(MT7920-E10/E11),
* 不伪装成功。
*
* 固件通过 Limine boot module 送入(limine.conf fw:mt7920_patch / fw:mt7920_ram),
* 驱动用 boot_modules_response 按 cmdline 定位,不嵌进 .drv。
*
* 寄存器/流程对拍 Linux mt76(drivers/net/wireless/mediatek/mt76),
* 版本见 .build_tmp/mt76ref。全程串口日志,便于实机逐步 bring-up。
*
* 这是硬件 bring-up 第一阶段:本环境(QEMU 无此卡)无法验证,需实机串口迭代。
*/
#include "../dkm_shared.h"
#include "../dkm_instr.h"
/* ---- limine boot module 结构(与 bootfs.drv 一致) ---- */
struct limine_file {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response {
u64 revision;
u64 module_count;
struct limine_file **modules;
};
static const char *const g_depends[] = {"pci"};
static const char *const g_provides[] = {"netdev"};
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION,
.desc_size=sizeof(struct dkm_driver_desc),
.name="mt7920",.version="0.2.0",.vendor="Deshab",
.driver_class=9,.stage=3,.flags=0,.priority=0,
.depends=g_depends,.depends_count=1,
.provides=g_provides,.provides_count=1,.min_kernel_abi=1,
};
#define MT_VID 0x14C3u
#define MT_DID 0x7920u
/* ===== 寄存器(对拍 mt792x_regs.h / mt7921/regs.h,物理地址) ===== */
#define MT_HW_CHIPID 0x70010200u
#define MT_HW_REV 0x70010204u
#define MT_HW_BOUND 0x70010020u
#define MT_TOP_LPCR_HOST_BAND0 0x18060010u
#define MT_TOP_LPCR_HOST_FW_OWN (1u<<0)
#define MT_TOP_LPCR_HOST_DRV_OWN (1u<<1)
#define MT_WFSYS_RST 0x18000140u
#define WFSYS_SW_RST_B (1u<<0)
#define WFSYS_SW_INIT_DONE (1u<<4)
/* conn_host_csr_top 0x7c060000 → BAR0 0xe0000 */
#define MT_CONN_ON_LPCTL 0x0e0010u
#define PCIE_LPCR_HOST_SET_OWN (1u<<0)
#define PCIE_LPCR_HOST_CLR_OWN (1u<<1)
#define PCIE_LPCR_HOST_OWN_SYNC (1u<<2)
#define MT_CONN_ON_MISC 0x0e00f0u
#define MT_TOP_MISC_FW_STATE 0x7u /* GENMASK(2,0) */
#define MT_TOP_MISC2_FW_PWR_ON (1u<<0)
#define MT_TOP_MISC2_FW_N9_RDY 0x3u /* GENMASK(1,0) */
/* WFDMA0 直接 BAR0 偏移(base 0xd4000) */
#define MT_WFDMA0(ofs) (0xd4000u + (ofs))
#define MT_WFDMA0_RST MT_WFDMA0(0x100)
#define MT_WFDMA0_RST_LOGIC_RST (1u<<4)
#define MT_WFDMA0_RST_DMASHDL_ALL_RST (1u<<5)
#define MT_MCU_CMD MT_WFDMA0(0x1f0)
#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208)
#define MT_WFDMA0_GLO_CFG_TX_DMA_EN (1u<<0)
#define MT_WFDMA0_GLO_CFG_TX_DMA_BUSY (1u<<1)
#define MT_WFDMA0_GLO_CFG_RX_DMA_EN (1u<<2)
#define MT_WFDMA0_GLO_CFG_RX_DMA_BUSY (1u<<3)
#define MT_WFDMA0_GLO_CFG_TX_WB_DDONE (1u<<6)
#define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN (1u<<12)
#define MT_WFDMA0_GLO_CFG_RX_WB_DDONE (1u<<13)
#define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN (1u<<15)
#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO (1u<<28)
#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 (1u<<21)
#define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS (1u<<30)
#define MT_WFDMA0_GLO_CFG_DMA_SIZE_SHIFT 4
#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c)
#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0)
#define MT_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0)
#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN (1u<<6)
#define MT_DMASHDL_SW_CONTROL (0x7c026004u) /* via fixed map below */
#define MT_DMASHDL_DMASHDL_BYPASS (1u<<28)
#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204)
#define MT_WFDMA0_HOST_INT_STA MT_WFDMA0(0x200)
#define MT_MCU2HOST_SW_INT_ENA MT_WFDMA0(0x1f4)
#define MT_WFDMA_DUMMY_CR (0x54000120u)
#define MT_WFDMA_NEED_REINIT (1u<<1)
#define MT_TX_RING_BASE MT_WFDMA0(0x300)
#define MT_RX_EVENT_RING_BASE MT_WFDMA0(0x500)
#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x520)
#define MT_WFDMA0_TX_RING0_EXT_CTRL MT_WFDMA0(0x600)
#define MT_RING_SIZE 0x10u
#define MT_PCIE_MAC_INT_ENABLE 0x010188u /* PCIE_MAC_IREG 0x74030000→0x10000 +0x188 */
/* L1 remap(高位地址窗口),MT_INFRA 0xfe000 */
#define MT_HIF_REMAP_L1 0x0fe24cu
#define MT_HIF_REMAP_BASE_L1 0x40000u
/* ConnAC2 队列索引 */
#define TXQ_FWDL 16
#define TXQ_MCU_WM 17
#define RXQ_MCU_WM 0
/* MCU 命令 */
#define MCU_CMD_TARGET_ADDRESS_LEN_REQ 0x01
#define MCU_CMD_FW_START_REQ 0x02
#define MCU_CMD_NIC_POWER_CTRL 0x04
#define MCU_CMD_PATCH_START_REQ 0x05
#define MCU_CMD_PATCH_FINISH_REQ 0x07
#define MCU_CMD_PATCH_SEM_CONTROL 0x10
#define MCU_CMD_FW_SCATTER 0xee
#define PATCH_SEM_RELEASE 0
#define PATCH_SEM_GET 1
#define PATCH_NOT_DL_SEM_SUCCESS 2
#define PATCH_IS_DL 3
#define PATCH_REL_SEM_SUCCESS 4
#define DL_MODE_NEED_RSP (1u<<31)
#define FW_START_OVERRIDE (1u<<0)
#define FW_FEATURE_OVERRIDE_ADDR (1u<<5)
#define FW_FEATURE_NON_DL (1u<<6)
#define PATCH_SEC_TYPE_MASK 0xffffu
#define PATCH_SEC_TYPE_INFO 0x2
#define MCU_PKT_ID 0xa0u
#define FWDL_MAX_LEN 4096
/* ===== 固件头结构(big-endian patch header,little-endian ram trailer) ===== */
struct patch_hdr {
char build_date[16];
char platform[4];
u32 hw_sw_ver; /* BE */
u32 patch_ver; /* BE */
u16 checksum;
u16 rsv;
struct {
u32 patch_ver;
u32 subsys;
u32 feature;
u32 n_region; /* BE */
u32 crc;
u32 rsv[11];
} desc;
};
struct patch_sec {
u32 type; /* BE */
u32 offs; /* BE */
u32 size; /* BE */
u32 addr; /* BE (info.addr) */
u32 len; /* BE (info.len) */
u32 sec_key_idx;
u32 align_len;
u32 rsv[9];
};
struct fw_trailer {
u8 chip_id;
u8 eco_code;
u8 n_region;
u8 format_ver;
u8 format_flag;
u8 rsv[2];
char fw_ver[10];
char build_date[15];
u32 crc;
};
struct fw_region {
u32 decomp_crc;
u32 decomp_len;
u32 decomp_blk_sz;
u8 rsv[4];
u32 addr; /* LE */
u32 len; /* LE */
u8 feature_set;
u8 type;
u8 rsv1[14];
};
/* mt76 descriptor */
struct mt_desc { u32 buf0; u32 ctrl; u32 buf1; u32 info; };
#define MT_DMA_CTL_SD_LEN0_SHIFT 16
#define MT_DMA_CTL_LAST_SEC0 (1u<<30)
#define MT_DMA_CTL_DMA_DONE (1u<<31)
/* ===== 驱动状态 ===== */
static struct dkm_log_api *g_log;
static const struct dkm_kernel_api *g_api;
static volatile u8 *g_bar; /* BAR0 虚拟基址 */
static u8 g_bus, g_dev, g_func;
static u8 g_mcu_seq;
struct mt_queue {
struct mt_desc *desc; /* 虚拟 */
u64 desc_phys;
u32 ndesc;
u32 head;
u32 ring_base; /* 寄存器基 */
u32 hw_idx; /* ring index within base */
};
static struct mt_queue g_q_fwdl, g_q_wm, g_q_rxmcu;
static struct dkm_dma_buffer g_tx_scratch; /* TX 命令/scatter 暂存 */
static struct dkm_dma_buffer g_rx_bufs; /* RX buffer 池 */
#define RX_NDESC 8u
#define TX_SCRATCH_SZ 8192u
/* ===== 串口日志 ===== */
#define COM1 0x3F8
static void rlog(const char *s){ while(*s){char c=*s++; if(c=='\n'){dkm_serial_wait_tx(COM1);dkm_outb(COM1,'\r');} dkm_serial_wait_tx(COM1);dkm_outb(COM1,(u8)c);} }
static void rhex(const char *p, u64 v){ static const char hx[]="0123456789abcdef"; char b[19]; rlog(p); b[0]='0';b[1]='x'; for(int i=0;i<16;i++)b[2+i]=hx[(v>>((15-i)*4))&0xf]; b[18]=0; rlog(b); rlog("\n"); }
/* strict 模式 g_log->panic 不返回;fallback 模式返回,调用点随后 return -1 */
static void mt_panic(const char *sym){ if(g_log)g_log->panic(sym); else for(;;)__asm__ volatile("cli;hlt"); }
/* ===== 基础 MMIO ===== */
static inline u32 bar_rr(u32 off){ return *(volatile u32*)(g_bar+off); }
static inline void bar_wr(u32 off,u32 v){ *(volatile u32*)(g_bar+off)=v; }
static inline void bar_set(u32 off,u32 m){ bar_wr(off, bar_rr(off)|m); }
static inline void bar_clr(u32 off,u32 m){ bar_wr(off, bar_rr(off)&~m); }
/* 固定映射:把高位物理地址翻成 BAR0 偏移(对拍 __mt7921_reg_addr 子集) */
static u32 fixed_map(u32 addr){
struct { u32 phys, maps, size; } m[] = {
{0x54000000,0x02000,0x01000},
{0x55000000,0x03000,0x01000},
{0x7c000000,0xf0000,0x10000},
{0x7c020000,0xd0000,0x10000},
{0x7c060000,0xe0000,0x10000},
{0x74030000,0x10000,0x10000},
};
if(addr<0x100000) return addr;
for(u32 i=0;i<sizeof(m)/sizeof(m[0]);i++){
if(addr<m[i].phys) continue;
u32 ofs=addr-m[i].phys;
if(ofs>m[i].size) continue;
return m[i].maps+ofs;
}
return 0; /* 需 L1 remap */
}
/* L1 remap 访问高位寄存器(0x18xxxxxx / 0x70xxxxxx 等) */
static u32 l1_addr(u32 addr){
u32 base=addr>>16, offset=addr&0xffff;
u32 cur=bar_rr(MT_HIF_REMAP_L1);
bar_wr(MT_HIF_REMAP_L1,(cur&~0xffffu)|(base&0xffffu));
(void)bar_rr(MT_HIF_REMAP_L1);
return MT_HIF_REMAP_BASE_L1+offset;
}
static u32 reg_rr(u32 phys){ u32 o=fixed_map(phys); if(!o)o=l1_addr(phys); return bar_rr(o); }
static void reg_wr(u32 phys,u32 v){ u32 o=fixed_map(phys); if(!o)o=l1_addr(phys); bar_wr(o,v); }
static void reg_set(u32 phys,u32 m){ reg_wr(phys,reg_rr(phys)|m); }
static void reg_clr(u32 phys,u32 m){ reg_wr(phys,reg_rr(phys)&~m); }
static int poll_reg(u32 phys,u32 mask,u32 val,u32 ms){
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
do { if((reg_rr(phys)&mask)==val) return 0; __asm__ volatile("pause"); } while(dkm_rdtsc()<dl);
return (reg_rr(phys)&mask)==val?0:-1;
}
static int poll_bar(u32 off,u32 mask,u32 val,u32 ms){
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
do { if((bar_rr(off)&mask)==val) return 0; __asm__ volatile("pause"); } while(dkm_rdtsc()<dl);
return (bar_rr(off)&mask)==val?0:-1;
}
/* ===== PCI ===== */
#define PCI_ADDR_PORT 0xCF8u
#define PCI_DATA_PORT 0xCFCu
static inline void outl_(u16 p,u32 v){__asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p));}
static inline u32 inl_(u16 p){u32 v;__asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p));return v;}
static u32 pci_rd(u8 b,u8 d,u8 f,u8 r){ outl_(PCI_ADDR_PORT,(1u<<31)|((u32)b<<16)|((u32)d<<11)|((u32)f<<8)|(r&0xFC)); return inl_(PCI_DATA_PORT); }
static void pci_wr(u8 b,u8 d,u8 f,u8 r,u32 v){ outl_(PCI_ADDR_PORT,(1u<<31)|((u32)b<<16)|((u32)d<<11)|((u32)f<<8)|(r&0xFC)); outl_(PCI_DATA_PORT,v); }
static int mt_find(void){
for(u32 b=0;b<256;b++) for(u8 d=0;d<32;d++){
u32 vd=pci_rd((u8)b,d,0,0);
if((vd&0xffff)==0xffff) continue;
if((u16)(vd&0xffff)==MT_VID && (u16)(vd>>16)==MT_DID){ g_bus=(u8)b; g_dev=d; g_func=0; return 0; }
}
return -1;
}
/* ===== boot module 固件定位 ===== */
static int fw_find(const char *tag, const u8 **data, u64 *size){
const struct limine_module_response *rsp=(const struct limine_module_response*)g_api->boot_modules_response;
if(!rsp) return -1;
for(u64 i=0;i<rsp->module_count;i++){
struct limine_file *f=rsp->modules[i];
if(!f||!f->cmdline) continue;
const char *a=f->cmdline,*b=tag;
while(*a&&*a==*b){a++;b++;}
if(*a==0&&*b==0){ *data=(const u8*)f->address; *size=f->size; return 0; }
}
return -1;
}
static u32 be32(const u8 *p){ return ((u32)p[0]<<24)|((u32)p[1]<<16)|((u32)p[2]<<8)|p[3]; }
static u32 le32(const u8 *p){ return ((u32)p[3]<<24)|((u32)p[2]<<16)|((u32)p[1]<<8)|p[0]; }
static void memcpy_(void *d,const void *s,u64 n){ u8 *dd=d; const u8 *ss=s; while(n--)*dd++=*ss++; }
static void memset_(void *d,u8 v,u64 n){ u8 *dd=d; while(n--)*dd++=v; }
/* ===== DMA 队列 ===== */
static int q_alloc(struct mt_queue *q,u32 ndesc,u32 ring_base,u32 hw_idx){
struct dkm_dma_buffer buf;
u64 bytes=(u64)ndesc*sizeof(struct mt_desc);
u64 pages=(bytes+4095)/4096;
if(g_api->dma->alloc_pages(pages,4096,0xFFFFFFFFull,&buf)!=0) return -1;
memset_(buf.virt,0,pages*4096);
q->desc=(struct mt_desc*)buf.virt;
q->desc_phys=buf.phys;
q->ndesc=ndesc;
q->head=0;
q->ring_base=ring_base;
q->hw_idx=hw_idx;
return 0;
}
/* 把队列的 desc_base/ring_size 写入硬件 ring 寄存器,并清 cpu/dma idx */
static void q_sync(struct mt_queue *q){
u32 r=q->ring_base+q->hw_idx*MT_RING_SIZE;
bar_wr(r+0x00,(u32)q->desc_phys); /* desc_base */
bar_wr(r+0x04,q->ndesc); /* ring_size */
bar_wr(r+0x08,0); /* cpu_idx */
bar_wr(r+0x0c,0); /* dma_idx (hw) */
q->head=0;
}
static void q_kick(struct mt_queue *q){
__asm__ volatile("sfence":::"memory");
bar_wr(q->ring_base+q->hw_idx*MT_RING_SIZE+0x08,q->head);
}
/* 把一段数据丢进 TX 队列(单段),info=0 */
static int tx_raw(struct mt_queue *q,u64 phys,u32 len){
u32 idx=q->head;
if((q->head+1)%q->ndesc==0 && q->ndesc<=1) return -1;
struct mt_desc *d=&q->desc[idx];
u32 ctrl=((len&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT)|MT_DMA_CTL_LAST_SEC0;
d->buf0=(u32)phys;
d->buf1=0;
d->info=0;
__asm__ volatile("sfence":::"memory");
d->ctrl=ctrl;
q->head=(q->head+1)%q->ndesc;
q_kick(q);
return 0;
}
/* 等待 RX MCU ring 出现一个响应,拷到 out(最多 cap),返回长度;超时 -1 */
static int rx_wait(u8 *out,u32 cap,u32 ms){
u32 r=g_q_rxmcu.ring_base+g_q_rxmcu.hw_idx*MT_RING_SIZE;
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*(u64)ms;
u32 tail=g_q_rxmcu.head;
do {
u32 dma_idx=bar_rr(r+0x0c);
if(dma_idx!=tail){
struct mt_desc *d=&g_q_rxmcu.desc[tail];
u32 ctrl=d->ctrl;
u32 len=(ctrl>>MT_DMA_CTL_SD_LEN0_SHIFT)&0x3fff;
const u8 *src=(const u8*)(g_api->hhdm_offset + d->buf0);
u32 n=len<cap?len:cap;
memcpy_(out,src,n);
/* 回收:重新武装该 desc(重填 buffer 长度) */
d->ctrl=((2048u&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT);
tail=(tail+1)%g_q_rxmcu.ndesc;
g_q_rxmcu.head=tail;
bar_wr(r+0x08,tail); /* cpu_idx 前进,归还 buffer */
return (int)n;
}
__asm__ volatile("pause");
} while(dkm_rdtsc()<dl);
return -1;
}
/* 发一条标准 MCU 命令(带 64 字节 txd 头),payload 在 g_tx_scratch 之后;
* wait=1 时等响应并返回首字节(PATCH_SEM / PATCH_FINISH 用)。 */
static int mcu_send(int cmd_id,int ext_id,const void *payload,u32 plen,int wait,u8 *rbyte){
u8 *buf=(u8*)g_tx_scratch.virt;
u32 hdr=64;
memset_(buf,0,hdr);
u32 total=hdr+plen;
if(total>TX_SCRATCH_SZ) return -2;
memcpy_(buf+hdr,payload,plen);
u8 seq=++g_mcu_seq & 0xf; if(!seq) seq=++g_mcu_seq & 0xf;
u32 *txd=(u32*)buf;
/* txd[0]: TX_BYTES(total) | PKT_FMT(CMD=2)<<23 | Q_IDX(0x20)<<25 */
txd[0]=(total&0xffff)|(2u<<23)|(0x20u<<25);
/* txd[1]: LONG_FORMAT bit31 | HDR_FORMAT(CMD=1)<<16 */
txd[1]=(1u<<31)|(1u<<16);
/* mcu_txd 覆盖在 offset 32 */
u8 *m=buf+32;
u16 mlen=(u16)(total-32);
m[0]=(u8)(mlen&0xff); m[1]=(u8)(mlen>>8); /* len */
u16 pq=(u16)((1u<<15)|(0x20u<<10));
m[2]=(u8)(pq&0xff); m[3]=(u8)(pq>>8); /* pq_id */
m[4]=(u8)cmd_id; /* cid */
m[5]=MCU_PKT_ID; /* pkt_type */
m[6]=ext_id?2:0; /* set_query: SET=2 for ext */
m[7]=seq; /* seq */
m[9]=(u8)ext_id; /* ext_cid */
m[10]=0; /* s2d_index=H2N */
m[11]=ext_id?1:0; /* ext_cid_ack */
if(tx_raw(&g_q_wm,g_tx_scratch.phys,total)!=0) return -3;
if(!wait) { dkm_delay_ms(2); return 0; }
static u8 rbuf[2048];
int n=rx_wait(rbuf,sizeof(rbuf),1000);
if(n<0) return -4;
/* rxd 头 36 字节;seq 在 offset 33(rxd[6]=24, len+pkt=4 →28, eid=28,seq=29) */
if(n>=34){
u8 rseq=rbuf[29];
if(rseq!=seq) { /* 不匹配也不致命,返回 0 继续 */ }
if(rbyte) *rbyte=rbuf[32]; /* PATCH_SEM/FINISH: sizeof(rxd)-4 = 32 */
}
return 0;
}
/* FW_SCATTER:无头,走 FWDL 队列,分片 4096 */
static int fw_scatter(const u8 *data,u32 len){
u8 *scr=(u8*)g_tx_scratch.virt;
while(len>0){
u32 cur=len<FWDL_MAX_LEN?len:FWDL_MAX_LEN;
memcpy_(scr,data,cur);
__asm__ volatile("sfence":::"memory");
if(tx_raw(&g_q_fwdl,g_tx_scratch.phys,cur)!=0) return -1;
/* 等 fwdl dma_idx 追上(简化:轮询 dma_idx==head) */
u32 r=g_q_fwdl.ring_base+g_q_fwdl.hw_idx*MT_RING_SIZE;
u64 dl=dkm_rdtsc()+dkm_tsc_per_ms*200;
while(bar_rr(r+0x0c)!=g_q_fwdl.head && dkm_rdtsc()<dl) __asm__ volatile("pause");
data+=cur; len-=cur;
}
return 0;
}
static int mcu_init_download(u32 addr,u32 len,u32 mode){
struct { u32 addr,len,mode; } req={addr,len,mode};
int cmd = (addr==0x900000)?MCU_CMD_PATCH_START_REQ:MCU_CMD_TARGET_ADDRESS_LEN_REQ;
return mcu_send(cmd,0,&req,sizeof(req),1,0);
}
/* ===== driver own / wfsys reset / dma ===== */
static int drv_own(void){
for(int i=0;i<10;i++){
reg_wr(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_CLR_OWN);
if(poll_reg(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_OWN_SYNC,0,50)==0) return 0;
}
return -1;
}
static int fw_own(void){
for(int i=0;i<10;i++){
reg_wr(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_SET_OWN);
if(poll_reg(MT_CONN_ON_LPCTL,PCIE_LPCR_HOST_OWN_SYNC,PCIE_LPCR_HOST_OWN_SYNC,50)==0) return 0;
}
return -1;
}
static int top_drv_own(void){
reg_wr(MT_TOP_LPCR_HOST_BAND0,MT_TOP_LPCR_HOST_DRV_OWN);
return poll_reg(MT_TOP_LPCR_HOST_BAND0,MT_TOP_LPCR_HOST_FW_OWN,0,500);
}
static int wfsys_reset(void){
reg_clr(MT_WFSYS_RST,WFSYS_SW_RST_B);
dkm_delay_ms(50);
reg_set(MT_WFSYS_RST,WFSYS_SW_RST_B);
return poll_reg(MT_WFSYS_RST,WFSYS_SW_INIT_DONE,WFSYS_SW_INIT_DONE,500);
}
static int dma_disable(int force){
bar_clr(MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN|MT_WFDMA0_GLO_CFG_RX_DMA_EN|
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN|
MT_WFDMA0_GLO_CFG_OMIT_TX_INFO|MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
if(poll_bar(MT_WFDMA0_GLO_CFG,MT_WFDMA0_GLO_CFG_TX_DMA_BUSY|MT_WFDMA0_GLO_CFG_RX_DMA_BUSY,0,100)!=0)
return -1;
bar_clr(MT_WFDMA0_GLO_CFG_EXT0,MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN);
reg_set(MT_DMASHDL_SW_CONTROL,MT_DMASHDL_DMASHDL_BYPASS);
if(force){
bar_clr(MT_WFDMA0_RST,MT_WFDMA0_RST_DMASHDL_ALL_RST|MT_WFDMA0_RST_LOGIC_RST);
bar_set(MT_WFDMA0_RST,MT_WFDMA0_RST_DMASHDL_ALL_RST|MT_WFDMA0_RST_LOGIC_RST);
}
return 0;
}
static int dma_enable(void){
bar_wr(MT_WFDMA0_RST_DTX_PTR,~0u);
bar_wr(MT_WFDMA0_PRI_DLY_INT_CFG0,0);
bar_set(MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_WB_DDONE|MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN|
MT_WFDMA0_GLO_CFG_CLK_GAT_DIS|MT_WFDMA0_GLO_CFG_OMIT_TX_INFO|
(3u<<MT_WFDMA0_GLO_CFG_DMA_SIZE_SHIFT)|MT_WFDMA0_GLO_CFG_RX_WB_DDONE|
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN|MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
bar_set(MT_WFDMA0_GLO_CFG,MT_WFDMA0_GLO_CFG_TX_DMA_EN|MT_WFDMA0_GLO_CFG_RX_DMA_EN);
bar_set(MT_WFDMA_DUMMY_CR,MT_WFDMA_NEED_REINIT);
return 0;
}
/* RX MCU ring 填充 buffer */
static int rx_fill(void){
u64 pages=(RX_NDESC*2048+4095)/4096;
if(g_api->dma->alloc_pages(pages,4096,0xFFFFFFFFull,&g_rx_bufs)!=0) return -1;
memset_(g_rx_bufs.virt,0,pages*4096);
for(u32 i=0;i<RX_NDESC;i++){
struct mt_desc *d=&g_q_rxmcu.desc[i];
u64 bp=g_rx_bufs.phys+(u64)i*2048;
d->buf0=(u32)bp;
d->buf1=0;
d->info=0;
d->ctrl=((2048u&0x3fff)<<MT_DMA_CTL_SD_LEN0_SHIFT);
}
__asm__ volatile("sfence":::"memory");
/* cpu_idx 指到最后,表示全部 buffer 可用 */
u32 r=g_q_rxmcu.ring_base+g_q_rxmcu.hw_idx*MT_RING_SIZE;
bar_wr(r+0x08,RX_NDESC-1);
g_q_rxmcu.head=0;
return 0;
}
static int dma_init(void){
if(dma_disable(1)!=0){ rlog("[mt7920] dma_disable timeout\n"); }
if(q_alloc(&g_q_wm,256,MT_TX_RING_BASE,TXQ_MCU_WM)!=0) return -1;
if(q_alloc(&g_q_fwdl,128,MT_TX_RING_BASE,TXQ_FWDL)!=0) return -1;
if(q_alloc(&g_q_rxmcu,RX_NDESC,MT_RX_EVENT_RING_BASE,RXQ_MCU_WM)!=0) return -1;
q_sync(&g_q_wm);
q_sync(&g_q_fwdl);
q_sync(&g_q_rxmcu);
if(rx_fill()!=0) return -1;
dma_enable();
return 0;
}
/* ===== 固件下载 ===== */
static int load_patch(const u8 *fw,u64 fwsz){
u8 sem=0;
struct { u32 op; } semreq;
semreq.op=PATCH_SEM_GET;
mcu_send(MCU_CMD_PATCH_SEM_CONTROL,0,&semreq,sizeof(semreq),1,&sem);
rhex("[mt7920] patch sem=",sem);
if(sem==PATCH_IS_DL) return 0;
if(sem!=PATCH_NOT_DL_SEM_SUCCESS){ rlog("[mt7920] patch sem fail\n"); }
if(fwsz<sizeof(struct patch_hdr)){ mt_panic("MT7920-E20 PATCH FW TOO SMALL"); return -1; }
const struct patch_hdr *h=(const struct patch_hdr*)fw;
u32 nreg=be32((const u8*)&h->desc.n_region);
rhex("[mt7920] patch regions=",nreg);
int ret=0;
for(u32 i=0;i<nreg;i++){
const struct patch_sec *s=(const struct patch_sec*)(fw+sizeof(struct patch_hdr)+i*sizeof(struct patch_sec));
if((be32((const u8*)&s->type)&PATCH_SEC_TYPE_MASK)!=PATCH_SEC_TYPE_INFO){ mt_panic("MT7920-E21 PATCH SEC TYPE"); ret=-1; break; }
u32 addr=be32((const u8*)&s->addr);
u32 len=be32((const u8*)&s->len);
u32 offs=be32((const u8*)&s->offs);
u32 mode=DL_MODE_NEED_RSP; /* plain,MT7920 patch 非加密段 */
rhex("[mt7920] patch addr=",addr);
rhex("[mt7920] patch len=",len);
if(mcu_init_download(addr,len,mode)!=0){ rlog("[mt7920] init_download fail\n"); ret=-1; break; }
if(fw_scatter(fw+offs,len)!=0){ rlog("[mt7920] scatter fail\n"); ret=-1; break; }
}
/* PATCH_FINISH */
struct { u8 check_crc; u8 rsv[3]; } fin; memset_(&fin,0,sizeof(fin));
mcu_send(MCU_CMD_PATCH_FINISH_REQ,0,&fin,sizeof(fin),1,0);
/* release sem */
semreq.op=PATCH_SEM_RELEASE;
mcu_send(MCU_CMD_PATCH_SEM_CONTROL,0,&semreq,sizeof(semreq),1,&sem);
return ret;
}
static int load_ram(const u8 *fw,u64 fwsz){
if(fwsz<sizeof(struct fw_trailer)){ mt_panic("MT7920-E22 RAM FW TOO SMALL"); return -1; }
const struct fw_trailer *tr=(const struct fw_trailer*)(fw+fwsz-sizeof(struct fw_trailer));
u8 nreg=tr->n_region;
rhex("[mt7920] ram regions=",nreg);
u32 offset=0, override=0, option=0;
for(u8 i=0;i<nreg;i++){
const struct fw_region *rg=(const struct fw_region*)((const u8*)tr-((u32)(nreg-i))*sizeof(struct fw_region));
u32 len=le32((const u8*)&rg->len);
u32 addr=le32((const u8*)&rg->addr);
u32 mode=DL_MODE_NEED_RSP;
if(rg->feature_set&FW_FEATURE_NON_DL){ offset+=len; continue; }
if(rg->feature_set&FW_FEATURE_OVERRIDE_ADDR) override=addr;
rhex("[mt7920] ram addr=",addr);
rhex("[mt7920] ram len=",len);
if(mcu_init_download(addr,len,mode)!=0){ rlog("[mt7920] ram init_download fail\n"); return -1; }
if(fw_scatter(fw+offset,len)!=0){ rlog("[mt7920] ram scatter fail\n"); return -1; }
offset+=len;
}
if(override) option|=FW_START_OVERRIDE;
struct { u32 option,addr; } sreq={option,override};
mcu_send(MCU_CMD_FW_START_REQ,0,&sreq,sizeof(sreq),1,0);
return 0;
}
static int load_firmware(void){
const u8 *patch,*ram; u64 psz,rsz;
if(fw_find("fw:mt7920_patch",&patch,&psz)!=0){ mt_panic("MT7920-E23 PATCH FW MODULE MISSING"); return -1; }
if(fw_find("fw:mt7920_ram",&ram,&rsz)!=0){ mt_panic("MT7920-E24 RAM FW MODULE MISSING"); return -1; }
rhex("[mt7920] patch fw size=",psz);
rhex("[mt7920] ram fw size=",rsz);
/* NIC_POWER_CTRL restart + 等 FW_PWR_ON */
struct { u8 pm; u8 rsv[3]; } pr; memset_(&pr,0,sizeof(pr)); pr.pm=1;
mcu_send(MCU_CMD_NIC_POWER_CTRL,0,&pr,sizeof(pr),0,0);
if(poll_reg(MT_CONN_ON_MISC,MT_TOP_MISC_FW_STATE,MT_TOP_MISC2_FW_PWR_ON,1000)!=0)
rlog("[mt7920] WARN MCU not ready for fw dl\n");
if(load_patch(patch,psz)!=0){ mt_panic("MT7920-E25 PATCH DOWNLOAD FAILED"); return -1; }
rlog("[mt7920] patch downloaded\n");
if(load_ram(ram,rsz)!=0){ mt_panic("MT7920-E26 RAM DOWNLOAD FAILED"); return -1; }
rlog("[mt7920] ram downloaded\n");
if(poll_reg(MT_CONN_ON_MISC,MT_TOP_MISC2_FW_N9_RDY,MT_TOP_MISC2_FW_N9_RDY,1500)!=0){
mt_panic("MT7920-E27 FIRMWARE N9 NOT READY");
return -1;
}
rlog("[mt7920] N9 firmware ready\n");
return 0;
}
/* ===== netdev 回调(扫描/关联未实现,显式 panic) ===== */
static int mt_scan_start(void *c){(void)c;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return -1;}
static int mt_scan_count(void *c){(void)c;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return 0;}
static int mt_scan_result(void *c,u32 n,struct dkm_net_scan_result *o){(void)c;(void)n;(void)o;mt_panic("MT7920-E10 WIFI SCAN NOT IMPLEMENTED");return -1;}
static int mt_connect(void *c,const char *s,const char *p){(void)c;(void)s;(void)p;mt_panic("MT7920-E11 WLAN CONNECT NOT IMPLEMENTED");return -1;}
static int mt_disconnect(void *c){(void)c;mt_panic("MT7920-E12 WLAN DISCONNECT NOT IMPLEMENTED");return -1;}
static int mt_is_wireless(void *c){(void)c;return 1;}
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,struct dkm_driver_handle *handle){
(void)handle;
if(!api||!api->log) return -1;
dkm_tsc_calibrate();
g_api=api; g_log=(struct dkm_log_api*)api->log;
dkm_instr_init(api);
rlog("[mt7920] driver_init entered\n");
if(mt_find()!=0){ rlog("[mt7920] 14c3:7920 not found\n"); return 0; }
rhex("[mt7920] b:d:f=",((u64)g_bus<<16)|((u64)g_dev<<8)|g_func);
u32 cmd=pci_rd(g_bus,g_dev,g_func,0x04);
cmd|=(1u<<1)|(1u<<2);
pci_wr(g_bus,g_dev,g_func,0x04,cmd);
u32 bar0=pci_rd(g_bus,g_dev,g_func,0x10);
if(bar0&1u){ mt_panic("MT7920-E02 BAR0 IS IO"); return -1; }
u64 phys=(u64)(bar0&0xFFFFFFF0u);
if((bar0&0x6u)==0x4u) phys|=((u64)pci_rd(g_bus,g_dev,g_func,0x14)<<32);
rhex("[mt7920] BAR0 phys=",phys);
if(!phys){ mt_panic("MT7920-E02 BAR0 MISSING"); return -1; }
if(!api->mm_map_mmio){ mt_panic("MT7920-E02 MMIO MAP API MISSING"); return -1; }
void *va=api->mm_map_mmio(phys,0x200000); /* 2MB BAR0 窗口 */
if(!va){ mt_panic("MT7920-E02 MMIO MAP FAILED"); return -1; }
g_bar=(volatile u8*)va;
/* 先让固件 own,再取 driver own(对拍 probe 顺序) */
fw_own();
if(drv_own()!=0){ mt_panic("MT7920-E03 DRIVER OWN FAILED"); g_bar=0; return -1; }
rlog("[mt7920] driver own ok\n");
u32 chip=reg_rr(MT_HW_CHIPID);
u32 rev=reg_rr(MT_HW_REV);
rhex("[mt7920] chipid=",chip);
rhex("[mt7920] rev=",rev);
if(wfsys_reset()!=0){ rlog("[mt7920] WARN wfsys reset timeout\n"); }
else rlog("[mt7920] wfsys reset done\n");
bar_wr(MT_WFDMA0_HOST_INT_ENA,0);
bar_wr(MT_PCIE_MAC_INT_ENABLE,0xff);
if(dma_init()!=0){ mt_panic("MT7920-E04 DMA INIT FAILED"); g_bar=0; return -1; }
rlog("[mt7920] dma init ok\n");
/* TX scratch */
if(api->dma->alloc_pages((TX_SCRATCH_SZ+4095)/4096,4096,0xFFFFFFFFull,&g_tx_scratch)!=0){
mt_panic("MT7920-E05 TX SCRATCH ALLOC FAILED");
g_bar=0; return -1;
}
/* MCU own via TOP LPCR,再下载固件 */
if(top_drv_own()!=0){ rlog("[mt7920] WARN top drv own timeout\n"); }
if(load_firmware()!=0){ mt_panic("MT7920-E06 FIRMWARE LOAD FAILED"); g_bar=0; return -1; }
/* 注册 netdev(link 需后续 phy/连接阶段才 up) */
if(api->net&&api->net->register_device){
struct dkm_net_device_desc nd; memset_(&nd,0,sizeof(nd));
nd.name="wlan0";
nd.flags=(1u<<3);
nd.scan_start=mt_scan_start;
nd.scan_count=mt_scan_count;
nd.scan_result=mt_scan_result;
nd.is_wireless=mt_is_wireless;
nd.connect=mt_connect;
nd.disconnect=mt_disconnect;
int idx=api->net->register_device(&nd);
rhex("[mt7920] netdev index=",(u64)(i32)idx);
}
rlog("[mt7920] init done (firmware running; scan/connect pending)\n");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle){ (void)handle; g_bar=0; return 0; }
+1 -1
View File
@@ -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) {
+7 -1
View File
@@ -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) {
+71 -37
View File
@@ -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,11 +583,14 @@ 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) {
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,54 +2064,57 @@ 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);
/* 控制器复位后端口掉电。先全部上电,再等 U 盘重新连接。
* 20ms 就读 CCS 时,实机上的 U 盘还没出现,于是不会注册块设备。 */
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
if (g_ppc && !(portsc & (1u << 9))) {
mmio_write32(port_off, portsc | (1u << 9));
}
}
g_log->info("[xhci] waiting 1500ms for port settle...");
dkm_delay_ms(1500);
g_msc.ready = 0;
int saw_ccs = 0;
int saw_reset_fail = 0;
for (int pass = 0; pass < 2 && !g_msc.ready; pass++) {
if (pass == 1) {
g_log->info("[xhci] second port scan");
dkm_delay_ms(1500);
}
for (u32 port = 0; port < g_max_ports; port++) {
u32 port_off = g_port_base + port * XHCI_PORT_STRIDE + XHCI_PORT_SC;
u32 portsc = mmio_read32(port_off);
/* Always log PORTSC for debugging, even if no device is connected */
log_hex("[xhci] port=", port);
log_hex("[xhci] PORTSC=", portsc);
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);
}
if (!(portsc & PORTSC_CCS)) continue; /* Still no device */
}
if (!(portsc & PORTSC_CCS)) continue;
saw_ccs = 1;
g_log->info("[xhci] device connected");
/* Port Reset */
if (xhci_reset_port(port) != 0) {
/* 严格错误策略插桩:端口复位失败 = 设备不可用,零降级 panic */
g_log->panic("XHC-E01 PORT RESET FAILED");
g_log->warn("[xhci] port reset failed");
saw_reset_fail = 1;
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 */
break;
}
g_log->warn("[xhci] MSC enumeration failed; trying next port");
}
}
if (!g_msc.ready && saw_ccs && xhci_nth >= 3) {
g_log->panic(saw_reset_fail ? "XHC-E01 PORT RESET FAILED"
: "XHC-E02 MSC ENUM FAILED");
}
/* ---- Phase 5: Register Block Device ---- */
if (g_msc.ready) {
@@ -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) {
+39 -2
View File
@@ -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)
+5 -1
View File
@@ -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];
+2
View File
@@ -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) {
+318
View File
@@ -0,0 +1,318 @@
/* ===================================================================
* D6 万物互联:dkm_bus 双向服务总线
*
* "没有主内核,每个模块都是内核——总线就是万物的神经。"
*
* kapi(kernel_api.c)是内核→模块的单向只读导出表;本文件把它双向化:
* 任何 .drv 模块在 driver_init 里注册 srv.* 服务,任何模块按名消费。
* kern.* 仅内核可注册(provider==NULL 时),srv.* 仅模块可注册。
*
* 失败策略(铁律映射):
* register 名字非法/身份不符 → BUS-E01 panic(编程错误)
* register 重名 → BUS-E02 panic
* register 表满 → BUS-E03 panic
* lookup 未命中 → BUS-E04 panic(硬依赖未满足=编程错误)
* unregister 违规 → BUS-E05 panic
* probe 未命中 → 返回 -1(能力缺席=设计内双路径,白名单)
*
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/panic.h>
#include <utsm/idt.h>
#include <utsm/mm.h>
#include <utsm/dma.h>
#include <utsm/drr.h>
#include <utsm/utrw.h>
#include <utsm/block.h>
#include <utsm/pe.h>
#include <utsm/linux_compat.h>
#include "../kernel/ini_parser.h"
/* manifest.c 提供;签名见 dkm.h 头注释(避免头依赖) */
void dsm_load_named(const ini_config *cfg, const char *name);
/* kernel/main.c 提供:FUCK 启动配置只读访问(g_boot_cfg) */
const ini_config *utsm_boot_cfg_get(void);
static dkm_bus_service g_services[DKM_BUS_MAX_SERVICES];
static u32 g_service_count;
static struct dkm_driver_handle *g_current_provider; /* NULL = 内核 */
/* ---- 名字工具 ---- */
static int bus_streq(const char *a, const char *b) {
usize i = 0;
while (a[i] && b[i]) {
if (a[i] != b[i]) return 0;
i++;
}
return a[i] == b[i];
}
static int bus_prefix_of(const char *s, const char *prefix) {
usize i = 0;
while (prefix[i]) {
if (s[i] != prefix[i]) return 0;
i++;
}
return 1;
}
static int bus_find(const char *name) {
for (u32 i = 0; i < g_service_count; i++) {
if (bus_streq(g_services[i].name, name)) return (int)i;
}
return -1;
}
/* ---- 总线原语(BUS-E 全部 panic,零降级) ---- */
static int bus_register_impl(const char *name, u32 version, void *fn) {
if (!name || !fn || !name[0]) {
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: null/empty service name or fn", 0);
return -1;
}
usize len = 0;
while (name[len]) {
if (len >= DKM_BUS_MAX_NAME) {
log_error("[BUS] name too long: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: service name exceeds DKM_BUS_MAX_NAME", 0);
return -1;
}
len++;
}
int is_kern = bus_prefix_of(name, "kern.");
int is_srv = bus_prefix_of(name, "srv.");
if (!is_kern && !is_srv) {
log_error("[BUS] name without namespace prefix: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: service name must start with kern. or srv.", 0);
return -1;
}
if (is_kern && g_current_provider != 0) {
log_error("[BUS] module tried to register kern.*: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: only the kernel may register kern.* services", 0);
return -1;
}
if (is_srv && g_current_provider == 0) {
log_error("[BUS] kernel tried to register srv.*: ");
log_info(name);
panic_full("BUS-E01 INVALID SERVICE NAME",
"dkm_bus: kernel services must use the kern.* namespace", 0);
return -1;
}
if (bus_find(name) >= 0) {
log_error("[BUS] duplicate service: ");
log_info(name);
panic_full("BUS-E02 DUPLICATE SERVICE",
"dkm_bus: service name already registered", 0);
return -1;
}
if (g_service_count >= DKM_BUS_MAX_SERVICES) {
panic_full("BUS-E03 SERVICE TABLE FULL",
"dkm_bus: g_services overflow (DKM_BUS_MAX_SERVICES)", 0);
return -1;
}
dkm_bus_service *s = &g_services[g_service_count];
for (usize i = 0; i < len; i++) s->name[i] = name[i];
s->name[len] = 0;
s->version = version;
s->fn = fn;
/* 存 desc 而非 handle:loader 的 handle 是栈局部(init 返回即悬垂),
* desc 在驱动镜像内存里持久;注销校验按 desc 指针判身份。 */
s->provider = g_current_provider ? g_current_provider->desc : 0;
if (is_kern) {
s->flags = DKM_BUS_F_KERNEL;
} else if (g_current_provider && (g_current_provider->flags & DKM_F_NO_UNLOAD)) {
s->flags = DKM_BUS_F_NO_UNLOAD;
} else {
s->flags = 0;
}
g_service_count++;
log_info("[BUS] service registered: ");
log_info(name);
return 0;
}
static int bus_unregister_impl(const char *name) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) {
log_error("[BUS] unregister unknown service: ");
log_info(name ? name : "(null)");
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: unregister of unknown service", 0);
return -1;
}
dkm_bus_service *s = &g_services[idx];
if (s->flags & (DKM_BUS_F_KERNEL | DKM_BUS_F_NO_UNLOAD)) {
log_error("[BUS] unregister of permanent service: ");
log_info(name);
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: F_KERNEL/F_NO_UNLOAD service cannot be unregistered", 0);
return -1;
}
if (s->provider != (g_current_provider ? g_current_provider->desc : 0)) {
log_error("[BUS] unregister by non-provider: ");
log_info(name);
panic_full("BUS-E05 UNREGISTER VIOLATION",
"dkm_bus: only the provider may unregister its service", 0);
return -1;
}
/* 紧缩(顺序无关紧要,表小) */
for (u32 i = (u32)idx; i + 1 < g_service_count; i++) {
g_services[i] = g_services[i + 1];
}
g_service_count--;
log_info("[BUS] service unregistered: ");
log_info(name);
return 0;
}
static int bus_lookup_impl(const char *name, void **out_fn, u32 *out_version) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) {
log_error("[BUS] lookup miss: ");
log_info(name ? name : "(null)");
panic_full("BUS-E04 LOOKUP MISS",
"dkm_bus: required service not registered (hard dependency)", 0);
return -1;
}
if (out_fn) *out_fn = g_services[idx].fn;
if (out_version) *out_version = g_services[idx].version;
return 0;
}
static int bus_probe_impl(const char *name, void **out_fn, u32 *out_version) {
int idx = name ? bus_find(name) : -1;
if (idx < 0) return -1;
if (out_fn) *out_fn = g_services[idx].fn;
if (out_version) *out_version = g_services[idx].version;
return 0;
}
static u32 bus_count_impl(void) {
return g_service_count;
}
static const dkm_bus_service *bus_at_impl(u32 index) {
if (index >= g_service_count) return 0;
return &g_services[index];
}
static const dkm_bus_api g_bus_api = {
.magic = DKM_BUS_MAGIC,
.version = DKM_BUS_VERSION,
.register_service = bus_register_impl,
.unregister_service = bus_unregister_impl,
.lookup = bus_lookup_impl,
.probe = bus_probe_impl,
.count = bus_count_impl,
.at = bus_at_impl,
};
const dkm_bus_api *dkm_bus_get_api(void) {
return &g_bus_api;
}
void dkm_bus_set_current_provider(struct dkm_driver_handle *h) {
g_current_provider = h;
}
int dkm_bus_prefix_registered(const char *prefix) {
if (!prefix) return 0;
usize len = 0;
while (prefix[len]) len++;
for (u32 i = 0; i < g_service_count; i++) {
const char *n = g_services[i].name;
if (bus_streq(n, prefix)) return 1;
/* 前缀命中:n == "prefix.xxx" */
if (bus_prefix_of(n, prefix) && n[len] == '.') return 1;
}
return 0;
}
/* ===================================================================
* kern.* 播种
*
* kapi 14 条(log.* dma/irq/mm/apic/drr/utrw)以 kern. 前缀重新播种
* (旧无前缀 kapi 名冻结不动,兼容层);另加 block/pe/lxc/probe/config/
* load_by_name。dsk_boot_context.reserved[] 的逐槽替代服务(srv.*)随
* P2-P4 各模块落地注册,本表不抢跑。
* kern.probe.info 随 P4b(shell 消费方)落地;kern.bootlog.milestone
* 随 P2(fat32.drv 实装)落地。
* =================================================================== */
static void bus_kern_panic(const char *msg) {
panic_full("DKM-PANIC", msg, 0);
}
static int bus_kern_load_by_name(const char *name) {
/* P3 接线后带 FUCK cfg 过滤;当前调用方(内部)走 manifest 语义。 */
dsm_load_named(0, name);
return 0;
}
static const char *bus_kern_config_get(const char *section, const char *key) {
const ini_config *cfg = utsm_boot_cfg_get();
if (!cfg) return 0;
return ini_get(cfg, section, key);
}
static int bus_seed(const char *name, void *fn) {
return bus_register_impl(name, 1u, fn);
}
void dkm_bus_init(void) {
static int s_inited = 0;
if (s_inited) return;
s_inited = 1;
g_service_count = 0;
g_current_provider = 0;
/* log(kapi 4 条迁移) */
bus_seed("kern.log.info", (void *)log_info);
bus_seed("kern.log.warn", (void *)log_warn);
bus_seed("kern.log.error", (void *)log_error);
bus_seed("kern.log.panic", (void *)bus_kern_panic);
/* dma / irq / mm / apic(kapi 迁移 + P6 真 IRQ 输入新增 unmask/mask) */
bus_seed("kern.dma.alloc_pages", (void *)dma_alloc_pages);
bus_seed("kern.irq.register", (void *)irq_register);
bus_seed("kern.irq.vector_alloc", (void *)irq_vector_alloc);
bus_seed("kern.irq.vector_free", (void *)irq_vector_free);
bus_seed("kern.irq.unmask", (void *)pic_irq_unmask);
bus_seed("kern.irq.mask", (void *)pic_irq_mask);
bus_seed("kern.mm.map_mmio", (void *)mm_map_mmio);
bus_seed("kern.mm.unmap_mmio", (void *)mm_unmap_mmio);
bus_seed("kern.apic.register_eoi", (void *)idt_register_apic_eoi);
/* drr / utrw(kapi 迁移) */
bus_seed("kern.drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty);
bus_seed("kern.drr.rollback_pages", (void *)drr_rollback_pages);
bus_seed("kern.utrw.debug_dump", (void *)utrw_debug_dump_page);
/* block registry 前端(替代 desktop 的 api+0xA8 裸偏移读) */
{
const dkm_block_api *b = block_get_api();
bus_seed("kern.block.read", (void *)b->read);
bus_seed("kern.block.write", (void *)b->write);
}
/* PE / Linux 兼容层服务(替代 reserved[4]/reserved[5]) */
bus_seed("kern.pe.service", (void *)pe_get_service);
bus_seed("kern.lxc.service", (void *)linux_compat_get_service);
/* 按名加载(session.drv 的排序拉手,P3 接 FUCK 过滤) */
bus_seed("kern.dkm.load_by_name", (void *)bus_kern_load_by_name);
/* FUCK 配置只读访问(替代 reserved[] 内散落的配置回传) */
bus_seed("kern.config.get", (void *)bus_kern_config_get);
log_info("[BUS] ready services=");
log_hex64("[BUS] count=", g_service_count);
}
+9 -5
View File
@@ -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();
+193 -4
View File
@@ -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,28 +463,48 @@ 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++) {
if (!stage->drivers[di].resolved) pending++;
}
while (pending > 0) {
u32 pending_before = pending;
for (u32 di = 0; di < stage->driver_count; di++) {
dsm_driver_entry *drv = &stage->drivers[di];
struct limine_file *f;
struct dkm_symbol_scan symbols;
int result;
if (drv->loaded) continue;
if (drv->resolved) continue;
/* === FUCK [drivers] 驱动开关检查 === */
/* === 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",
@@ -386,6 +524,8 @@ static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max
panic_full("DKM-E05 REQUIRED DRIVER ELF INVALID",
"required driver boot module failed ELF64 check", 0);
}
drv->resolved = 1;
pending--;
continue;
}
@@ -395,6 +535,8 @@ static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max
panic_full("DKM-E06 REQUIRED DRIVER SYMBOL SCAN FAILED",
"required driver symbol scan failed", 0);
}
drv->resolved = 1;
pending--;
continue;
}
@@ -405,10 +547,31 @@ static void dsm_load_stage_range(const ini_config *cfg, int limit_stage, u32 max
/* 严格错误策略插桩:required 驱动加载失败零降级(DKM-E07) */
panic_full("DKM-E07 REQUIRED DRIVER LOAD FAILED",
"required driver relocation/load failed", 0);
break;
}
drv->resolved = 1;
pending--;
} else {
drv->loaded = 1;
drv->resolved = 1;
pending--;
/* P0:加载成功才登记 provides(disabled/missing/failed 一律不登记) */
dsm_register_provides(drv);
/* P1:stage>=4 provides 总线契约校验(DKM-E45) */
dsm_verify_provides(stage, drv);
}
}
if (pending > 0 && pending == pending_before) {
/* 一整趟零进展:真缺依赖(DKM-E44)或依赖环(DKM-E47) */
dsm_report_dependency_stall(stage);
/* fallback:停滞的候选全部放弃(不登记 provides),结束本 stage */
for (u32 di = 0; di < stage->driver_count; di++) {
if (stage->drivers[di].resolved) continue;
log_warn("[DSM] fallback: driver skipped (unresolved deps): ");
log_info(stage->drivers[di].name);
stage->drivers[di].resolved = 1;
}
pending = 0;
}
}
log_info("[DSM] stage end");
@@ -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);
}
+1
View File
@@ -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);
+69
View File
@@ -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.<mod>.run)。
* 设计定稿:docs/RE/万物互联与服务总线设计.md §1。
* =================================================================== */
#define DKM_BUS_MAGIC 0x53425553u /* "SBUS" */
#define DKM_BUS_VERSION 1u
#define DKM_BUS_MAX_SERVICES 128
#define DKM_BUS_MAX_NAME 48
#define DKM_BUS_F_KERNEL 1u /* 内核注册,不可注销 */
#define DKM_BUS_F_NO_UNLOAD 2u /* provider 声明 NO_UNLOAD,注销即 BUS-E05 */
typedef struct dkm_bus_service {
char name[DKM_BUS_MAX_NAME]; /* NUL 终止;必须带命名空间前缀 */
u32 version; /* 服务契约版本,消费方校验 */
u32 flags; /* DKM_BUS_F_* */
void *fn; /* 服务入口,签名由各服务契约定义 */
const struct dkm_driver_desc *provider; /* NULL = 内核;存 desc 而非
* handle——loader 的 handle 是栈
* 局部变量会悬垂,desc 在驱动
* 镜像内存里持久且每驱动唯一 */
} dkm_bus_service;
typedef struct dkm_bus_api {
u32 magic;
u32 version;
/* 注册:name 必须带命名空间前缀且与调用方身份匹配(BUS-E01)、
* 重名 BUS-E02、表满 BUS-E03——全部 panic,成功返回 0。 */
int (*register_service)(const char *name, u32 version, void *fn);
/* 注销:仅 provider 本人可注销;F_KERNEL/F_NO_UNLOAD 或不存在 → BUS-E05。 */
int (*unregister_service)(const char *name);
/* 必存在查找:未命中 → BUS-E04 panic,绝不返回 NULL 伪装成功。 */
int (*lookup)(const char *name, void **out_fn, u32 *out_version);
/* 可选探测:未命中返回 -1;调用方必须显式处理缺席(白名单登记)。 */
int (*probe)(const char *name, void **out_fn, u32 *out_version);
u32 (*count)(void); /* 诊断 / /dev/services */
const dkm_bus_service *(*at)(u32 index); /* 按 index 枚举 */
} dkm_bus_api;
typedef struct dkm_driver_desc {
u32 magic;
u16 abi_version;
@@ -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;
+3
View File
@@ -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 */
+174 -1
View File
@@ -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;
+8
View File
@@ -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
+24 -1
View File
@@ -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);
+2
View File
@@ -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 显式豁免(设计内白名单),放行 */
+1 -1
View File
@@ -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);
}
}
+1
View File
@@ -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;
+53 -21
View File
@@ -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,9 +415,12 @@ 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");
}
}
INSTR_TS_DECL(ts_dkm);
INSTR_TS_BEGIN(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) {
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) {
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);
}
+6 -2
View File
@@ -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);
}
+96 -29
View File
@@ -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);
}
}
}
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 - 24, dim);
(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) {
+46
View File
@@ -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 竞态保护) ==== */
{
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+339 -20
View File
@@ -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){
{
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,
1 /* user_desktop:桌面只显示 .lnk */
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:<name> → 已注册内置应用(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:<name> → 绑定已注册应用 */
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 快捷方式 */
+62
View File
@@ -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)
* ============================================================ */
+6
View File
@@ -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);
+54 -1
View File
@@ -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);
+4
View File
@@ -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 调试:定位失败步骤 */
+2 -1
View File
@@ -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);
+586
View File
@@ -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);
}
+32 -11
View File
@@ -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;
+34 -6
View File
@@ -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<nn>)。
* ============================================================ */
/* 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 计时刷新 */
+524
View File
@@ -0,0 +1,524 @@
/* prank.c — Deshab 整蛊系统("致力于打造极差的用户体验")
*
* 设计目标:在用户正常使用桌面时,不定时播放一段"恶作剧"叠加层,
* 制造"这系统是不是坏了"的错觉,随后若无其事地消失。
*
* 已实现整蛊(全部由 FUCK [prank] 段控制,见 build/configs/<variant>/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;
}
}
+6
View File
@@ -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);
+394
View File
@@ -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;
}
+345 -8
View File
@@ -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)) {
+7 -4
View File
@@ -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) {
+269 -30
View File
@@ -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];
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);
}
}
}
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;
}
fatalf("FS-E24 EXT4 PARTITION NOT FOUND IN GPT");
/* 机器里那份 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);
+7
View File
@@ -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);
+83 -32
View File
@@ -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<nn>(文件系统层)。
* =================================================================== */
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,
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 + g_ascii_h + 16,
((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,10 +2209,10 @@ 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;
}
} else {
logl("[DSK] FirstInit returned, persisting user.conf");
dsk_persist_userconf(ctx);
logh("[DSK:STAT] fat32_read_count=", (u64)fat32_read_count);
@@ -2179,6 +2227,7 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
} else if (!dsk_run_login(ctx)) {
dsk_fatal("FS-E06 LOGIN REQUIRED AFTER FIRSTINIT");
}
}
logl("[DSK] loading desktop");
milestone_write(6, "desktop loading");
@@ -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");
+132 -70
View File
@@ -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<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 = ((u32)a * global_alpha) >> 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<h; r++) {
@@ -236,21 +211,33 @@ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y,
* 整屏显示错误码后停机。 */
#include "../login/ohmylogo_data.c"
static u64 fi_fat_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color);
/* DSK_BOOT_FLAG_PANIC_FALLBACK:firstinit_fatal 只留串口坐标后返回,调用点走备用路径 */
static int g_panic_fallback;
static void firstinit_fatal(const char *sym) {
logl(sym);
if (g_panic_fallback) {
logl("[FirstInit: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, 0xFF020F22u);
i64 lx = ((i64)fb_w - g_panic_logo_w) / 2;
i64 ly = ((i64)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 text_need = (ASCII_H + 6) * 4;
i64 step = 1;
while (step < 8 &&
(i64)g_panic_logo_h / step + text_need + 24 > (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);
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 + ASCII_H + 16, 0xFFB08890u, 0);
((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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 color = bg_at_y(yy);
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] = 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");
+35 -1
View File
@@ -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
+33 -2
View File
@@ -137,8 +137,15 @@ static u64 lf_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
* 必须整屏 fatal 显示错误码后停机。"疯但好使" 的反面保障:
* 错也错得有落款。每个插桩点一个唯一 symbol(LOGIN-E<nn>)。
* =================================================================== */
/* 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; r<h; r++) {
i64 yy = y + r;
@@ -609,7 +633,7 @@ static void draw_login_card(u32 *fb, i64 card_x, i64 card_y, const char *pass, i
u32 sub_fg = 0xFF9098A0;
/* Background + card */
cursor_restore_bg(fb);
fill_gradient_rect(fb, card_x - 24, card_y - 24, CARD_W + 48, CARD_H + 48);
restore_wallpaper_rect(fb, card_x - 24, card_y - 24, CARD_W + 48, CARD_H + 48);
draw_card(fb, card_x, card_y, CARD_W, CARD_H, card);
/* Title */
fb_text(fb, "Deshab Login", card_x + 40, card_y + 40, fg, card);
@@ -805,8 +829,11 @@ __attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[login] boot");
g_panic_fallback = (ctx && ctx->magic == 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");
+41
View File
@@ -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.<mod>.run`,由会话排序者(session.drv)按总线调用拉起。
完整设计见 [docs/RE/万物互联与服务总线设计.md](../../../docs/RE/万物互联与服务总线设计.md)。
---
## 5. 头文件索引
+1
View File
@@ -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
+71
View File
@@ -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 字段偏移常量(消除散落的硬编码偏移) ----
+25
View File
@@ -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 }"
+26
View File
@@ -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
}
+253
View File
@@ -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");
}
}
+14 -9
View File
@@ -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;
+60
View File
@@ -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 <g++路径>]
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)"
+109
View File
@@ -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);
}
+117
View File
@@ -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);
}
+136
View File
@@ -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);
}
+136
View File
@@ -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 <windows.h>
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;
}
+341
View File
@@ -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 <windows.h>
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 */
+121
View File
@@ -0,0 +1,121 @@
/* portio.cpp — Deshab 端口读写工具(控制台 PE64,diskpart 风格硬件调试)
*
* 用法(在 CMD 里运行,输出走串口):
* portio <port> 读一字节(十六进制端口)
* portio <port> <value> 写一字节
* portio 用法帮助
*
* 例:
* portio 70 → 读 CMOS 地址口
* portio 70 0 → CMOS 寄存器 0(秒)
* portio 71 → 读出秒(BCD)
*
* Ring0 原生执行,IOPL 无限制。危险提示:写错端口可能挂机。
*/
#include <windows.h>
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 <port> [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;
}
+128
View File
@@ -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);
}
+12 -9
View File
@@ -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
)
+14 -5
View File
@@ -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
+9 -3
View File
@@ -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) | 疯圣典:矛盾美学与「疯但好使」物理学 |
+29
View File
@@ -0,0 +1,29 @@
# Deshab 开始菜单注册表(desktop 启动时解析,FAT32 根目录)
# 格式: NAME|显示名|path|args ('args' 可省略)
#
# path 四种:
# builtin:<name> → 已注册内置应用(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
Binary file not shown.
Binary file not shown.
+8
View File
@@ -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",
+6
View File
@@ -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
+34
View File
@@ -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_<name>=1(默认)而能力未就绪 → 启动即莲花屏 panic。
# require_<name>=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
-4
View File
@@ -1,4 +0,0 @@
编辑器
EDITOR ELF
/system/deshab64/tools/editor.elf
editor
+14 -6
View File
@@ -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
}
+34
View File
@@ -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_<name>=1(默认)而能力未就绪 → 启动即莲花屏 panic。
# require_<name>=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
+167
View File
@@ -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_<name>=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
+2
View File
@@ -0,0 +1,2 @@
1
0
+12 -2
View File
@@ -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_<name>=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 跟随鼠标后消失
@@ -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.<mod>.run`)。这回答了"desktop 什么时候 driver_init"——在 UTSM 阶段、任何 UI 出现之前;init 只注册不运行;运行由排序者按总线调用发起。
loader 单线程不变式:session.drv 的 `kern.dkm.load_by_name` 在 stage 循环外被调(顺序执行 + loaded 标志防重入),总线无锁。任何未来并发加载都是契约违规。
---
## 2. 跨模块状态迁移表
现状真正跨模块耦合只有 `dsk_boot_context`(desktop.h 的 extern 全局是 desktop 单模块内多 TU 链接,`ld -r` 后原样工作)。逐槽消灭:
| 现状耦合通道 | 总线化后 | 提供方 |
|---|---|---|
| desktop `*(u64*)(api+0xA8)` 裸偏移读 block | `api->block` 字段直读(driver_init 拿到真 api 指针) | UTSM |
| `reserved[4]` pe_service | `kern.pe.service` | UTSM pe.c |
| `reserved[5]` lxc service | `kern.lxc.service` | UTSM linux_compat.c |
| `reserved[6]` probe_info | `kern.probe.info` | UTSM probe.c |
| `reserved[7]` net_lease | `srv.net.lease`(fn 返回 const net_lease_info*) | netman.drv |
| `reserved[0/1]` userconf 缓冲 | `srv.user.config`(ptr/size/状态打包结构) | firstinit.drv |
| `reserved[2]` login 成功标志 | `srv.auth.result` | login.drv |
| `reserved[3]` dev_mode | `srv.session.dev_mode` | session.drv |
| `g_mouse_state` / `mouse_poll_state()` | `srv.input.mouse.poll`(沿用 mouse_state_t magic/version 契约) | ps2mouse.drv |
| DSK 自带 FAT32 只读解析 + 写路径 | `srv.fs.fat32.read_path` / `srv.fs.fat32.write_file` / `srv.fs.fat32.bootlog` | fat32.drv(实装) |
| DSK 自带 ext4.c | `srv.fs.ext4.*` | ext4.drv(新) |
| DSK PIE ELF loader | (消灭)app 全变 .drv,统一走 UTSM DKM ET_REL loader | UTSM |
| dsk_fatal 莲花屏 | `api->log->panic`(内核 F1 莲花屏,视觉本就同源) | UTSM |
| dsk_boot_context 整体 | (消灭)P5 删除 dsk.h 中的定义 | — |
---
## 3. DSK 的命运:溶解
终态**完全消灭** deshab.elf。不保留"会话协调器特权模块"——会话排序逻辑移植为 session.drv,一个普通的 stage4 .drv,无任何特殊待遇。
迁移期(P2-P4)DSK 先降格为纯总线客户端(调度调用从"自带 FAT32 + PIE loader"改成 bus 调用),P5 删除二进制、dsk_loader.c 交接代码与 dsk_boot_context。
职责归位地图:
| DSK 职责 | 归宿 |
|---|---|
| FAT32 只读解析 + 写路径 + bootlog | fat32.drv 实装(母本:dsk_loader.c 的 fat32_read_path/write_file_in_dir 全套) |
| ext4.c 读写 | ext4.drv(整文件移植,dsk_fatal → g_log->panic) |
| PIE ELF loader + 8MB ibuf | 消灭(app 全变 .drv ET_REL;DKM-E70 ibuf 雷区整体绕开) |
| 静态 Logo / splash | session.drv |
| FUCK INI 读取 | 单一事实源 = Limine boot module(cmdline="fuck:config")+ session.drv 自带 INI 解析副本 |
| firstInit.txt 检测 | session.drv(经 srv.fs.fat32.read_path) |
| 模块调度(mouseInit/FirstInit/netman/login/shell/desktop/cmd) | 各自变 stage4 .drv;session.drv 用 kern.dkm.load_by_name 按需拉起 + bus 调 srv.*.run |
| BOOT01-07.TXT 里程碑 | `kern.bootlog.milestone` / srv.fs.fat32.bootlog |
| 会话选择(native/kde/cmd)+ 登录强制 | session.drv(登录失败 → SESSION-E02 panic,沿用 FS-E06 语义) |
| dsk_boot_context | 消灭 |
**P2-P5 之间的双 FAT32 实现是受控临时债**:deshab.elf 自身装载仍走内核侧 FAT32(`dsk_load_from_block_fat32`),直到 P5 删除。窗口期内禁止"顺手"修两边分叉逻辑。
---
## 4. 落地路线(P0-P6)
每 phase QEMU 可启动(build.bat -Variant dev → run_qemu.bat,ahci0 SATA + TCG + 串口 stdio),证据日志落 `.build_tmp/`。
### P0 — DSM 依赖图强制
`dkm/manifest.c`:capability 集合 + 不动点排序 + depends 校验(`DKM-E44`)。
验收:正常启动串口与基线逐行一致;负向测试(假 depends 条目)→ DKM-E44 莲花屏。
### P1 — 服务总线落地
`dkm/bus.c`(新)+ dkm.h 尾部追加 + provider 上下文 + `dsm_verify_provides`(DKM-E45)+ attest 翻绿 + selftest 用例(TST-E25..E28)+ devfs `/dev/services`。
验收:`[BUS] ready services=N`;`[TST] bus selftest PASS`;老 14 驱动不变。
### P2 — 文件系统归位
fat32.drv 实装(srv.fs.fat32.*)+ ext4.drv(srv.fs.ext4.*)+ DSK/UTSM 消费侧切总线。
验收:`[fat32] srv.fs.fat32.read_path registered`、`[ext4] selftest PASS`、首次启动全流程不回归。
### P3 — 加载器 FS 化 + stage4 + ps2mouse.drv 试点
DSM_MAX_STAGES 4→6 / PER_STAGE 8→12;`dsm_read_module_fs`(boot module 优先 / FS 回退 / `DKM-E46`);`kern.dkm.load_by_name` 接通;ps2mouse.drv(srv.input.mouse.poll);manifest 新增 stage4 "session";DSK 删 mouseInit 调度块。
验收:`[DSM] stage 4 session begin` → `[ps2mouse] driver ready`。
### P4 — 应用模块 DKM 化(4a-4f)
统一配方:① `dsk_entry(ctx)` → `driver_init(api, handle)` + driver_desc(stage=4、NO_UNLOAD);② 原 entry 主体改 `static <mod>_run(void)` 注册 `srv.<mod>.run`;③ ctx 消费点按 §2 迁移表替换;④ 自带 fatal → `api->log->panic`;⑤ 构建:PIE linker.ld 弃用,`clang -c + lld -r` 产出到 `SYSTEM/driver/session/`,**每模块必跑 `llvm-readobj --relocations` 核对 ∈ {R_X86_64_64/32/32S/PC32/PLT32}**;⑥ DSK 同 commit 删对应调度块改 bus 调 run。
子步:4a netman → 4b shell → 4c firstInit(user.conf 写盘收编进模块,reserved[0/1] 往返删除)→ 4d login → 4e desktop(~20 TU,最大风险点;host SDL 构建 #ifdef 隔离)→ 4f cmd + 收尾。
### P5 — DSK 退役 + session.drv
session.drv(排序骨架移植)+ 删 dsk_loader.c / dsk_boot_context / CODE/dsk/ + main.c 交接改 `dsm_load_by_manifest_upto(cfg, 4)`(quiesce 保留——运行时行为与 DSK 时代逐字节对齐,无回归)+ CLAUDE.md 全面更新。
验收:串口无 `[DSK] boot`;`[session] stage4 sequencing begin` → 全流程正常;dev/release 双过。
### P6 — 常驻调度 + 事件循环 + 真 IRQ 输入
sched_enable 常驻 + bootstrap park;desktop 任务化(task_create + 每帧 srv.input.*.poll + sleep_ms);ps2kbd 事件环形缓冲 + srv.input.kbd.poll;ps2mouse IRQ12 解屏蔽(kern.irq.unmask);firstInit/login 键盘轮询 → kbd.poll(强制:IRQ 驱动后 handler 读走 0x60,轮询端口会饿死)。
验收:`[SCHED] enable` 常驻;QEMU QMP sendkey/鼠标事件到达 desktop;`[desktop] task slot=N`。
### 收尾(并入 P6)
kapi 静态表退役(总线唯一注册源);block/net 注册表前端化(消费者切 `kern.block.*`);STUB grep 基线不抬升;desktop 任务 DRR 看门狗 register/kick(可选)。
---
## 5. 错误码登记
| 码 | 含义 | 触发点 |
|---|---|---|
| BUS-E01 | 服务名非法(无命名空间 / 模块注册 kern.* / 超长) | register_service |
| BUS-E02 | 服务重名 | register_service |
| BUS-E03 | 服务表满(DKM_BUS_MAX_SERVICES) | register_service |
| BUS-E04 | lookup 未命中(硬依赖未满足) | lookup |
| BUS-E05 | 注销违规(非 provider / F_NO_UNLOAD) | unregister_service |
| DKM-E44 | manifest depends 未满足 | dsm_load_stage_range / dsm_load_named |
| DKM-E45 | stage≥4 provides 未注册到总线 | dsm_verify_provides |
| DKM-E46 | 模块 boot module 与 FS 双源皆无 | dsm_load_stage_range |
| DKM-E47 | 依赖图成环 | 不动点排序 |
| DKM-E48 | capability 表满(DSM_MAX_CAPS) | dsm_capability_add |
| SESSION-E01 | session.drv 初始化失败(FUCK 缺失等) | session.drv |
| SESSION-E02 | 登录强制失败(沿用 FS-E06 语义) | session.drv |
| SESSION-E03 | 会话类型非法/加载失败 | session.drv |
| TST-E34..E38 | 总线 selftest 用例(注册/命中/probe/重名/注销违规;TST-E01..E33 已被既有用例占用) | utsm_selftest.c |
全部零降级:panic 莲花屏。
## 6. 设计内白名单(probe 型缺席 + 顺序豁免,CLAUDE.md 同步登记)
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 内。
---
> "没有主内核,每个模块都是内核——总线就是万物的神经。疯但互联。" 🪷
+39
View File
@@ -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 <mod>_run(void)` 并注册 `srv.<mod>.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. 第一版定稿
第一版驱动系统确定:
+4 -2
View File
@@ -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 做完:连"用户"这个概念都消失——万物皆内核,互联皆总线。**
---
+18
View File
@@ -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)
+19 -1
View File
@@ -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 + 原子切换)