Files
Deshab-OS/docs/上下文/260729/Deshab Linux_EXE 兼容层开发.md
2026-08-05 13:58:13 +08:00

266 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deshab Linux/EXE 兼容层开发
## 架构总览
```
桌面命令行1: shell.elf (原生系统命令行 + linux 命令委托执行)
桌面命令行2: cmd.elf (Windows 风格命令行 + PE/EXE 兼容)
```
三个垂直层次:
1. **虚拟化底座**:UTSM 作为 Type-1 hypervisor,VMX/EPT 完整虚拟化,park-and-resume 机制让 Linux guest 与 DSK 时分复用 CPU
2. **双兼容引擎**:Linux 兼容路径(IPC 委托 Linux guest 执行)+ PE/EXE 兼容路径(UTSM 内置 PE loader + x86emu32 解释器)
3. **用户界面与文件系统**:shell.elf / cmd.elf / desktop.elf / FAT32 / initramfs+virtio-blk
## 一、PE/EXE 兼容层(UTSM 内置,`CODE/UTSM/pe/`)
### 1. pe_service.c / pe.h — 统一服务入口
- 全局 service table(函数指针表):`pe_load()` / `pe_run()` / `pe_unload()`
- PE32+ → preferred_base 加载,Ring0 原生执行(MS x64 ABI)
- PE32 → 专用大缓冲区,x86emu32 解释执行
- 通过 `dsk_boot_context.reserved[4]` 暴露给 DSK
### 2. pe_loader.c — PE 解析/映射/重定位
- DOS Header → PE Header → Section Table 解析
- PE32+:`__attribute__((ms_abi))` shim,原生 Ring0 执行
- PE32:无需重定位,直接 preferred_base 加载
- `apply_relocations(image_size)` — base relocation 处理
- `pe_get_iat_entries()` — 暴露 IAT 给 shim 注册
- import 解析 → 查 shim 表 → 填充 IAT
### 3. pe_shim.c / pe_shim.h — Windows API 拦截
- IAT 拦截:按 DLL!Function 名查 shim 表填充函数指针
- shim 函数签名用 `__attribute__((ms_abi))`
- `__builtin_setjmp`/`__builtin_longjmp`(`void **`)用于 PE 入口异常恢复
- 每条 IAT 条目可注册 `fn32`(PE32)和 `fn64`(PE32+),`try_iat_call` 在 fn32=NULL 时回退到 fn64
### 4. x86emu32.c / x86emu32.h — x86-32 指令解释器
- 支持指令:MOV/ADD/SUB/AND/OR/XOR/CMP/TEST, SHL/SHR/SAR(含 CL 变长), PUSH/POP, JMP/Jcc/CALL/RET, MOVSX/MOVZX, rep_prefix, ModR/M+SIB 解码
- `emu->mem` 指向大缓冲区起始(虚拟地址 0),`mem[preferred_base + rva]` 正确寻址
- `try_iat_call` — 调用 IAT 中的 shim 函数
### cmd.elf(`CODE/cmd/main.c`)
- Windows cmd.exe 风格命令行,从 `reserved[4]` 读取 `pe_svc`
- 内置命令:dir, cd, type, copy, del, exit 等
- FAT32 I/O 通过 `f32_list_root()` 回调 API
- Esc 键返回 DSK,支持从 FAT32 root 加载 PE 文件执行
## 二、Linux 兼容层(双内核 park-and-resume)
### GPA 布局
| GPA 区间 | 用途 |
|---------|------|
| `0x00400000 - 0x02FFFFFF` | initrd / boot_params |
| `0x03000000 - 0x03FFFFFF` | Linux guest initrd |
| `0x04000000` | IPC 共享内存(1MB) |
| `0x05000000+` | Linux guest RAM(64MB) |
| `0x01000000` | Kernel 映射 |
| `0x00600000` | GDT |
### VMCS / VMEXIT 处理
- **vmexit_asm.S**:保存 16 个 guest GPR 到 `g_guest_regs`,调用 C `vmexit_dispatch()`
- dispatch 返回 0 → `vmresume`;返回非 0 → 恢复 host RSP/返回 RIP
- `g_saved_host_rsp` / `g_saved_return_rip` 全局变量(vmm.h 声明,vmm.c 定义)
**已实现 exit reason**:
| Exit Reason | 处理 |
|-------------|------|
| EXIT_HLT | park-and-resume:self-test 终止,Linux guest park |
| EXIT_EPT_VIOLATION | 按需分配 HPA,区分 RAM/MMIO |
| EXIT_CPUID | host passthrough + 屏蔽 VMX 位 |
| EXIT_IO_INSTRUCTION | COM1/PIC/PIT 模拟 |
| EXIT_RDMSR/WRMSR | MSR 读写处理 |
| EXIT_VMCALL | 9+ hypercall 分发 |
| EXIT_INTERRUPT_WINDOW | guest 开中断时注入 pending IRQ |
| EXIT_VMX_PREEMPTION_TIMER | 1ms 周期 exit |
### park-and-resume 机制
- `handle_hlt` 区分 self-test(终止)和 Linux guest(park)
- Linux guest HLT → 推进 guest RIP → `g_guest_parked = 1` → 返回非 0 → asm 恢复 host 上下文
- `linux_is_parked()` 查询状态,`linux_compat_exec()` 调用 `linux_resume()` 唤醒
- `linux_launch()` 成功后设 `g_guest_parked = 1`
### IPC 协议
**Hypercall ABI**:guest RAX = 操作码,RDI/RSI/RDX = 参数,返回值写入 guest RAX
- VMCALL 魔数 `0x5554534D48430000`("UTSMHC\0\0")
- 9 种基础 op:PING/HELLO/SHM_INFO/CAP_VALIDATE/UTRW_READ/UTRW_WRITE/DRR_CHECKPOINT/CONSOLE_WRITE/CONSOLE_READ
**IPC EXEC 协议**(消息号 16-20):
| 消息号 | 常量 | 用途 |
|-------|------|------|
| 16 | UTSM_MSG_EXEC_REQUEST | shell 请求 Linux 执行命令 |
| 17 | UTSM_MSG_EXEC_STDOUT | daemon 回传 stdout |
| 18 | UTSM_MSG_EXEC_STDERR | daemon 回传 stderr |
| 19 | UTSM_MSG_EXEC_EXIT | daemon 回传退出码 |
| 20 | UTSM_MSG_EXEC_READY | daemon 就绪通知 |
`ipc_exec_request` 结构体约束 ≤240 字节(适配 IPC 单消息槽)。
### linux_compat 服务(`CODE/UTSM/vmm/linux_compat.c`)
- `exec()` 实现:构建 `ipc_exec_request` → `ipc_shm_send()` → `linux_resume()` → 读取响应
- 通过 `dsk_boot_context.reserved[5]` 暴露
### shell `linux` 命令(`CODE/shell/main.c`)
- `cmd_linux` 函数:`linux ls -la` → `g_lxc_svc->exec("/bin/ls -la")`
- 支持绝对路径,Linux 未构建时显示"兼容层不可用"
### virtio 模拟
- 模型:virtio-mmio(非 PCI)
- **IRQ 路由**:virtio-blk → IRQ5(0x35),virtio-net → IRQ6(0x36),公式 `IRQ_vector = 0x30 + irq`
- MMIO 处理:EPT misconfig exit → 解码 guest 指令访问大小/数据 → 寄存器分发
- `QUEUE_NOTIFY` 后调用 `vmx_guest_queue_irq(0x30+irq)` 注入中断
- `maybe_inject_irq` 按 guest IMR/IF 决定投递时机
**virtio-blk**:桥接到 UTSM block API(ahci0 SATA 盘),8GB 容量
**virtio-net**:桥接到 UTSM net API,TX/RX ring 实现占位
**virtio-rootfs-blk**:memory-backed 后端,从 Limine boot module 加载 Arch rootfs 镜像
### EPT 动态页映射
- 4 级 EPT 页表 walk,支持 4KB/2MB/1GB
- `ept_identity_map(gpa, RWX)` — 1:1 恒等映射
- `ept_gpa_to_hpa(gpa)` — hypercall 访问 guest 内存
- EPT violation handler:RAM 区按需分配 HPA,MMIO 区路由到设备模拟
### Linux guest 启动流程(`kernel/main.c`)
1. `vmm_init()` — 启用 VMX、初始化 EPT、分配 VMCS
2. `vmm_self_test()` — 自测 guest vmlaunch
3. `ipc_shm_init()` — 分配 1MB 共享内存、EPT 映射到 GPA 0x04000000
4. `linux_loader_init()` — 加载 bzImage、setup boot_params、e820 映射
5. `linux_launch()` — 配置 VMCS、`virtio_mmio_init()`、arm preemption timer、`vmlaunch`
6. `linux_compat_init()` — 初始化 Linux 兼容服务
7. `dsk_load_and_jump()` — 加载 DSK 主内核
**cmdline 关键参数**:`nohlt` + `idle=poll`(禁止 guest HLT idle),仅 daemon 显式 park 时触发 UTSM resume
### Linux exec daemon(`CODE/linux/initramfs/bin/utsm_exec_daemon.c`)
- 静态编译,打包进 initramfs
- 流程:fork → exec 命令 → pipe 捕获 stdout/stderr → IPC 发送 EXEC_STDOUT/STDERR/EXIT → HLT park
- 缺失时 init 回退到 `setsid cttyhack sh`(ttyS0 交互 shell)
### UTSM driver(`CODE/linux/patches/utsm_hcall.c`)
- `/dev/utsm` 字符设备,VMCALL hypercall 封装
- SHM 映射(mmap 共享内存 GPA 0x04000000)
- ioctl 接口:RECV_MSG / SEND_MSG / PARK / GET_READY
## 三、WSL 构建 Linux 内核
### 构建步骤
```bash
# WSL 内执行
cd ~
curl -L -o linux-6.6.tar.xz https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.6.tar.xz
tar xf linux-6.6.tar.xz && cd linux-6.6
# 安装 UTSM 驱动
mkdir -p drivers/utsm/include
cp /mnt/d/Code/Deshab/CODE/linux/patches/utsm_hcall.c drivers/utsm/
cp /mnt/d/Code/Deshab/CODE/linux/patches/utsm_Kconfig drivers/utsm/Kconfig
cp /mnt/d/Code/Deshab/CODE/linux/patches/utsm_Makefile drivers/utsm/Makefile
cp /mnt/d/Code/Deshab/CODE/utsm-ipc/ipc_proto.h drivers/utsm/include/
bash /mnt/d/Code/Deshab/CODE/linux/fix_driver.sh ~/linux-6.6
# 配置 + 编译
cp /mnt/d/Code/Deshab/CODE/linux/configs/utsm_x86_64_defconfig .config
make olddefconfig
make -j$(nproc) KCFLAGS='-std=gnu17' bzImage # KCFLAGS 修复 gcc 15 C23 兼容
# 拷贝产物
mkdir -p /mnt/d/Code/Deshab/SYSTEM/boot
cp arch/x86/boot/bzImage /mnt/d/Code/Deshab/SYSTEM/boot/linux-bzImage
```
### 关键修复
1. **ipc_proto.h 条件编译**:`<stdint.h>` 在 Linux 内核不可用,需 `#ifdef __UTSM_KERNEL__` / `#include <linux/types.h>` 分支
2. **gcc 15 C23 兼容**:`bool`/`false` 变关键字,需 `KCFLAGS='-std=gnu17'`;顶层 Makefile `-std=gnu11` 改为 `-std=gnu17`;`arch/x86/boot/compressed/` 也需同步修改
3. **WSL ext4 I/O 错误**:WSL 虚拟磁盘可能损坏,需 `wsl --shutdown` + `wsl --unregister Ubuntu` + 重装
4. **WSL 网络问题**:GitHub 访问被墙,需用清华镜像下载内核 tarball
### initramfs 构建(`build.sh` / `build_initrd.sh`)
- busybox 1.35.0 静态二进制下载(缓存 `.cache/`)
- 内核自带 `gen_init_cpio` 打包(无 root、无 cpio 依赖)
- spec 声明 `/dev/console`、`/dev/ttyS0` 节点 + 14 个 applet symlink(含 switch_root、mkdir)
- daemon 静态编译
- init 脚本:daemon 缺失时回退 `setsid cttyhack sh`(ttyS0 交互 shell,bring-up 用)
## 四、WHPX 嵌套虚拟化问题
**问题**:WHPX 下 CPUID 报告 VMX 支持但 VMXON 不可用,导致 QEMU exit code 4 不可恢复崩溃。
**解决方案**(`vmx.c` `vmx_truly_available()`):
1. 检查 CPUID.01H:ECX[31] hypervisor present bit
2. 检测 Hyper-V 签名 → 通过 CPUID 检查嵌套 VMX 支持 → 无则跳过
3. 检测 KVM 签名 → 允许(KVM 通常支持嵌套 VMX)
4. 未知 Hypervisor → 保守跳过
5. `run_qemu.bat`:WHPX 下使用 `-cpu qemu64,-vmx` 禁用 VMX CPUID 标志
**QEMU 验证结果**:UTSM → VMM init → 检测 Hyper-V 无嵌套 VMX → 安全跳过 → 加载 DSK → shell.elf 正常运行
## 五、Arch rootfs 构建(第二阶段)
### build_arch_rootfs.sh 关键步骤
1. 下载 Arch bootstrap tarball(`.tar.zst` 格式,需 `zstd` 包)
2. 解压到 WSL 原生 ext4(`/tmp`,避免 9P 慢速 I/O)
3. 编译 utsm_exec_daemon 静态二进制并安装到 rootfs
4. stripping:删除 locale(109M)/man(37M)/include(39M)/doc(13M)/info(7.3M)/.a libs → 597M→363M
5. 创建 512MB ext4 镜像,rsync rootfs 内容
6. 拷贝最终 `rootfs.img` 到 `SYSTEM/boot/`
### 内核配置确认
- `CONFIG_EXT4_FS=y`, `CONFIG_VIRTIO_BLK=y`, `CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y`, `CONFIG_DEVTMPFS_MOUNT=y`, `CONFIG_VIRTIO_NET=y`
### init 脚本修改
- 挂载 `/dev/vdb`(Arch rootfs)→ `switch_root` 到 Arch
- 保留 phase-1 回退:无 rootfs 时 ttyS0 交互 shell
### limine.conf 修改
- 添加 `linux-rootfs.img` boot module
- cmdline 添加第三个 `virtio_mmio.device` 条目
## 六、Pacman 包管理方案(WSL 互操作模式)
### 核心矛盾
- pacman 依赖 glibc + POSIX syscall + libalpm + libcurl + gpgme + zlib/zstd/xz + 可写 Unix 文件层级 + fork/exec
- Deshab 是 SAS-R0 单地址空间 Ring0,无 POSIX 层、无动态链接器、无 syscall 入口
### 方案 C(推荐):IPC 委托执行
- `SYSTEM/bin/` 放 stub,PATH 命中后通过 IPC 委托 Linux guest 执行 `/usr/bin/xxx`
- Linux 程序在 Linux guest 内原生执行,依赖 glibc + Linux syscall ABI
### 五阶段实现
| 阶段 | 核心任务 | 状态 |
|------|---------|------|
| 第一阶段 | Linux guest 跑到 shell | 代码就绪,需真机 VT-x 验证 |
| 第二阶段 | Arch rootfs + pacman + 网络 | rootfs 已构建,需 VMX 环境验证 |
| 第三阶段 | FILE_TRANSFER hypercall + payload_pool 分块传输 | 未实现 |
| 第四阶段 | EXEC_FORWARD hypercall + shell 委托执行 | 未实现 |
| 第五阶段 | desktop 动态注册 Linux 命令图标 | 未实现 |
## 七、已实现 vs 待实现
### 已实现
- PE 兼容层完整(pe_service/loader/shim/x86emu32),零警告
- cmd.elf + shell.elf `linux` 命令 + desktop 双命令行
- park-and-resume + IPC exec 协议(消息 16-20)+ linux_compat 服务
- Linux driver ioctl(RECV_MSG/SEND_MSG/PARK/GET_READY)+ exec daemon
- virtio-mmio 框架 + virtio-blk/net/rootfs-blk 后端 + EPT 动态页映射
- vmexit dispatch 接线(CPUID/IO/INT_WINDOW/PREEMPTION_TIMER)+ virtio IRQ 注入
- WHPX 安全 VMX 检测 + 优雅回退
- build.sh busybox initramfs 打包 + Arch rootfs 构建脚本
- Linux bzImage(5.7MB) + initrd(1MB) + rootfs.img(512MB stripped) 已构建
- FAT32 镜像含 CMD ELF
### 待实现
- 真机 VT-x / KVM 环境验证 Linux guest 启动
- FILE_TRANSFER / EXEC_FORWARD hypercall
- 完整 Win32 API + .NET API 覆盖
- virtio-net 完整 TX/RX ring
- Arch rootfs 在 VMX 环境中实际运行验证
- pacman 镜像源配置 + AUR helper
- desktop 动态注册 Linux 命令图标
- FAT32 ESP 容量扩展(当前 128MB 不够容纳 rootfs.img)