diff --git a/CODE/UTSM/MAKEFILE b/CODE/UTSM/MAKEFILE index 56636d8..6715fb9 100644 --- a/CODE/UTSM/MAKEFILE +++ b/CODE/UTSM/MAKEFILE @@ -9,7 +9,7 @@ CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno- ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude -I../utsm-ipc LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld -C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c vmm/*.c) +C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c vmm/*.c pe/*.c) S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S) OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o) diff --git a/CODE/UTSM/include/utsm/linux_compat.h b/CODE/UTSM/include/utsm/linux_compat.h new file mode 100644 index 0000000..57b525d --- /dev/null +++ b/CODE/UTSM/include/utsm/linux_compat.h @@ -0,0 +1,56 @@ +#ifndef UTSM_LINUX_COMPAT_H +#define UTSM_LINUX_COMPAT_H + +/* linux_compat.h — Deshab Linux 程序兼容层服务接口。 + * + * 依赖 UTSM+Linux 双内核架构:Linux guest 作为 daemon 在 VMX guest 中 + * park-and-resume 驻留。UTSM 侧的 linux_compat_service 通过 IPC 共享内存 + * 向 Linux daemon 发送 exec 请求,并 vmresume 唤醒 guest 执行。 + * + * 执行流程: + * 1. shell.elf / cmd.elf 通过 boot context 获取 linux_compat_service 指针 + * 2. 调用 exec(path, argc, argv, ...) 写 IPC 请求到 utsm_to_linux ring + * 3. exec() 调用 linux_resume() → vmresume → Linux daemon fork+exec + * 4. daemon 通过 linux_to_utsm ring 回传 stdout/stderr/exit,然后 HLT park + * 5. exec() 从 ring 读取输出,返回给调用方 + * + * 服务指针通过 dsk_boot_context.reserved[5] 暴露给 DSK 用户态(shell/cmd/desktop)。 + */ + +#include + +#define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */ + +/* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */ +typedef struct linux_compat_service { + u64 magic; /* LINUX_COMPAT_MAGIC */ + + /* 查询 Linux 兼容层是否可用(Linux guest 已 park 且 IPC 就绪)。 + * 返回 1 可用,0 不可用。 */ + int (*is_available)(void); + + /* 执行一个 Linux 程序。 + * path : 程序路径(如 "/bin/ls"),NUL-terminated + * argc/argv : 参数向量(argv[0] 通常为程序名) + * stdout_buf : 接收 stdout 的缓冲区(可为 NULL,此时丢弃 stdout) + * stdout_cap : stdout_buf 容量(字节) + * stdout_len : 输出实际写入的字节数(可为 NULL) + * exit_code : 输出进程退出码(可为 NULL) + * 返回 0 成功,负值失败(-1=服务不可用, -2=IPC 发送失败, + * -3=linux_resume 失败, -4=超时无响应)。 */ + int (*exec)(const char *path, int argc, const char *const *argv, + char *stdout_buf, u64 stdout_cap, u64 *stdout_len, + u64 *exit_code); + + /* 查询 Linux guest 状态字符串(调试用),写入 buf,返回字符串长度。 */ + int (*status)(char *buf, u64 cap); +} linux_compat_service; + +/* 获取全局 Linux 兼容层服务实例指针(UTSM 内部使用, + * 外部通过 boot context reserved[5] 获取)。 */ +const linux_compat_service *linux_compat_get_service(void); + +/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */ +void linux_compat_init(void); + +#endif /* UTSM_LINUX_COMPAT_H */ diff --git a/CODE/UTSM/include/utsm/linux_loader.h b/CODE/UTSM/include/utsm/linux_loader.h index 0f6f204..6496f00 100644 --- a/CODE/UTSM/include/utsm/linux_loader.h +++ b/CODE/UTSM/include/utsm/linux_loader.h @@ -162,9 +162,12 @@ const struct linux_guest_info *linux_get_guest_info(void); * Returns 0 on successful guest termination, negative on error. */ int linux_launch(void); -/* Default Linux command line (can be overridden). */ +/* Default Linux command line (can be overridden). + * nohlt: prevent kernel idle loop from using HLT — only the exec daemon's + * explicit ioctl(PARK) HLT should trigger VM-Exit park, so that + * linux_launch() returns only after the daemon is ready. */ #define LINUX_DEFAULT_CMDLINE \ "console=ttyS0,115200 earlyprintk=serial nokaslr " \ - "no_timer_check loglevel=7" + "no_timer_check loglevel=7 nohlt idle=poll" #endif diff --git a/CODE/UTSM/include/utsm/linux_resume.h b/CODE/UTSM/include/utsm/linux_resume.h new file mode 100644 index 0000000..98727f4 --- /dev/null +++ b/CODE/UTSM/include/utsm/linux_resume.h @@ -0,0 +1,28 @@ +#ifndef UTSM_LINUX_RESUME_H +#define UTSM_LINUX_RESUME_H + +#include + +/* linux_resume.h — Linux guest park-and-resume 唤醒接口。 + * + * linux_launch() 启动 Linux guest 后,guest daemon 完成 init 并 HLT 驻留(park)。 + * linux_resume() 通过 vmresume 唤醒 guest,daemon 处理 IPC 请求后再次 HLT park, + * 控制权返回 host。 + * + * 典型流程: + * 1. shell 调用 linux_compat_service->exec(path, argc, argv) + * 2. exec() 写 IPC 请求到 utsm_to_linux ring + * 3. exec() 调用 linux_resume() → vmresume → guest daemon 读取请求 → fork+exec + * 4. daemon 写输出到 linux_to_utsm ring → HLT park → linux_resume() 返回 + * 5. exec() 从 ring 读取输出,返回给 shell + */ + +/* 唤醒已 park 的 Linux guest。 + * 前置条件:g_guest_parked == 1(linux_launch 已完成且 guest 已 HLT)。 + * 返回 0 成功(guest 再次 park),负值失败。 */ +int linux_resume(void); + +/* 查询 Linux guest 是否已 park 就绪。 */ +int linux_is_parked(void); + +#endif /* UTSM_LINUX_RESUME_H */ diff --git a/CODE/UTSM/include/utsm/pe.h b/CODE/UTSM/include/utsm/pe.h new file mode 100644 index 0000000..92a3813 --- /dev/null +++ b/CODE/UTSM/include/utsm/pe.h @@ -0,0 +1,42 @@ +#ifndef UTSM_PE_H +#define UTSM_PE_H + +/* pe.h — Deshab PE/EXE 兼容层公共接口。 + * + * UTSM 内置 PE32+(64位)与 PE32(32位)加载器: + * - PE32+:解析 → 映射节区 → 基址重定位 → import 解析(Windows API shim)→ Ring0 原生执行 + * - PE32 :解析 → 映射节区 → 基址重定位 → import 解析 → x86-32 解释器执行 + * + * pe_service 通过 dsk_boot_context.reserved[4] 暴露给 DSK/cmd.elf, + * 同地址空间 Ring0 直接调用。 + */ + +#include + +#define PE_SERVICE_MAGIC 0x5045535643200000ULL /* "PESVC\0\0" */ + +/* PE 加载结果 */ +typedef struct pe_image_info { + u64 image_base; /* 实际加载基址(kmem_alloc_aligned 返回) */ + u64 image_size; /* 映射后总大小(SizeOfImage) */ + u64 entry_point; /* 绝对入口地址 = image_base + AddressOfEntryPoint */ + int is_pe32_plus; /* 1=PE32+ 64位原生执行, 0=PE32 32位解释执行 */ + int load_ok; /* 1=加载成功 */ +} pe_image_info; + +/* PE 服务表 — 通过 boot context reserved[4] 传递 */ +typedef struct pe_service { + u64 magic; /* PE_SERVICE_MAGIC */ + /* 加载 PE 到内存(不执行)。返回 0 成功,info 填充。 */ + int (*load)(const void *pe_data, u64 size, pe_image_info *out); + /* 加载并执行 PE。cmdline = 命令行字符串(GetCommandLineA 返回值)。 + * 返回 0 成功,*exit_code 为进程退出码。 */ + int (*run)(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code); + /* 释放 pe_load 分配的镜像内存。 */ + void (*unload)(pe_image_info *info); +} pe_service; + +/* 获取全局 PE 服务实例指针(UTSM 内部使用,外部通过 boot context 获取) */ +const pe_service *pe_get_service(void); + +#endif /* UTSM_PE_H */ diff --git a/CODE/UTSM/include/utsm/vmm.h b/CODE/UTSM/include/utsm/vmm.h index d130267..c03f4ca 100644 --- a/CODE/UTSM/include/utsm/vmm.h +++ b/CODE/UTSM/include/utsm/vmm.h @@ -42,6 +42,30 @@ struct vmexit_guest_regs { * Non-static: shared between vmm.c, vmexit.c, hypercall.c, and vmexit_asm.S. */ extern volatile struct vmexit_guest_regs g_guest_regs; +/* ===== Linux guest park-and-resume 状态 ===== + * + * g_linux_guest_active: 1 = 当前运行的 guest 是 Linux guest。 + * handle_hlt 检查此标志:Linux guest HLT 时 park(推进 RIP,exit-to-host), + * self-test guest HLT 时 terminate。 + * g_guest_parked: 1 = Linux guest 已 HLT 驻留,可被 linux_resume() 唤醒。 + * linux_compat_service 通过此标志判断 guest 是否就绪。 */ +extern volatile int g_linux_guest_active; +extern volatile int g_guest_parked; + +/* ===== VM-Exit host 恢复点(vmm.c 定义,vmm.c / linux_boot.c / linux_resume.c 共用) ===== + * + * g_saved_host_rsp: vmlaunch/vmresume 前保存的 host RSP。VM-Exit 处理器 + * 终止 guest 时恢复此 RSP 并 jmp 到 g_saved_return_rip。 + * g_saved_return_rip: vmlaunch/vmresume 后的返回地址(post_guest / post_resume 标签)。 + * g_guest_terminated: 1 = guest 已终止(不再 vmresume)。dispatch 返回 0 时置位。 + * g_last_exit_reason: 最近一次 VM-Exit 的 reason(vmexit_asm.S 路径写入)。 + * + * 这些符号必须是非 static 全局变量,因为 vmexit_asm.S 通过 RIP 相对寻址引用。 */ +extern volatile u64 g_saved_host_rsp; +extern volatile u64 g_saved_return_rip; +extern volatile int g_guest_terminated; +extern volatile u64 g_last_exit_reason; + /* ===== VMM API ===== */ /* 初始化 VMM:VMX root 模式 + EPT 表 + 分配 VMCS。 diff --git a/CODE/UTSM/kernel/dsk_loader.c b/CODE/UTSM/kernel/dsk_loader.c index 837adec..4145b2d 100644 --- a/CODE/UTSM/kernel/dsk_loader.c +++ b/CODE/UTSM/kernel/dsk_loader.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "../arch/x86_64/limine.h" #define DSK_PATH "/system/deshab64/deshab.elf" @@ -319,6 +321,18 @@ static void dsk_fill_boot_context(dsk_boot_context *ctx) { ctx->boot_modules_response = (u64)g_module_request.response; ctx->dkm_kernel_api = (u64)dkm_get_kernel_api(); + /* PE 兼容层服务(PE32+ 原生 + PE32 解释器) */ + const pe_service *pe = pe_get_service(); + if (pe && pe->magic == PE_SERVICE_MAGIC) { + ctx->reserved[4] = (u64)pe; + } + + /* Linux 兼容层服务(依赖双内核 park-and-resume + IPC) */ + const linux_compat_service *lxc = linux_compat_get_service(); + if (lxc && lxc->magic == LINUX_COMPAT_MAGIC) { + ctx->reserved[5] = (u64)lxc; + } + u64 rsp; __asm__ volatile ("movq %%rsp, %0" : "=r"(rsp)); ctx->kernel_stack_top = rsp; diff --git a/CODE/UTSM/kernel/dsk_loader.o b/CODE/UTSM/kernel/dsk_loader.o index 7beab38..0e90312 100644 Binary files a/CODE/UTSM/kernel/dsk_loader.o and b/CODE/UTSM/kernel/dsk_loader.o differ diff --git a/CODE/UTSM/kernel/main.c b/CODE/UTSM/kernel/main.c index 3eabec6..366a89d 100644 --- a/CODE/UTSM/kernel/main.c +++ b/CODE/UTSM/kernel/main.c @@ -8,6 +8,7 @@ #include #include #include +#include void arch_halt_forever(void); @@ -71,7 +72,10 @@ void kernel_main(void) { int lin_st = linux_launch(); if (lin_st == 0) { - log_info("[UTSM] Linux guest terminated"); + log_info("[UTSM] Linux guest parked (daemon ready)"); + /* Phase 3.2: 初始化 Linux 兼容层服务 + * (需在 linux_launch 成功 + guest park 后调用) */ + linux_compat_init(); } else { log_error("[UTSM] Linux launch failed"); log_hex64("[UTSM] Linux st=", (u64)(i64)lin_st); diff --git a/CODE/UTSM/kernel/main.o b/CODE/UTSM/kernel/main.o index 0c4c444..47a09e3 100644 Binary files a/CODE/UTSM/kernel/main.o and b/CODE/UTSM/kernel/main.o differ diff --git a/CODE/UTSM/pe/pe_loader.c b/CODE/UTSM/pe/pe_loader.c new file mode 100644 index 0000000..faa3d07 --- /dev/null +++ b/CODE/UTSM/pe/pe_loader.c @@ -0,0 +1,368 @@ +/* pe_loader.c — PE32+/PE32 解析、节区映射、重定位、import 解析。 + * + * 加载流程: + * 1. 验证 MZ + PE 签名 + * 2. 区分 PE32(OptionalHeader.Magic=0x10b)/ PE32+(0x20b) + * 3. 分配 SizeOfImage 字节 RWX 内存(kmem_alloc_aligned) + * 4. 复制 SizeOfHeaders + 各节区 raw data 到 virtual_address + * 5. 应用基址重定位(.reloc 节区:DIR64 for PE32+,HIGHLOW for PE32) + * 6. 解析 import 表,用 shim 函数指针填充 IAT + * 7. 构建 IAT 条目表(供 32位解释器 CALL 拦截) + */ + +#include "pe_loader.h" +#include "pe_shim.h" +#include "x86emu32.h" +#include "../include/utsm/pe.h" +#include +#include +#include + +/* 合成 shim 地址范围(32位解释器用):0x00010000..0x0001FFFF */ +#define PE_SHIM_ADDR_BASE 0x00010000u +#define PE_SHIM_ADDR_END 0x00020000u +#define PE_SHIM_UNIMPL 0x0001FFFFu + +/* 全局 IAT 表(pe_loader 填充,x86emu32 读取) */ +static pe_iat_entry g_iat_entries[256]; +static int g_iat_count; + +/* ---- 辅助:RVA → 文件偏移 ---- */ +static u32 rva_to_offset(const image_section_header *sections, int nsec, + u32 rva, u32 *out_size) { + for (int i = 0; i < nsec; i++) { + u32 va = sections[i].virtual_address; + u32 vs = sections[i].virtual_size; + if (vs == 0) vs = sections[i].size_of_raw_data; + if (rva >= va && rva < va + vs) { + if (out_size) *out_size = vs; + return sections[i].pointer_to_raw_data + (rva - va); + } + } + if (out_size) *out_size = 0; + return 0xFFFFFFFF; +} + +/* ---- 解析 NT 头 ---- */ +typedef struct { + const image_file_header *file_hdr; + const void *optional_hdr; + int is_pe32_plus; + u32 image_base; /* 32位截断的加载基址(PE32+ 也截断用于 RVA 计算) */ + u32 size_of_image; + u32 size_of_headers; + u32 entry_rva; + const image_data_directory *data_dir; +} pe_nt_info; + +static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) { + if (size < sizeof(image_dos_header)) return -1; + const image_dos_header *dos = (const image_dos_header *)pe_data; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -2; + if (dos->e_lfanew + 4 + sizeof(image_file_header) > size) return -3; + + const u8 *nt_base = (const u8 *)pe_data + dos->e_lfanew; + u32 sig = *(const u32 *)nt_base; + if (sig != IMAGE_NT_SIGNATURE) return -4; + + const image_file_header *fh = (const image_file_header *)(nt_base + 4); + out->file_hdr = fh; + + const void *opt = (const void *)(nt_base + 4 + sizeof(image_file_header)); + out->optional_hdr = opt; + + u16 magic = *(const u16 *)opt; + if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + const image_optional_header64 *oh = (const image_optional_header64 *)opt; + out->is_pe32_plus = 1; + out->image_base = (u32)oh->image_base; + out->size_of_image = oh->size_of_image; + out->size_of_headers = oh->size_of_headers; + out->entry_rva = oh->address_of_entry_point; + out->data_dir = oh->data_directory; + } else if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + const image_optional_header32 *oh = (const image_optional_header32 *)opt; + out->is_pe32_plus = 0; + out->image_base = oh->image_base; + out->size_of_image = oh->size_of_image; + out->size_of_headers = oh->size_of_headers; + out->entry_rva = oh->address_of_entry_point; + out->data_dir = oh->data_directory; + } else { + return -5; + } + + if (out->size_of_image == 0 || out->size_of_image > 64 * 1024 * 1024) return -6; + if (out->entry_rva == 0 || out->entry_rva >= out->size_of_image) return -7; + return 0; +} + +/* ---- 应用基址重定位 ---- */ +static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u32 image_base_preferred, + const image_data_directory *reloc_dir, + const image_section_header *sections, int nsec, + const void *pe_data, u64 pe_size, int is_pe32_plus) { + if (reloc_dir->virtual_address == 0 || reloc_dir->size == 0) return; + + u32 block_rva = reloc_dir->virtual_address; + u32 block_end = block_rva + reloc_dir->size; + i64 delta = (i64)image_base_actual - (i64)image_base_preferred; + if (delta == 0) return; /* 加载到首选基址,无需重定位 */ + + while (block_rva < block_end) { + u32 dummy_size; + u32 file_off = rva_to_offset(sections, nsec, block_rva, &dummy_size); + if (file_off == 0xFFFFFFFF || file_off + 8 > pe_size) break; + + const u8 *block = (const u8 *)pe_data + file_off; + u32 page_rva = *(const u32 *)block; + u32 block_size = *(const u32 *)(block + 4); + if (block_size < 8 || block_rva + block_size > block_end) break; + + u32 entry_count = (block_size - 8) / 2; + const u16 *entries = (const u16 *)(block + 8); + + for (u32 i = 0; i < entry_count; i++) { + u16 e = entries[i]; + u32 type = (e >> 12) & 0xF; + u32 offset = e & 0xFFF; + + if (type == IMAGE_REL_BASED_ABSOLUTE) continue; + + u32 target_rva = page_rva + offset; + /* image 缓冲区按 RVA 排列(image[0] = RVA 0) */ + if (target_rva >= (u32)image_size) continue; + u8 *target = image + target_rva; + if (type == IMAGE_REL_BASED_DIR64) { + u64 val = *(u64 *)target; + val += (u64)delta; + *(u64 *)target = val; + } else if (type == IMAGE_REL_BASED_HIGHLOW) { + u32 val = *(u32 *)target; + val += (u32)delta; + *(u32 *)target = val; + } else { + /* 其他重定位类型暂不支持 */ + } + } + block_rva += block_size; + } + (void)is_pe32_plus; +} + +/* ---- 解析 imports,填充 IAT ---- */ +static void resolve_imports(u8 *image, u64 image_base_actual, + const image_data_directory *import_dir, + const image_section_header *sections, int nsec, + const void *pe_data, u64 pe_size, int is_pe32_plus) { + (void)image_base_actual; /* PE32 IAT 用合成地址,不依赖 actual_base */ + if (import_dir->virtual_address == 0 || import_dir->size == 0) return; + + g_iat_count = 0; + u32 dummy; + u32 imp_off = rva_to_offset(sections, nsec, import_dir->virtual_address, &dummy); + if (imp_off == 0xFFFFFFFF) return; + + const image_import_descriptor *imp = (const image_import_descriptor *) + ((const u8 *)pe_data + imp_off); + + for (int dll_idx = 0; ; dll_idx++) { + const image_import_descriptor *desc = &imp[dll_idx]; + if (desc->name == 0 && desc->first_thunk == 0) break; + + /* DLL 名 */ + u32 name_off = rva_to_offset(sections, nsec, desc->name, &dummy); + if (name_off == 0xFFFFFFFF || name_off >= pe_size) continue; + const char *dll_name = (const char *)pe_data + name_off; + + /* IAT (first_thunk) 与 INT (original_first_thunk) */ + u32 iat_rva = desc->first_thunk; + u32 int_rva = desc->original_first_thunk; + if (int_rva == 0) int_rva = iat_rva; /* 无 INT,直接用 IAT */ + + u32 iat_off = rva_to_offset(sections, nsec, iat_rva, &dummy); + u32 int_off = rva_to_offset(sections, nsec, int_rva, &dummy); + + for (int fn_idx = 0; ; fn_idx++) { + u32 iat_entry_off = iat_off + (u32)fn_idx * (is_pe32_plus ? 8 : 4); + if (iat_entry_off + (is_pe32_plus ? 8 : 4) > pe_size) break; + + u32 int_entry_off = int_off + (u32)fn_idx * (is_pe32_plus ? 8 : 4); + if (int_entry_off + (is_pe32_plus ? 8 : 4) > pe_size) break; + + /* 读 INT 条目判断函数名/序号 */ + u64 thunk = 0; + if (is_pe32_plus) { + thunk = *(const u64 *)((const u8 *)pe_data + int_entry_off); + } else { + thunk = *(const u32 *)((const u8 *)pe_data + int_entry_off); + } + if (thunk == 0) break; /* 结束 */ + + const char *func_name = 0; + if (thunk & 0x80000000ULL) { + /* 序号导入:暂不支持,记为未实现 */ + func_name = "#ordinal"; + } else { + /* 名称导入:thunk 是 IMAGE_IMPORT_BY_NAME 的 RVA */ + u32 hint_rva = (u32)thunk; + u32 hint_off = rva_to_offset(sections, nsec, hint_rva, &dummy); + if (hint_off != 0xFFFFFFFF && hint_off + 2 < pe_size) { + func_name = (const char *)pe_data + hint_off + 2; /* 跳过 hint(2字节) */ + } + } + + /* 查 shim 表 */ + int shim_idx = -1; + if (func_name && func_name[0] != '#') { + shim_idx = pe_shim_lookup(dll_name, func_name); + } + + /* 填充 IAT(image 缓冲区内) */ + u8 *iat_ptr = image + iat_rva + (u32)fn_idx * (is_pe32_plus ? 8 : 4); + if (is_pe32_plus) { + u64 fn_addr; + if (shim_idx >= 0) { + const pe_shim_entry *se = pe_shim_get(shim_idx); + fn_addr = (u64)se->fn64; + } else { + fn_addr = pe_shim_unimpl_stub(); + } + *(u64 *)iat_ptr = fn_addr; + } else { + /* 32位:填合成地址,解释器拦截 */ + u32 synth_addr; + if (shim_idx >= 0) { + synth_addr = PE_SHIM_ADDR_BASE | (u32)(shim_idx & 0xFFFF); + } else { + synth_addr = PE_SHIM_UNIMPL; + } + *(u32 *)iat_ptr = synth_addr; + + /* 记录 IAT 条目供解释器查询 */ + if (g_iat_count < 255) { + g_iat_entries[g_iat_count].iat_rva = iat_rva + (u32)fn_idx * 4; + g_iat_entries[g_iat_count].shim_index = shim_idx; + g_iat_entries[g_iat_count].target_addr = synth_addr; + g_iat_count++; + } + } + + if (shim_idx < 0 && func_name) { + log_info("[PE] unimplemented import"); + serial_write(dll_name); + serial_write("!"); + serial_write(func_name); + serial_write("\n"); + } + } + } +} + +/* ---- 主加载函数 ---- */ +int pe_load_image(const void *pe_data, u64 size, pe_image_info *out) { + if (!pe_data || !out || size < sizeof(image_dos_header)) return -1; + + pe_nt_info nt; + int rc = parse_nt_headers(pe_data, size, &nt); + if (rc != 0) { + log_error("[PE] NT header parse failed"); + log_hex64("[PE] rc=", (u64)(i64)rc); + return -2; + } + + /* 节区头 */ + int nsec = nt.file_hdr->number_of_sections; + if (nsec <= 0 || nsec > 96) return -3; + usize opt_size = nt.file_hdr->size_of_optional_header; + const image_section_header *sections = (const image_section_header *) + ((const u8 *)nt.optional_hdr + opt_size); + + /* 分配 SizeOfImage RWX 内存 */ + u32 img_size = nt.size_of_image; + u8 *image = (u8 *)kmem_alloc_aligned(img_size, 0x1000); + if (!image) { + log_error("[PE] alloc image failed"); + return -4; + } + /* 清零 */ + for (u32 i = 0; i < img_size; i++) image[i] = 0; + + /* 复制 headers */ + u32 hdr_copy = nt.size_of_headers; + if (hdr_copy > size) hdr_copy = (u32)size; + if (hdr_copy > img_size) hdr_copy = img_size; + for (u32 i = 0; i < hdr_copy; i++) image[i] = ((const u8 *)pe_data)[i]; + + /* 复制各节区 raw data */ + for (int i = 0; i < nsec; i++) { + u32 va = sections[i].virtual_address; + u32 raw_size = sections[i].size_of_raw_data; + u32 raw_off = sections[i].pointer_to_raw_data; + if (va >= img_size) continue; + u32 copy_size = raw_size; + if (va + copy_size > img_size) copy_size = img_size - va; + if (raw_off + copy_size > size) { + copy_size = (u32)(size - raw_off); + } + if (raw_off + copy_size > size) continue; + for (u32 j = 0; j < copy_size; j++) { + image[va + j] = ((const u8 *)pe_data)[raw_off + j]; + } + } + + /* 基址重定位 */ + u64 actual_base = (u64)image; + u32 preferred_base = nt.image_base; + /* image 缓冲区按 RVA 排列(image[0]=RVA 0),重定位写入 image 内 */ + /* 注意:apply_relocations 直接操作 image 缓冲区,delta = actual_base - preferred_base */ + { + const image_data_directory *reloc_dir = &nt.data_dir[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + /* 重定位 target 是 RVA,写入 image[rva]。 + * 但 image 加载在 actual_base,代码中绝对地址 = actual_base + RVA。 + * 重定位修正量 = actual_base - preferred_base。 + * image[rva] 处的值原为 preferred_base + rva_offset(链接时假设), + * 修正后应为 actual_base + rva_offset,即加 delta。 */ + apply_relocations(image, img_size, actual_base, preferred_base, reloc_dir, + sections, nsec, pe_data, size, nt.is_pe32_plus); + } + + /* 解析 imports */ + { + const image_data_directory *import_dir = &nt.data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT]; + resolve_imports(image, actual_base, import_dir, sections, nsec, + pe_data, size, nt.is_pe32_plus); + } + + out->image_base = actual_base; + out->image_size = img_size; + out->entry_point = actual_base + nt.entry_rva; + out->is_pe32_plus = nt.is_pe32_plus; + out->load_ok = 1; + + log_hex64("[PE] loaded base=", actual_base); + log_hex64("[PE] image size=", img_size); + log_hex64("[PE] entry=", out->entry_point); + log_info(nt.is_pe32_plus ? "[PE] PE32+ (64-bit)" : "[PE] PE32 (32-bit)"); + return 0; +} + +void pe_unload_image(pe_image_info *info) { + if (!info || !info->load_ok) return; + /* kmem_alloc_aligned 无对应 free(arena 分配器),标记为未加载即可 */ + info->load_ok = 0; +} + +u64 pe_get_image_base(const pe_image_info *info) { + return info ? info->image_base : 0; +} + +u64 pe_get_image_size(const pe_image_info *info) { + return info ? info->image_size : 0; +} + +/* 供 x86emu32 获取 IAT 表 */ +pe_iat_entry *pe_get_iat_entries(int *count) { + if (count) *count = g_iat_count; + return g_iat_entries; +} diff --git a/CODE/UTSM/pe/pe_loader.h b/CODE/UTSM/pe/pe_loader.h new file mode 100644 index 0000000..759badd --- /dev/null +++ b/CODE/UTSM/pe/pe_loader.h @@ -0,0 +1,185 @@ +#ifndef PE_LOADER_H +#define PE_LOADER_H + +/* pe_loader.h — PE32+/PE32 解析与加载器内部接口。 */ + +#include +#include + +/* ===== PE 常量 ===== */ +#define IMAGE_DOS_SIGNATURE 0x5A4D /* "MZ" */ +#define IMAGE_NT_SIGNATURE 0x00004550 /* "PE\0\0" */ +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b + +/* Section characteristics */ +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +/* Data directory indices */ +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 +#define IMAGE_DIRECTORY_ENTRY_IAT 12 + +/* Relocation types */ +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGHLOW 3 /* 32位: base + offset 写 32位 */ +#define IMAGE_REL_BASED_DIR64 10 /* 64位: base + offset 写 64位 */ + +/* ===== PE 结构体定义(packed) ===== */ + +typedef struct __attribute__((packed)) { + u16 e_magic; /* "MZ" */ + u16 e_cblp; + u16 e_cp; + u16 e_crlc; + u16 e_cparhdr; + u16 e_minalloc; + u16 e_maxalloc; + u16 e_ss; + u16 e_sp; + u16 e_csum; + u16 e_ip; + u16 e_cs; + u16 e_lfarlc; + u16 e_ovno; + u16 e_res[4]; + u16 e_oemid; + u16 e_oeminfo; + u16 e_res2[10]; + u32 e_lfanew; /* PE 头偏移 */ +} image_dos_header; + +typedef struct __attribute__((packed)) { + u16 machine; + u16 number_of_sections; + u32 time_date_stamp; + u32 pointer_to_symbol_table; + u32 number_of_symbols; + u16 size_of_optional_header; + u16 characteristics; +} image_file_header; + +typedef struct __attribute__((packed)) { + u32 virtual_address; + u32 size; +} image_data_directory; + +typedef struct __attribute__((packed)) { + u16 magic; + u8 major_linker_version; + u8 minor_linker_version; + u32 size_of_code; + u32 size_of_initialized_data; + u32 size_of_uninitialized_data; + u32 address_of_entry_point; + u32 base_of_code; + u32 base_of_data; /* PE32 only */ + u32 image_base; /* PE32: 32位 */ + u32 section_alignment; + u32 file_alignment; + u16 major_os_version; + u16 minor_os_version; + u16 major_image_version; + u16 minor_image_version; + u16 major_subsystem_version; + u16 minor_subsystem_version; + u32 win32_version_value; + u32 size_of_image; + u32 size_of_headers; + u32 check_sum; + u16 subsystem; + u16 dll_characteristics; + u32 size_of_stack_reserve; + u32 size_of_stack_commit; + u32 size_of_heap_reserve; + u32 size_of_heap_commit; + u32 loader_flags; + u32 number_of_rva_and_sizes; + image_data_directory data_directory[16]; +} image_optional_header32; + +typedef struct __attribute__((packed)) { + u16 magic; + u8 major_linker_version; + u8 minor_linker_version; + u32 size_of_code; + u32 size_of_initialized_data; + u32 size_of_uninitialized_data; + u32 address_of_entry_point; + u32 base_of_code; + u64 image_base; /* PE32+: 64位 */ + u32 section_alignment; + u32 file_alignment; + u16 major_os_version; + u16 minor_os_version; + u16 major_image_version; + u16 minor_image_version; + u16 major_subsystem_version; + u16 minor_subsystem_version; + u32 win32_version_value; + u32 size_of_image; + u32 size_of_headers; + u32 check_sum; + u16 subsystem; + u16 dll_characteristics; + u64 size_of_stack_reserve; + u64 size_of_stack_commit; + u64 size_of_heap_reserve; + u64 size_of_heap_commit; + u32 loader_flags; + u32 number_of_rva_and_sizes; + image_data_directory data_directory[16]; +} image_optional_header64; + +typedef struct __attribute__((packed)) { + u8 name[8]; + u32 virtual_size; + u32 virtual_address; + u32 size_of_raw_data; + u32 pointer_to_raw_data; + u32 pointer_to_relocations; + u32 pointer_to_linenumbers; + u16 number_of_relocations; + u16 number_of_linenumbers; + u32 characteristics; +} image_section_header; + +typedef struct __attribute__((packed)) { + u32 original_first_thunk; /* INT (Import Name Table) RVA */ + u32 time_date_stamp; + u32 forwarder_chain; + u32 name; /* DLL 名 RVA */ + u32 first_thunk; /* IAT RVA */ +} image_import_descriptor; + +typedef struct __attribute__((packed)) { + u32 original_first_thunk; /* 指向 IMAGE_IMPORT_BY_NAME */ +} image_import_by_name_ref; + +/* ===== 加载器 API ===== */ + +/* 解析并加载 PE 到内存。 + * - 分配 SizeOfImage 字节 RWX 内存 + * - 复制 headers + 各节区 + * - 应用基址重定位 + * - 解析 imports(填 IAT 为 shim 函数指针) + * 返回 0 成功,info 填充;负数失败。 */ +int pe_load_image(const void *pe_data, u64 size, pe_image_info *out); + +/* 释放 pe_load_image 分配的内存 */ +void pe_unload_image(pe_image_info *info); + +/* 获取 PE 内部信息(供 shim/解释器使用) */ +u64 pe_get_image_base(const pe_image_info *info); +u64 pe_get_image_size(const pe_image_info *info); + +/* 获取 IAT 条目表(供 32位解释器 CALL 拦截)。 + * pe_iat_entry 定义在 x86emu32.h。返回表指针,*count 填充条目数。 */ +struct pe_iat_entry; +struct pe_iat_entry *pe_get_iat_entries(int *count); + +#endif /* PE_LOADER_H */ diff --git a/CODE/UTSM/pe/pe_service.c b/CODE/UTSM/pe/pe_service.c new file mode 100644 index 0000000..68f912b --- /dev/null +++ b/CODE/UTSM/pe/pe_service.c @@ -0,0 +1,290 @@ +/* pe_service.c — PE 兼容层服务装配。 + * + * 将 pe_loader + pe_shim + x86emu32 整合为统一的 pe_service, + * 通过 pe_get_service() 暴露给 DSK/cmd.elf(经 dsk_boot_context.reserved[4])。 + * + * 执行策略: + * PE32+ (64位):pe_load_image 加载(actual_base 重定位), + * IAT 填 ms_abi shim 函数地址,Ring0 原生执行入口。 + * ExitProcess 经 __builtin_longjmp 跳回 pe_service_run。 + * PE32 (32位):直接以 preferred_base 加载到一块含栈/堆的大缓冲区, + * 无需重定位。IAT 填合成地址(0x00010000 | shim_idx), + * x86emu32 解释执行,CALL 合成地址时拦截分发到 shim32。 + */ + +#include "pe_loader.h" +#include "pe_shim.h" +#include "x86emu32.h" +#include "../include/utsm/pe.h" +#include +#include +#include + +/* 32位 PE 用的栈与堆 */ +#define PE32_STACK_SIZE (64 * 1024) /* 64KB 栈 */ +#define PE32_HEAP_SIZE (256 * 1024) /* 256KB 堆 */ +#define PE32_MAX_IAT 256 + +/* 合成 shim 地址范围(与 pe_loader.c 一致) */ +#define PE32_SHIM_ADDR_BASE 0x00010000u +#define PE32_SHIM_UNIMPL 0x0001FFFFu + +/* 前向声明(服务表引用) */ +int pe_service_load(const void *pe_data, u64 size, pe_image_info *out); +int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code); +void pe_service_unload(pe_image_info *info); + +/* 全局服务表实例 */ +static const pe_service g_pe_service = { + PE_SERVICE_MAGIC, + pe_service_load, + pe_service_run, + pe_service_unload, +}; + +const pe_service *pe_get_service(void) { + return &g_pe_service; +} + +/* ===== 加载(不执行)===== */ +int pe_service_load(const void *pe_data, u64 size, pe_image_info *out) { + if (!pe_data || !out) return -1; + return pe_load_image(pe_data, size, out); +} + +void pe_service_unload(pe_image_info *info) { + pe_unload_image(info); +} + +/* ===== PE32 专用加载器(加载到指定缓冲区,base=preferred_base,无重定位)===== */ +typedef struct { + u8 *mem; /* 大缓冲区起始 */ + u32 mem_size; /* 缓冲区总大小 */ + u32 image_base; /* = preferred_base */ + u32 image_size; + u32 entry_point; /* 绝对入口 = image_base + entry_rva */ + pe_iat_entry iat[PE32_MAX_IAT]; + int iat_count; +} pe32_interp_image; + +static int pe32_load_into(const void *pe_data, u64 size, pe32_interp_image *img) { + const u8 *base = (const u8 *)pe_data; + if (size < sizeof(image_dos_header)) return -1; + const image_dos_header *dos = (const image_dos_header *)base; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -2; + if ((u64)dos->e_lfanew + 4 + sizeof(image_file_header) > size) return -3; + + const u8 *nt = base + dos->e_lfanew; + if (*(const u32 *)nt != IMAGE_NT_SIGNATURE) return -4; + const image_file_header *fh = (const image_file_header *)(nt + 4); + const void *opt = (const void *)(nt + 4 + sizeof(image_file_header)); + u16 magic = *(const u16 *)opt; + if (magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) return -5; /* 仅 PE32 */ + + const image_optional_header32 *oh = (const image_optional_header32 *)opt; + u32 preferred_base = oh->image_base; + u32 image_size = oh->size_of_image; + u32 entry_rva = oh->address_of_entry_point; + if (image_size == 0 || image_size > 64 * 1024 * 1024) return -6; + if (preferred_base > 0x10000000u) return -7; /* 解释器地址空间限制 */ + + int nsec = fh->number_of_sections; + if (nsec <= 0 || nsec > 96) return -8; + const image_section_header *sections = (const image_section_header *) + ((const u8 *)opt + fh->size_of_optional_header); + + /* 分配大缓冲区:[0 .. preferred_base + image_size + stack + heap] */ + u32 total = preferred_base + image_size + PE32_STACK_SIZE + PE32_HEAP_SIZE; + u8 *mem = (u8 *)kmem_alloc_aligned((u64)total, 0x1000); + if (!mem) return -9; + for (u32 i = 0; i < total; i++) mem[i] = 0; + + /* 复制 headers */ + u32 hdr_copy = oh->size_of_headers; + if (hdr_copy > size) hdr_copy = (u32)size; + if (hdr_copy > image_size) hdr_copy = image_size; + u8 *img_start = mem + preferred_base; + for (u32 i = 0; i < hdr_copy; i++) img_start[i] = base[i]; + + /* 复制各节区 */ + for (int i = 0; i < nsec; i++) { + u32 va = sections[i].virtual_address; + u32 raw_size = sections[i].size_of_raw_data; + u32 raw_off = sections[i].pointer_to_raw_data; + if (va >= image_size) continue; + u32 copy_size = raw_size; + if (va + copy_size > image_size) copy_size = image_size - va; + if ((u64)raw_off + copy_size > size) copy_size = (u32)(size - raw_off); + for (u32 j = 0; j < copy_size; j++) img_start[va + j] = base[raw_off + j]; + } + + /* 解析 imports,填充 IAT(合成地址) */ + img->iat_count = 0; + const image_data_directory *import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (import_dir->virtual_address != 0 && import_dir->size != 0) { + /* RVA→offset 辅助 */ + for (int dll_idx = 0; ; dll_idx++) { + u32 imp_rva = import_dir->virtual_address + (u32)dll_idx * sizeof(image_import_descriptor); + /* imp_rva 在 image 内 */ + if (imp_rva + sizeof(image_import_descriptor) > image_size) break; + const image_import_descriptor *desc = (const image_import_descriptor *)(img_start + imp_rva); + if (desc->name == 0 && desc->first_thunk == 0) break; + + /* DLL 名 */ + u32 name_rva = desc->name; + const char *dll_name = 0; + if (name_rva < image_size) dll_name = (const char *)(img_start + name_rva); + + u32 iat_rva = desc->first_thunk; + u32 int_rva = desc->original_first_thunk; + if (int_rva == 0) int_rva = iat_rva; + + for (int fn_idx = 0; ; fn_idx++) { + u32 int_entry_rva = int_rva + (u32)fn_idx * 4; + if (int_entry_rva + 4 > image_size) break; + u32 thunk = *(const u32 *)(img_start + int_entry_rva); + if (thunk == 0) break; + + const char *func_name = 0; + if (thunk & 0x80000000u) { + func_name = "#ordinal"; + } else { + u32 hint_rva = thunk; + if (hint_rva + 2 < image_size) { + func_name = (const char *)(img_start + hint_rva + 2); + } + } + + int shim_idx = -1; + if (func_name && func_name[0] != '#') { + shim_idx = pe_shim_lookup(dll_name, func_name); + } + + u32 synth_addr; + if (shim_idx >= 0) { + synth_addr = PE32_SHIM_ADDR_BASE | (u32)(shim_idx & 0xFFFF); + } else { + synth_addr = PE32_SHIM_UNIMPL; + } + + u32 iat_entry_rva = iat_rva + (u32)fn_idx * 4; + if (iat_entry_rva + 4 <= image_size) { + *(u32 *)(img_start + iat_entry_rva) = synth_addr; + } + + if (img->iat_count < PE32_MAX_IAT) { + img->iat[img->iat_count].iat_rva = iat_entry_rva; + img->iat[img->iat_count].shim_index = shim_idx; + img->iat[img->iat_count].target_addr = synth_addr; + img->iat_count++; + } + + if (shim_idx < 0 && func_name) { + log_info("[PE32] unimplemented import"); + if (dll_name) serial_write(dll_name); + serial_write("!"); + serial_write(func_name); + serial_write("\n"); + } + } + } + } + + img->mem = mem; + img->mem_size = total; + img->image_base = preferred_base; + img->image_size = image_size; + img->entry_point = preferred_base + entry_rva; + return 0; +} + +/* ===== 运行 ===== + * 加载 + 执行 + 卸载。返回 0 成功,*exit_code 为进程退出码。 */ +int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code) { + if (!pe_data || size == 0) return -1; + + /* 初始化 shim 状态(cmdline for GetCommandLineA) */ + pe_shim_init(cmdline ? cmdline : ""); + + /* 先解析 PE 判断 32/64 位 */ + if (size < sizeof(image_dos_header)) return -2; + const image_dos_header *dos = (const image_dos_header *)pe_data; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -3; + const u8 *nt_base = (const u8 *)pe_data + dos->e_lfanew; + if (*(const u32 *)nt_base != IMAGE_NT_SIGNATURE) return -4; + const void *opt = (const void *)(nt_base + 4 + sizeof(image_file_header)); + u16 opt_magic = *(const u16 *)opt; + + if (opt_magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + /* ===== PE32+ : Ring0 原生执行 ===== */ + pe_image_info info; + int rc = pe_load_image(pe_data, size, &info); + if (rc != 0) { + log_error("[PE] PE32+ load failed"); + return -5; + } + log_info("[PE] running PE32+ natively"); + + u64 code = 0; + int jumped = pe_shim_setup_exit(); + if (jumped == 0) { + typedef void (*pe_entry_fn)(void); + pe_entry_fn entry = (pe_entry_fn)info.entry_point; + log_hex64("[PE] entry=", (u64)entry); + entry(); + code = 0; + } else { + code = pe_shim_get_exit_code(); + log_hex64("[PE] ExitProcess code=", code); + } + + pe_unload_image(&info); + if (exit_code) *exit_code = code; + return 0; + + } else if (opt_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + /* ===== PE32 : x86-32 解释器执行 ===== */ + pe32_interp_image img; + int rc = pe32_load_into(pe_data, size, &img); + if (rc != 0) { + log_error("[PE] PE32 interp load failed"); + log_hex64("[PE] rc=", (u64)(i64)rc); + return -6; + } + log_info("[PE] running PE32 via x86emu32"); + + u32 stack_top = img.image_base + img.image_size + PE32_STACK_SIZE - 16; + + x86emu_state emu; + int irc = x86emu32_init(&emu, img.mem, img.mem_size, + img.image_base, img.image_size, + img.iat, img.iat_count, stack_top); + if (irc != 0) { + log_error("[PE32] emu init failed"); + return -7; + } + emu.eip = img.entry_point; + + u64 code = 0; + int jumped = pe_shim_setup_exit(); + if (jumped == 0) { + int rrc = x86emu32_run(&emu, &code); + if (rrc != 0) { + log_error("[PE32] emulator error"); + code = 0xFFFFFFFFULL; + } + } else { + code = pe_shim_get_exit_code(); + log_hex64("[PE32] ExitProcess code=", code); + } + + if (emu.error && !emu.exited) { + log_warn("[PE32] emulator stopped with error"); + } + + if (exit_code) *exit_code = code; + return 0; + } + + return -8; /* 未知 PE magic */ +} diff --git a/CODE/UTSM/pe/pe_shim.c b/CODE/UTSM/pe/pe_shim.c new file mode 100644 index 0000000..a226814 --- /dev/null +++ b/CODE/UTSM/pe/pe_shim.c @@ -0,0 +1,421 @@ +/* pe_shim.c — Windows API shim 实现。 + * + * 所有 64位 shim 用 __attribute__((ms_abi)) 声明,匹配 PE32+ 调用约定。 + * 32位 shim 从模拟栈读参数,供 x86emu32 解释器调用。 + * + * 支持 API:kernel32.dll(GetStdHandle/WriteFile/ReadFile/ExitProcess/ + * HeapAlloc/HeapFree/VirtualAlloc/VirtualFree/GetCommandLineA/...) + * msvcrt.dll(printf/puts/putchar/malloc/free/exit/memcpy/memset/ + * strlen/strcmp/strcpy/_initterm/_initterm_e) + */ + +#include "pe_shim.h" +#include +#include +#include + +/* setjmp/longjmp for ExitProcess — 使用 __builtin_setjmp/__builtin_longjmp + * (clang/gcc 内置,freestanding 可用,无需 libc)。 + * __builtin_setjmp 接受 void** 缓冲区,至少需要 8 个指针槽。 */ +static void *g_exit_jmp[24]; +static int g_exit_jmp_set; +static u64 g_exit_code; +static const char *g_cmdline = ""; + +void pe_shim_init(const char *cmdline) { + g_cmdline = cmdline ? cmdline : ""; + g_exit_jmp_set = 0; + g_exit_code = 0; +} + +/* pe_run 调用:保存上下文,返回 0 = 首次(继续执行),1 = ExitProcess 触发 */ +int pe_shim_setup_exit(void) { + g_exit_jmp_set = 1; + return __builtin_setjmp(g_exit_jmp); +} + +void pe_shim_exit_process(u64 exit_code) { + g_exit_code = exit_code; + if (g_exit_jmp_set) { + __builtin_longjmp(g_exit_jmp, 1); + } + /* 无 jmpbuf:直接返回 */ +} + +int pe_shim_is_exit_call(u64 target) { + /* 64位 ExitProcess shim 地址 */ + extern u64 pe_shim_exitprocess64_addr(void); + if (target == pe_shim_exitprocess64_addr()) return 1; + /* 32位合成地址中 ExitProcess 的标记 */ + return 0; +} + +const char *pe_shim_get_cmdline(void) { return g_cmdline; } + +u64 pe_shim_unimpl_stub(void) { + extern u64 pe_shim_unimpl64_addr(void); + return pe_shim_unimpl64_addr(); +} + +/* ===== 串口/帧缓冲输出(shim 内部复用) ===== */ +static void shim_output(const char *s, u64 len) { + for (u64 i = 0; i < len; i++) { + serial_putc(s[i]); + } +} + +static void shim_output_str(const char *s) { + serial_write(s); +} + +/* ===== kernel32.dll shims (64位, ms_abi) ===== */ + +/* 句柄定义 */ +#define STD_INPUT_HANDLE ((u64)0xFFFFFFF6) +#define STD_OUTPUT_HANDLE ((u64)0xFFFFFFF5) +#define STD_ERROR_HANDLE ((u64)0xFFFFFFF4) +#define SHIM_STDOUT_HANDLE ((u64)0x10000001) +#define SHIM_STDERR_HANDLE ((u64)0x10000002) +#define SHIM_STDIN_HANDLE ((u64)0x10000003) + +static u64 __attribute__((ms_abi)) shim_GetStdHandle(u64 n_std_handle) { + if (n_std_handle == STD_OUTPUT_HANDLE) return SHIM_STDOUT_HANDLE; + if (n_std_handle == STD_ERROR_HANDLE) return SHIM_STDERR_HANDLE; + if (n_std_handle == STD_INPUT_HANDLE) return SHIM_STDIN_HANDLE; + return 0; +} + +static u64 __attribute__((ms_abi)) shim_WriteFile(u64 handle, u64 buffer, u64 length, + u64 bytes_written_ptr, u64 overlapped) { + (void)overlapped; + if (handle == SHIM_STDOUT_HANDLE || handle == SHIM_STDERR_HANDLE) { + const char *buf = (const char *)buffer; + shim_output(buf, length); + if (bytes_written_ptr) *(u64 *)bytes_written_ptr = length; + return 1; /* TRUE */ + } + return 0; +} + +static u64 __attribute__((ms_abi)) shim_ReadFile(u64 handle, u64 buffer, u64 max_length, + u64 bytes_read_ptr, u64 overlapped) { + (void)handle; (void)buffer; (void)max_length; (void)overlapped; + /* 暂不支持输入读取 */ + if (bytes_read_ptr) *(u64 *)bytes_read_ptr = 0; + return 1; +} + +static u64 __attribute__((ms_abi)) shim_ExitProcess(u64 exit_code) { + pe_shim_exit_process(exit_code); + return 0; /* 不返回 */ +} + +static u64 __attribute__((ms_abi)) shim_GetCommandLineA(void) { + return (u64)g_cmdline; +} + +static u64 __attribute__((ms_abi)) shim_GetCommandLineW(void) { + return (u64)g_cmdline; /* 简化:返回 ASCII */ +} + +static u64 __attribute__((ms_abi)) shim_GetLastError(void) { return 0; } +static u64 __attribute__((ms_abi)) shim_SetLastError(u64 code) { (void)code; return 0; } + +static u64 __attribute__((ms_abi)) shim_HeapCreate(u64 fl, u64 init, u64 max) { + (void)fl; (void)init; (void)max; + return 0x10000010; /* 假堆句柄 */ +} +static u64 __attribute__((ms_abi)) shim_HeapAlloc(u64 heap, u64 flags, u64 size) { + (void)heap; (void)flags; + return (u64)kmem_alloc(size); +} +static u64 __attribute__((ms_abi)) shim_HeapFree(u64 heap, u64 flags, u64 ptr) { + (void)heap; (void)flags; (void)ptr; + return 1; /* arena 无 free,返回 TRUE */ +} +static u64 __attribute__((ms_abi)) shim_VirtualAlloc(u64 addr, u64 size, u64 alloc_type, u64 protect) { + (void)addr; (void)alloc_type; (void)protect; + return (u64)kmem_alloc(size); +} +static u64 __attribute__((ms_abi)) shim_VirtualFree(u64 addr, u64 size, u64 free_type) { + (void)addr; (void)size; (void)free_type; + return 1; +} +static u64 __attribute__((ms_abi)) shim_GetModuleHandleA(u64 module_name) { + (void)module_name; + return 0x10000000; /* 假模块基址 */ +} +static u64 __attribute__((ms_abi)) shim_GetModuleHandleW(u64 module_name) { + (void)module_name; + return 0x10000000; +} +static u64 __attribute__((ms_abi)) shim_GetEnvironmentVariableA(u64 name, u64 buf, u64 size) { + (void)name; (void)buf; (void)size; + return 0; +} +static u64 __attribute__((ms_abi)) shim_CreateFileA(u64 filename, u64 access, u64 share, + u64 sa, u64 disp, u64 flags, u64 template) { + (void)filename; (void)access; (void)share; (void)sa; (void)disp; (void)flags; (void)template; + return 0; /* 暂不支持文件 */ +} +static u64 __attribute__((ms_abi)) shim_CloseHandle(u64 handle) { (void)handle; return 1; } +static u64 __attribute__((ms_abi)) shim_GetFileSize(u64 handle, u64 high) { + (void)handle; (void)high; return 0; +} +static u64 __attribute__((ms_abi)) shim_SetFilePointer(u64 handle, i64 dist, u64 high_ptr, u64 method) { + (void)handle; (void)dist; (void)high_ptr; (void)method; return 0; +} + +/* ===== msvcrt.dll shims (64位, ms_abi) ===== */ + +static void shim_msvcrt_output(const char *s) { + shim_output_str(s); +} + +/* printf — 简化版,只支持 %s %d %c %x %u %% */ +static u64 __attribute__((ms_abi)) shim_printf(u64 fmt, u64 a1, u64 a2, u64 a3) { + /* 可变参数:MS x64 ABI 下 a1-a3 在 RDX/R8/R9,更多参数在栈上。 + * 简化处理:只处理前 3 个参数。 */ + const char *f = (const char *)fmt; + u64 args[3] = {a1, a2, a3}; + int argi = 0; + char outbuf[512]; + int oi = 0; + for (int i = 0; f[i] && oi < 510; i++) { + if (f[i] == '%' && f[i+1]) { + i++; + switch (f[i]) { + case 's': { + const char *s = (const char *)(args[argi++ & 3]); + if (s) while (*s && oi < 510) outbuf[oi++] = *s++; + break; + } + case 'd': { + i64 v = (i64)args[argi++ & 3]; + if (v < 0) { outbuf[oi++] = '-'; v = -v; } + char tmp[24]; int ti = 0; + if (v == 0) tmp[ti++] = '0'; + while (v > 0 && ti < 23) { tmp[ti++] = '0' + (v % 10); v /= 10; } + while (ti > 0 && oi < 510) outbuf[oi++] = tmp[--ti]; + break; + } + case 'u': { + u64 v = args[argi++ & 3]; + char tmp[24]; int ti = 0; + if (v == 0) tmp[ti++] = '0'; + while (v > 0 && ti < 23) { tmp[ti++] = '0' + (v % 10); v /= 10; } + while (ti > 0 && oi < 510) outbuf[oi++] = tmp[--ti]; + break; + } + case 'x': { + u64 v = args[argi++ & 3]; + char tmp[24]; int ti = 0; + if (v == 0) tmp[ti++] = '0'; + while (v > 0 && ti < 23) { int d = (int)(v & 0xF); tmp[ti++] = d < 10 ? '0'+d : 'a'+d-10; v >>= 4; } + while (ti > 0 && oi < 510) outbuf[oi++] = tmp[--ti]; + break; + } + case 'c': { + outbuf[oi++] = (char)(args[argi++ & 3] & 0xFF); + break; + } + case '%': outbuf[oi++] = '%'; break; + default: outbuf[oi++] = '%'; outbuf[oi++] = f[i]; break; + } + } else { + outbuf[oi++] = f[i]; + } + } + outbuf[oi] = 0; + shim_msvcrt_output(outbuf); + return (u64)oi; +} + +static u64 __attribute__((ms_abi)) shim_puts(u64 str) { + const char *s = (const char *)str; + shim_msvcrt_output(s); + shim_msvcrt_output("\n"); + return 0; +} + +static u64 __attribute__((ms_abi)) shim_putchar(u64 ch) { + serial_putc((char)(ch & 0xFF)); + return ch & 0xFF; +} + +static u64 __attribute__((ms_abi)) shim_malloc(u64 size) { + return (u64)kmem_alloc(size); +} +static u64 __attribute__((ms_abi)) shim_calloc(u64 count, u64 size) { + u64 total = count * size; + u8 *p = kmem_alloc(total); + if (p) for (u64 i = 0; i < total; i++) p[i] = 0; + return (u64)p; +} +static u64 __attribute__((ms_abi)) shim_free(u64 ptr) { (void)ptr; return 0; } +static u64 __attribute__((ms_abi)) shim_realloc(u64 ptr, u64 size) { + (void)ptr; return (u64)kmem_alloc(size); +} +static u64 __attribute__((ms_abi)) shim_exit(u64 code) { + pe_shim_exit_process(code); + return 0; +} + +static u64 __attribute__((ms_abi)) shim_memcpy(u64 dst, u64 src, u64 n) { + u8 *d = (u8 *)dst; const u8 *s = (const u8 *)src; + for (u64 i = 0; i < n; i++) d[i] = s[i]; + return dst; +} +static u64 __attribute__((ms_abi)) shim_memset(u64 dst, u64 val, u64 n) { + u8 *d = (u8 *)dst; u8 v = (u8)(val & 0xFF); + for (u64 i = 0; i < n; i++) d[i] = v; + return dst; +} +static u64 __attribute__((ms_abi)) shim_memmove(u64 dst, u64 src, u64 n) { + u8 *d = (u8 *)dst; const u8 *s = (const u8 *)src; + if (d < s) { for (u64 i = 0; i < n; i++) d[i] = s[i]; } + else { for (u64 i = n; i > 0; i--) d[i-1] = s[i-1]; } + return dst; +} +static u64 __attribute__((ms_abi)) shim_strlen(u64 str) { + const char *s = (const char *)str; + u64 n = 0; while (s[n]) n++; return n; +} +static u64 __attribute__((ms_abi)) shim_strcmp(u64 a, u64 b) { + const char *pa = (const char *)a; const char *pb = (const char *)b; + while (*pa && *pa == *pb) { pa++; pb++; } + return (u64)(i64)(*pa - *pb); +} +static u64 __attribute__((ms_abi)) shim_strcpy(u64 dst, u64 src) { + char *d = (char *)dst; const char *s = (const char *)src; + while (*s) *d++ = *s++; + *d = 0; + return dst; +} +static u64 __attribute__((ms_abi)) shim_strncmp(u64 a, u64 b, u64 n) { + const char *pa = (const char *)a; const char *pb = (const char *)b; + for (u64 i = 0; i < n; i++) { + if (pa[i] != pb[i]) return (u64)(i64)(pa[i] - pb[i]); + if (pa[i] == 0) return 0; + } + return 0; +} +static u64 __attribute__((ms_abi)) shim_fwrite(u64 ptr, u64 size, u64 nmemb, u64 stream) { + (void)stream; + u64 total = size * nmemb; + shim_output((const char *)ptr, total); + return nmemb; +} +static u64 __attribute__((ms_abi)) shim_fputs(u64 str, u64 stream) { + (void)stream; + shim_output_str((const char *)str); + return 0; +} +static u64 __attribute__((ms_abi)) shim_fputc(u64 ch, u64 stream) { + (void)stream; + serial_putc((char)(ch & 0xFF)); + return ch & 0xFF; +} +/* _initterm / _initterm_e — CRT 初始化,遍历函数指针表调用。 + * 简化:直接跳过(返回 0)。 */ +static u64 __attribute__((ms_abi)) shim_initterm(u64 start, u64 end) { + (void)start; (void)end; + return 0; +} +static u64 __attribute__((ms_abi)) shim_initterm_e(u64 start, u64 end) { + (void)start; (void)end; + return 0; +} + +/* 未实现 stub(64位)— 日志告警并返回 0 */ +static u64 __attribute__((ms_abi)) shim_unimpl64(u64 a0, u64 a1, u64 a2, u64 a3) { + (void)a0; (void)a1; (void)a2; (void)a3; + log_warn("[PE] unimplemented API called"); + return 0; +} + +/* 提供给 pe_shim.h 的地址获取函数 */ +u64 pe_shim_unimpl64_addr(void) { return (u64)shim_unimpl64; } +u64 pe_shim_exitprocess64_addr(void) { return (u64)shim_ExitProcess; } + +/* 获取退出码 */ +u64 pe_shim_get_exit_code(void) { return g_exit_code; } + +/* ===== shim 表 ===== */ +static const pe_shim_entry g_shim_table[] = { + /* kernel32.dll */ + {"kernel32.dll", "GetStdHandle", (void*)shim_GetStdHandle, 0}, + {"kernel32.dll", "WriteFile", (void*)shim_WriteFile, 0}, + {"kernel32.dll", "ReadFile", (void*)shim_ReadFile, 0}, + {"kernel32.dll", "ExitProcess", (void*)shim_ExitProcess, 0}, + {"kernel32.dll", "GetCommandLineA", (void*)shim_GetCommandLineA, 0}, + {"kernel32.dll", "GetCommandLineW", (void*)shim_GetCommandLineW, 0}, + {"kernel32.dll", "GetLastError", (void*)shim_GetLastError, 0}, + {"kernel32.dll", "SetLastError", (void*)shim_SetLastError, 0}, + {"kernel32.dll", "HeapCreate", (void*)shim_HeapCreate, 0}, + {"kernel32.dll", "HeapAlloc", (void*)shim_HeapAlloc, 0}, + {"kernel32.dll", "HeapFree", (void*)shim_HeapFree, 0}, + {"kernel32.dll", "VirtualAlloc", (void*)shim_VirtualAlloc, 0}, + {"kernel32.dll", "VirtualFree", (void*)shim_VirtualFree, 0}, + {"kernel32.dll", "GetModuleHandleA", (void*)shim_GetModuleHandleA, 0}, + {"kernel32.dll", "GetModuleHandleW", (void*)shim_GetModuleHandleW, 0}, + {"kernel32.dll", "GetEnvironmentVariableA",(void*)shim_GetEnvironmentVariableA, 0}, + {"kernel32.dll", "CreateFileA", (void*)shim_CreateFileA, 0}, + {"kernel32.dll", "CloseHandle", (void*)shim_CloseHandle, 0}, + {"kernel32.dll", "GetFileSize", (void*)shim_GetFileSize, 0}, + {"kernel32.dll", "SetFilePointer", (void*)shim_SetFilePointer, 0}, + /* msvcrt.dll */ + {"msvcrt.dll", "printf", (void*)shim_printf, 0}, + {"msvcrt.dll", "puts", (void*)shim_puts, 0}, + {"msvcrt.dll", "putchar", (void*)shim_putchar, 0}, + {"msvcrt.dll", "malloc", (void*)shim_malloc, 0}, + {"msvcrt.dll", "calloc", (void*)shim_calloc, 0}, + {"msvcrt.dll", "free", (void*)shim_free, 0}, + {"msvcrt.dll", "realloc", (void*)shim_realloc, 0}, + {"msvcrt.dll", "exit", (void*)shim_exit, 0}, + {"msvcrt.dll", "memcpy", (void*)shim_memcpy, 0}, + {"msvcrt.dll", "memset", (void*)shim_memset, 0}, + {"msvcrt.dll", "memmove", (void*)shim_memmove, 0}, + {"msvcrt.dll", "strlen", (void*)shim_strlen, 0}, + {"msvcrt.dll", "strcmp", (void*)shim_strcmp, 0}, + {"msvcrt.dll", "strcpy", (void*)shim_strcpy, 0}, + {"msvcrt.dll", "strncmp", (void*)shim_strncmp, 0}, + {"msvcrt.dll", "fwrite", (void*)shim_fwrite, 0}, + {"msvcrt.dll", "fputs", (void*)shim_fputs, 0}, + {"msvcrt.dll", "fputc", (void*)shim_fputc, 0}, + {"msvcrt.dll", "_initterm", (void*)shim_initterm, 0}, + {"msvcrt.dll", "_initterm_e", (void*)shim_initterm_e, 0}, +}; + +int pe_shim_lookup(const char *dll, const char *func) { + if (!dll || !func) return -1; + for (int i = 0; i < (int)(sizeof(g_shim_table)/sizeof(g_shim_table[0])); i++) { + /* 大小写不敏感比较 DLL 名 */ + const char *a = g_shim_table[i].dll; + const char *b = dll; + int match = 1; + while (*a && *b) { + char ca = *a, cb = *b; + if (ca >= 'A' && ca <= 'Z') ca += 32; + if (cb >= 'A' && cb <= 'Z') cb += 32; + if (ca != cb) { match = 0; break; } + a++; b++; + } + if (!match || *a != *b) continue; + /* 精确比较函数名 */ + const char *fa = g_shim_table[i].func; + const char *fb = func; + while (*fa && *fb) { if (*fa != *fb) break; fa++; fb++; } + if (*fa == *fb) return i; + } + return -1; +} + +const pe_shim_entry *pe_shim_get(int index) { + if (index < 0 || index >= (int)(sizeof(g_shim_table)/sizeof(g_shim_table[0]))) return 0; + return &g_shim_table[index]; +} + +int pe_shim_count(void) { + return (int)(sizeof(g_shim_table)/sizeof(g_shim_table[0])); +} diff --git a/CODE/UTSM/pe/pe_shim.h b/CODE/UTSM/pe/pe_shim.h new file mode 100644 index 0000000..3c5ae9b --- /dev/null +++ b/CODE/UTSM/pe/pe_shim.h @@ -0,0 +1,65 @@ +#ifndef PE_SHIM_H +#define PE_SHIM_H + +/* pe_shim.h — Windows API shim 表。 + * + * 将常见 Windows DLL 函数映射到 Deshab 等价实现: + * kernel32.dll!WriteFile → 写串口 + 帧缓冲 + * kernel32.dll!HeapAlloc → kmem_alloc + * msvcrt.dll!printf → 串口输出 + * ... + * + * 64位 PE:shim 用 __attribute__((ms_abi)) 声明,PE 代码以 MS x64 ABI 调用, + * IAT 直接填 shim 函数地址,原生执行。 + * 32位 PE:解释器在 CALL IAT 时拦截,从模拟栈读参数调用 fn32,结果写 EAX。 + */ + +#include + +/* 32位 shim:从模拟栈读取参数(cdecl 语义,调用者清栈)。 + * esp 指向模拟栈(第一个参数在 esp[0])。返回值 = EAX。 */ +typedef u64 (*pe_shim_fn32)(void *esp); + +/* shim 表条目 */ +typedef struct pe_shim_entry { + const char *dll; /* 小写 DLL 名,如 "kernel32.dll" */ + const char *func; /* 函数名,如 "WriteFile" */ + void *fn64; /* 64位 ms_abi 函数地址(可为 NULL) */ + pe_shim_fn32 fn32; /* 32位实现(可为 NULL) */ +} pe_shim_entry; + +/* 按 dll+func 名查找 shim。返回表索引,-1 未找到。 */ +int pe_shim_lookup(const char *dll, const char *func); + +/* 获取 shim 表条目 */ +const pe_shim_entry *pe_shim_get(int index); + +/* 获取 shim 表大小 */ +int pe_shim_count(void); + +/* 生成一个唯一的"未实现 stub"地址。 + * 64位:返回一个固定 stub 函数地址,被调用时日志告警并返回 0。 + * 32位:返回一个特殊标记值,解释器识别后日志告警。 */ +u64 pe_shim_unimpl_stub(void); + +/* 初始化 shim 子系统(注册全局状态,如 cmdline) */ +void pe_shim_init(const char *cmdline); + +/* ExitProcess 实现:通过 __builtin_longjmp 跳回 pe_run。 + * 64位和32位共用。exit_code = 进程退出码。 */ +void pe_shim_exit_process(u64 exit_code); + +/* pe_run 调用:保存上下文(__builtin_setjmp)。 + * 返回 0 = 首次(继续执行 PE),1 = ExitProcess 触发(longjmp 回来)。 */ +int pe_shim_setup_exit(void); + +/* 获取上次 ExitProcess 的退出码 */ +u64 pe_shim_get_exit_code(void); + +/* 判断地址是否为 ExitProcess shim(解释器用) */ +int pe_shim_is_exit_call(u64 target); + +/* 获取 cmdline(shim 内部用) */ +const char *pe_shim_get_cmdline(void); + +#endif /* PE_SHIM_H */ diff --git a/CODE/UTSM/pe/x86emu32.c b/CODE/UTSM/pe/x86emu32.c new file mode 100644 index 0000000..2e7ec93 --- /dev/null +++ b/CODE/UTSM/pe/x86emu32.c @@ -0,0 +1,975 @@ +/* x86emu32.c — x86-32 指令解释器(PE32 执行引擎)。 + * + * 寄存器级解释执行 32位 PE 代码。覆盖 mingw-w64 -O0/-O1 控制台程序常用指令: + * ALU(ADD/OR/ADC/SBB/AND/SUB/XOR/CMP)、MOV(全形式)、PUSH/POP、 + * INC/DEC/NEG/NOT、MUL/IMUL/DIV/IDIV、移位/旋转、LEA、XCHG、MOVSX/MOVZX、 + * JMP/Jcc/CALL/RET/LOOP、字符串指令(+REP)、ENTER/LEAVE、INT(shim 调度)。 + * + * 遇不支持指令时设置 error 并停止。 + */ + +#include "x86emu32.h" +#include "pe_shim.h" +#include +#include + +/* ===== 内存访问(带边界检查) ===== */ +u8 x86emu32_read8(x86emu_state *emu, u32 addr) { + if (addr >= emu->mem_size) { emu->error = 1; emu->error_msg = "read8 OOB"; return 0; } + return emu->mem[addr]; +} +u16 x86emu32_read16(x86emu_state *emu, u32 addr) { + if (addr + 1 >= emu->mem_size) { emu->error = 1; emu->error_msg = "read16 OOB"; return 0; } + return (u16)emu->mem[addr] | ((u16)emu->mem[addr+1] << 8); +} +u32 x86emu32_read32(x86emu_state *emu, u32 addr) { + if (addr + 3 >= emu->mem_size) { emu->error = 1; emu->error_msg = "read32 OOB"; return 0; } + return (u32)emu->mem[addr] | ((u32)emu->mem[addr+1] << 8) | + ((u32)emu->mem[addr+2] << 16) | ((u32)emu->mem[addr+3] << 24); +} +void x86emu32_write8(x86emu_state *emu, u32 addr, u8 val) { + if (addr >= emu->mem_size) { emu->error = 1; emu->error_msg = "write8 OOB"; return; } + emu->mem[addr] = val; +} +void x86emu32_write16(x86emu_state *emu, u32 addr, u16 val) { + if (addr + 1 >= emu->mem_size) { emu->error = 1; emu->error_msg = "write16 OOB"; return; } + emu->mem[addr] = (u8)(val & 0xFF); + emu->mem[addr+1] = (u8)((val >> 8) & 0xFF); +} +void x86emu32_write32(x86emu_state *emu, u32 addr, u32 val) { + if (addr + 3 >= emu->mem_size) { emu->error = 1; emu->error_msg = "write32 OOB"; return; } + emu->mem[addr] = (u8)(val & 0xFF); + emu->mem[addr+1] = (u8)((val >> 8) & 0xFF); + emu->mem[addr+2] = (u8)((val >> 16) & 0xFF); + emu->mem[addr+3] = (u8)((val >> 24) & 0xFF); +} + +/* ===== 栈操作 ===== */ +void x86emu32_push32(x86emu_state *emu, u32 val) { + emu->regs[X86_ESP] -= 4; + x86emu32_write32(emu, emu->regs[X86_ESP], val); +} +u32 x86emu32_pop32(x86emu_state *emu) { + u32 val = x86emu32_read32(emu, emu->regs[X86_ESP]); + emu->regs[X86_ESP] += 4; + return val; +} + +u64 x86emu32_read_arg(x86emu_state *emu, int esp_offset) { + return x86emu32_read32(emu, emu->regs[X86_ESP] + (u32)esp_offset * 4); +} + +/* ===== 寄存器访问 ===== */ +/* 32位寄存器名:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI(对应 0-7) */ +/* 8位寄存器映射:AL,CL,DL,BL,AH,CH,DH,BH(0-3 = 低8位, 4-7 = 高8位) */ +static u8 get_reg8(x86emu_state *emu, int idx) { + if (idx < 4) return (u8)(emu->regs[idx] & 0xFF); + return (u8)((emu->regs[idx - 4] >> 8) & 0xFF); +} +static void set_reg8(x86emu_state *emu, int idx, u8 val) { + if (idx < 4) { + emu->regs[idx] = (emu->regs[idx] & 0xFFFFFF00) | val; + } else { + emu->regs[idx - 4] = (emu->regs[idx - 4] & 0xFFFF00FF) | ((u32)val << 8); + } +} +static u16 get_reg16(x86emu_state *emu, int idx) { + return (u16)(emu->regs[idx] & 0xFFFF); +} +static void set_reg16(x86emu_state *emu, int idx, u16 val) { + emu->regs[idx] = (emu->regs[idx] & 0xFFFF0000) | val; +} +static u32 get_reg32(x86emu_state *emu, int idx) { return emu->regs[idx]; } +static void set_reg32(x86emu_state *emu, int idx, u32 val) { emu->regs[idx] = val; } + +/* ===== EFLAGS 辅助 ===== */ +static void set_flag(x86emu_state *emu, u32 mask, int on) { + if (on) emu->eflags |= mask; else emu->eflags &= ~mask; +} +static int get_flag(x86emu_state *emu, u32 mask) { return (emu->eflags & mask) ? 1 : 0; } + +static u8 parity8(u8 v) { + v ^= v >> 4; v ^= v >> 2; v ^= v >> 1; + return (v & 1) == 0; +} + +static void set_zf_sf_pf(x86emu_state *emu, u32 result, int size) { + u32 mask = (size == 1) ? 0xFF : (size == 2) ? 0xFFFF : 0xFFFFFFFF; + result &= mask; + set_flag(emu, X86_ZF, result == 0); + if (size == 1) set_flag(emu, X86_SF, (result & 0x80) != 0); + else if (size == 2) set_flag(emu, X86_SF, (result & 0x8000) != 0); + else set_flag(emu, X86_SF, (result & 0x80000000) != 0); + set_flag(emu, X86_PF, parity8((u8)result)); +} + +static void set_cf_add(x86emu_state *emu, u32 a, u32 b, int size) { + u32 mask = (size == 1) ? 0xFF : (size == 2) ? 0xFFFF : 0xFFFFFFFF; + set_flag(emu, X86_CF, ((u64)(a & mask) + (u64)(b & mask)) > mask); +} +static void set_cf_sub(x86emu_state *emu, u32 a, u32 b, int size) { + u32 mask = (size == 1) ? 0xFF : (size == 2) ? 0xFFFF : 0xFFFFFFFF; + set_flag(emu, X86_CF, (a & mask) < (b & mask)); +} +static void set_of_add(x86emu_state *emu, u32 a, u32 b, u32 r, int size) { + u32 sign = (size == 1) ? 0x80 : (size == 2) ? 0x8000 : 0x80000000; + set_flag(emu, X86_OF, (~(a ^ b) & (a ^ r) & sign) != 0); +} +static void set_of_sub(x86emu_state *emu, u32 a, u32 b, u32 r, int size) { + u32 sign = (size == 1) ? 0x80 : (size == 2) ? 0x8000 : 0x80000000; + set_flag(emu, X86_OF, ((a ^ b) & (a ^ r) & sign) != 0); +} + +/* ===== 指令取 ===== */ +static u8 fetch8(x86emu_state *emu) { + u8 v = x86emu32_read8(emu, emu->eip); + emu->eip++; + return v; +} +static u16 fetch16(x86emu_state *emu) { + u16 v = x86emu32_read16(emu, emu->eip); + emu->eip += 2; + return v; +} +static u32 fetch32(x86emu_state *emu) { + u32 v = x86emu32_read32(emu, emu->eip); + emu->eip += 4; + return v; +} + +/* ===== ModR/M 解码 ===== */ +typedef struct { + int mod; + int reg; /* reg 字段(也可作 opcode 扩展) */ + int rm; /* rm 字段 */ + u32 ea; /* 有效地址(若 mod != 3) */ + int has_ea; /* 是否有内存操作数 */ +} modrm_dec; + +static modrm_dec decode_modrm(x86emu_state *emu) { + modrm_dec m; + u8 b = fetch8(emu); + m.mod = (b >> 6) & 3; + m.reg = (b >> 3) & 7; + m.rm = b & 7; + m.has_ea = 0; + m.ea = 0; + + if (m.mod == 3) return m; /* 寄存器直接寻址 */ + + m.has_ea = 1; + u32 base = 0; + int has_sib = (m.rm == 4); + + if (has_sib) { + u8 sib = fetch8(emu); + int scale = 1 << ((sib >> 6) & 3); + int index = (sib >> 3) & 7; + int base_reg = sib & 7; + base = emu->regs[base_reg]; + if (index != 4) { /* ESP 不作 index */ + base += emu->regs[index] * (u32)scale; + } + } else { + base = emu->regs[m.rm]; + } + + /* disp */ + if (m.mod == 0) { + if (!has_sib && m.rm == 5) { + /* disp32 直接地址 */ + base = fetch32(emu); + } + } else if (m.mod == 1) { + base += (u32)(i32)(i8)fetch8(emu); /* disp8 符号扩展 */ + } else { /* mod == 2 */ + base += fetch32(emu); + } + + m.ea = base; + return m; +} + +/* 读/写 modr/m 操作数 */ +static u32 read_rm32(x86emu_state *emu, const modrm_dec *m) { + if (m->mod == 3) return get_reg32(emu, m->rm); + return x86emu32_read32(emu, m->ea); +} +static void write_rm32(x86emu_state *emu, const modrm_dec *m, u32 val) { + if (m->mod == 3) set_reg32(emu, m->rm, val); + else x86emu32_write32(emu, m->ea, val); +} +static u8 read_rm8(x86emu_state *emu, const modrm_dec *m) { + if (m->mod == 3) return get_reg8(emu, m->rm); + return x86emu32_read8(emu, m->ea); +} +static void write_rm8(x86emu_state *emu, const modrm_dec *m, u8 val) { + if (m->mod == 3) set_reg8(emu, m->rm, val); + else x86emu32_write8(emu, m->ea, val); +} +static u16 read_rm16(x86emu_state *emu, const modrm_dec *m) { + if (m->mod == 3) return get_reg16(emu, m->rm); + return x86emu32_read16(emu, m->ea); +} +static void write_rm16(x86emu_state *emu, const modrm_dec *m, u16 val) { + if (m->mod == 3) set_reg16(emu, m->rm, val); + else x86emu32_write16(emu, m->ea, val); +} + +/* ===== ALU 操作(按 op 索引:0=ADD 1=OR 2=ADC 3=SBB 4=AND 5=SUB 6=XOR 7=CMP) ===== */ +static u32 alu_op(x86emu_state *emu, int op, u32 a, u32 b, int size) { + u32 r = 0; + u32 mask = (size == 1) ? 0xFF : (size == 2) ? 0xFFFF : 0xFFFFFFFF; + a &= mask; b &= mask; + switch (op) { + case 0: r = a + b; set_cf_add(emu, a, b, size); set_of_add(emu, a, b, r, size); set_zf_sf_pf(emu, r, size); break; + case 1: r = a | b; set_flag(emu, X86_CF, 0); set_flag(emu, X86_OF, 0); set_zf_sf_pf(emu, r, size); break; + case 2: { int cf = get_flag(emu, X86_CF); r = a + b + cf; set_cf_add(emu, a + cf, b, size); set_of_add(emu, a, b, r, size); set_zf_sf_pf(emu, r, size); break; } + case 3: { int cf = get_flag(emu, X86_CF); r = a - b - cf; set_cf_sub(emu, a - cf, b, size); set_of_sub(emu, a, b, r, size); set_zf_sf_pf(emu, r, size); break; } + case 4: r = a & b; set_flag(emu, X86_CF, 0); set_flag(emu, X86_OF, 0); set_zf_sf_pf(emu, r, size); break; + case 5: r = a - b; set_cf_sub(emu, a, b, size); set_of_sub(emu, a, b, r, size); set_zf_sf_pf(emu, r, size); break; + case 6: r = a ^ b; set_flag(emu, X86_CF, 0); set_flag(emu, X86_OF, 0); set_zf_sf_pf(emu, r, size); break; + case 7: r = a - b; set_cf_sub(emu, a, b, size); set_of_sub(emu, a, b, r, size); set_zf_sf_pf(emu, r, size); break; /* CMP 不写回 */ + } + return r & mask; +} + +/* ===== 移位操作(按 op:ROL/ROR/RCL/RCR/SHL/SHR/SAR/SAL) ===== */ +static u32 shift_op(x86emu_state *emu, int op, u32 val, u8 count, int size) { + u32 mask = (size == 1) ? 0xFF : (size == 2) ? 0xFFFF : 0xFFFFFFFF; + val &= mask; + count &= 0x1F; + if (count == 0) return val; + u32 r = val; + switch (op) { + case 4: case 6: /* SHL / SAL */ + r = val << count; + set_flag(emu, X86_CF, (val >> (32 - count)) & 1); + break; + case 5: /* SHR */ + r = val >> count; + set_flag(emu, X86_CF, (val >> (count - 1)) & 1); + break; + case 7: /* SAR */ + r = (u32)((i32)val >> count); + set_flag(emu, X86_CF, (val >> (count - 1)) & 1); + break; + case 0: /* ROL */ + r = ((val << count) | (val >> (size * 8 - count))) & mask; + set_flag(emu, X86_CF, r & 1); + break; + case 1: /* ROR */ + r = ((val >> count) | (val << (size * 8 - count))) & mask; + set_flag(emu, X86_CF, (r >> (size * 8 - 1)) & 1); + break; + default: break; /* RCL/RCR 暂简化 */ + } + if (op >= 4) set_zf_sf_pf(emu, r, size); /* SHL/SHR/SAR 影响 ZF/SF/PF */ + return r & mask; +} + +/* ===== IAT CALL 拦截 ===== */ +/* stdcall 参数个数表(kernel32.dll 函数:callee 清栈)。 + * msvcrt.dll 函数为 cdecl(caller 清栈),不在此表。 + * key = shim 函数名首字符 + 长度组合的简单 hash,避免 strcmp。 + * 简化:直接用函数指针地址查表。 */ +static int try_iat_call(x86emu_state *emu, u32 target) { + /* 检查是否为合成 shim 地址 */ + if (target >= 0x00010000 && target < 0x00020000) { + int shim_idx = target & 0xFFFF; + if (shim_idx == 0xFFFF) { + /* 未实现 stub */ + log_warn("[PE32] call unimplemented API"); + emu->regs[X86_EAX] = 0; + return 1; + } + const pe_shim_entry *se = pe_shim_get(shim_idx); + if (!se) { + log_warn("[PE32] shim index out of range"); + emu->regs[X86_EAX] = 0; + return 1; + } + + /* 从模拟栈读参数。CALL 被拦截前未 push 返回地址, + * 所以 ESP 指向第一个参数。 */ + u32 esp_val = emu->regs[X86_ESP]; + if (esp_val + 32 > emu->mem_size) { + emu->error = 1; + emu->error_msg = "shim esp OOB"; + return 1; + } + u64 a0 = x86emu32_read32(emu, esp_val); + u64 a1 = x86emu32_read32(emu, esp_val + 4); + u64 a2 = x86emu32_read32(emu, esp_val + 8); + u64 a3 = x86emu32_read32(emu, esp_val + 12); + + u64 ret; + if (se->fn32) { + /* 有专用 32位实现 */ + void *esp_ptr = &emu->mem[esp_val]; + ret = se->fn32(esp_ptr); + } else if (se->fn64) { + /* 回退:调用 64位 shim(最多 4 个参数)。 + * MS x64 ABI 由 C 编译器处理寄存器分配。 */ + typedef u64 (*fn4_t)(u64, u64, u64, u64); + fn4_t f = (fn4_t)se->fn64; + ret = f(a0, a1, a2, a3); + + /* stdcall 清栈:kernel32.dll 函数 callee 清栈。 + * 简化:按已知参数个数调整 ESP。 + * msvcrt.dll (cdecl) 不清栈。 */ + if (se->dll[0] == 'k') { /* kernel32.dll */ + int nargs = 4; /* 默认假设 4 参数 */ + /* 按函数名精化参数个数 */ + const char *fn = se->func; + if (fn[0]=='G' && fn[1]=='e' && fn[2]=='t' && fn[3]=='S') nargs = 1; + else if (fn[0]=='W' && fn[1]=='r' && fn[2]=='i') nargs = 5; + else if (fn[0]=='R' && fn[1]=='e' && fn[2]=='a' && fn[3]=='d') nargs = 5; + else if (fn[0]=='E' && fn[1]=='x' && fn[2]=='i' && fn[3]=='t') nargs = 1; + else if (fn[0]=='G' && fn[1]=='e' && fn[2]=='t' && fn[3]=='C') nargs = 0; + else if (fn[0]=='G' && fn[1]=='e' && fn[2]=='t' && fn[3]=='L') nargs = 0; + else if (fn[0]=='S' && fn[1]=='e' && fn[2]=='t' && fn[3]=='L') nargs = 1; + else if (fn[0]=='H' && fn[1]=='e' && fn[2]=='a' && fn[3]=='p') nargs = 3; + else if (fn[0]=='V' && fn[1]=='i' && fn[2]=='r') nargs = 4; + else if (fn[0]=='G' && fn[1]=='e' && fn[2]=='t' && fn[3]=='M') nargs = 1; + else if (fn[0]=='C' && fn[1]=='l' && fn[2]=='o') nargs = 1; + else if (fn[0]=='C' && fn[1]=='r' && fn[2]=='e') nargs = 7; + emu->regs[X86_ESP] += (u32)(nargs * 4); + } + } else { + log_warn("[PE32] no shim impl"); + ret = 0; + } + emu->regs[X86_EAX] = (u32)ret; + return 1; + } + /* 检查是否为 ExitProcess 特殊地址(64位 ExitProcess shim) */ + if (pe_shim_is_exit_call((u64)target)) { + pe_shim_exit_process(emu->regs[X86_EAX] ? emu->regs[X86_EAX] : 0); + emu->exited = 1; + return 1; + } + return 0; /* 不是 IAT/shim 调用 */ +} + +/* ===== 条件跳转判断 ===== */ +static int check_jcc(x86emu_state *emu, int cond) { + int zf = get_flag(emu, X86_ZF); + int sf = get_flag(emu, X86_SF); + int of = get_flag(emu, X86_OF); + int cf = get_flag(emu, X86_CF); + int pf = get_flag(emu, X86_PF); + switch (cond & 0xF) { + case 0x0: return of; + case 0x1: return !of; + case 0x2: return cf; + case 0x3: return !cf; + case 0x4: return zf; + case 0x5: return !zf; + case 0x6: return cf || zf; + case 0x7: return !cf && !zf; + case 0x8: return sf; + case 0x9: return !sf; + case 0xA: return pf; + case 0xB: return !pf; + case 0xC: return sf != of; + case 0xD: return sf == of; + case 0xE: return zf || (sf != of); + case 0xF: return !zf && (sf == of); + } + return 0; +} + +/* ===== 主执行循环 ===== */ +int x86emu32_run(x86emu_state *emu, u64 *exit_code) { + emu->insn_limit = 500000000ULL; /* 5亿指令上限防死循环 */ + + while (!emu->exited && !emu->error && emu->insn_count < emu->insn_limit) { + emu->insn_count++; + + /* 前缀处理 */ + int operand_size = 4; /* 默认 32位 */ + int rep_prefix = 0; /* 0=无, 1=REP/REPE, 2=REPNE(记录但暂未用于字符串指令) */ + u8 op; + int prefix_done = 0; + while (!prefix_done) { + op = fetch8(emu); + switch (op) { + case 0x66: operand_size = 2; break; + case 0x67: break; /* 地址大小前缀(忽略,32位寻址) */ + case 0xF0: break; /* LOCK */ + case 0xF2: rep_prefix = 2; break; /* REPNE */ + case 0xF3: rep_prefix = 1; break; /* REP/REPE */ + case 0x26: case 0x2E: case 0x36: case 0x3E: case 0x64: case 0x65: break; /* 段前缀忽略 */ + default: prefix_done = 1; break; + } + } + (void)rep_prefix; /* 记录前缀,字符串指令暂未使用 */ + + /* 操作数大小:对 0x66 前缀,32位操作变 16位 */ + int osz = operand_size; /* 4 或 2 */ + + switch (op) { + /* ---- ALU modr/m(0x00-0x3F)---- */ + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x18: case 0x19: case 0x1A: case 0x1B: + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x28: case 0x29: case 0x2A: case 0x2B: + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x38: case 0x39: case 0x3A: case 0x3B: { + int alu = (op >> 3) & 7; + int dir = op & 1; /* 0: r/m8←r8, 1: r/m32←r32 (或反向) */ + int is8 = !(op & 4); /* 0x00-0x03=8位, 0x04+ =32位 */ + modrm_dec m = decode_modrm(emu); + if (is8) { + u8 a, b; int cmp_only = (alu == 7); + if (dir == 0) { a = read_rm8(emu, &m); b = get_reg8(emu, m.reg); } + else { a = get_reg8(emu, m.reg); b = read_rm8(emu, &m); } + u8 r = (u8)alu_op(emu, alu, a, b, 1); + if (alu != 7) { if (dir == 0) write_rm8(emu, &m, r); else set_reg8(emu, m.reg, r); } + (void)cmp_only; + } else { + u32 a, b; + if (dir == 0) { a = read_rm32(emu, &m); b = get_reg32(emu, m.reg); } + else { a = get_reg32(emu, m.reg); b = read_rm32(emu, &m); } + u32 r = alu_op(emu, alu, a, b, osz); + if (alu != 7) { if (dir == 0) write_rm32(emu, &m, r); else set_reg32(emu, m.reg, r); } + if (osz == 2) { if (alu != 7) { if (dir == 0) write_rm16(emu, &m, (u16)r); else set_reg16(emu, m.reg, (u16)r); } } + } + break; + } + /* ---- ALU AL/EAX, imm ---- */ + case 0x04: case 0x0C: case 0x14: case 0x1C: + case 0x24: case 0x2C: case 0x34: case 0x3C: { + int alu = (op >> 3) & 7; + u8 imm = fetch8(emu); + u8 r = (u8)alu_op(emu, alu, get_reg8(emu, 0), imm, 1); + if (alu != 7) set_reg8(emu, 0, r); + break; + } + case 0x05: case 0x0D: case 0x15: case 0x1D: + case 0x25: case 0x2D: case 0x35: case 0x3D: { + int alu = (op >> 3) & 7; + if (osz == 2) { + u16 imm = fetch16(emu); + u16 r = (u16)alu_op(emu, alu, get_reg16(emu, 0), imm, 2); + if (alu != 7) set_reg16(emu, 0, r); + } else { + u32 imm = fetch32(emu); + u32 r = alu_op(emu, alu, get_reg32(emu, 0), imm, 4); + if (alu != 7) set_reg32(emu, 0, r); + } + break; + } + /* ---- INC/DEC reg(0x40-0x4F)---- */ + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: { + int r = op - 0x40; + int cf = get_flag(emu, X86_CF); + if (osz == 2) { u16 v = get_reg16(emu, r); u16 res = (u16)alu_op(emu, 0, v, 1, 2); set_reg16(emu, r, res); } + else { u32 v = get_reg32(emu, r); u32 res = alu_op(emu, 0, v, 1, 4); set_reg32(emu, r, res); } + set_flag(emu, X86_CF, cf); /* INC 不影响 CF */ + break; + } + case 0x48: case 0x49: case 0x4A: case 0x4B: + case 0x4C: case 0x4D: case 0x4E: case 0x4F: { + int r = op - 0x48; + int cf = get_flag(emu, X86_CF); + if (osz == 2) { u16 v = get_reg16(emu, r); u16 res = (u16)alu_op(emu, 5, v, 1, 2); set_reg16(emu, r, res); } + else { u32 v = get_reg32(emu, r); u32 res = alu_op(emu, 5, v, 1, 4); set_reg32(emu, r, res); } + set_flag(emu, X86_CF, cf); + break; + } + /* ---- PUSH/POP reg ---- */ + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + x86emu32_push32(emu, get_reg32(emu, op - 0x50)); + break; + case 0x58: case 0x59: case 0x5A: case 0x5B: + case 0x5C: case 0x5D: case 0x5E: case 0x5F: + set_reg32(emu, op - 0x58, x86emu32_pop32(emu)); + break; + /* ---- PUSHA/POPA ---- */ + case 0x60: { + u32 sp = emu->regs[X86_ESP]; + x86emu32_push32(emu, emu->regs[X86_EAX]); + x86emu32_push32(emu, emu->regs[X86_ECX]); + x86emu32_push32(emu, emu->regs[X86_EDX]); + x86emu32_push32(emu, emu->regs[X86_EBX]); + x86emu32_push32(emu, sp); + x86emu32_push32(emu, emu->regs[X86_EBP]); + x86emu32_push32(emu, emu->regs[X86_ESI]); + x86emu32_push32(emu, emu->regs[X86_EDI]); + break; + } + case 0x61: { + emu->regs[X86_EDI] = x86emu32_pop32(emu); + emu->regs[X86_ESI] = x86emu32_pop32(emu); + emu->regs[X86_EBP] = x86emu32_pop32(emu); + x86emu32_pop32(emu); /* 忽略原始 ESP */ + emu->regs[X86_EBX] = x86emu32_pop32(emu); + emu->regs[X86_EDX] = x86emu32_pop32(emu); + emu->regs[X86_ECX] = x86emu32_pop32(emu); + emu->regs[X86_EAX] = x86emu32_pop32(emu); + break; + } + /* ---- PUSH imm ---- */ + case 0x68: x86emu32_push32(emu, fetch32(emu)); break; + case 0x6A: x86emu32_push32(emu, (u32)(i32)(i8)fetch8(emu)); break; + /* ---- IMUL r, r/m, imm ---- */ + case 0x69: case 0x6B: { + modrm_dec m = decode_modrm(emu); + u32 src = read_rm32(emu, &m); + u32 imm = (op == 0x69) ? fetch32(emu) : (u32)(i32)(i8)fetch8(emu); + i64 result = (i64)(i32)src * (i64)(i32)imm; + set_reg32(emu, m.reg, (u32)result); + set_flag(emu, X86_OF, result > 0x7FFFFFFFLL || result < -0x80000000LL); + set_flag(emu, X86_CF, get_flag(emu, X86_OF)); + break; + } + /* ---- Jcc rel8 ---- */ + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7A: case 0x7B: + case 0x7C: case 0x7D: case 0x7E: case 0x7F: { + i8 rel = (i8)fetch8(emu); + if (check_jcc(emu, op & 0xF)) emu->eip += (u32)(i32)rel; + break; + } + /* ---- ALU r/m, imm8 (0x80/0x82) / r/m, imm32 (0x81) ---- */ + case 0x80: case 0x82: { + modrm_dec m = decode_modrm(emu); + u8 imm = fetch8(emu); + u8 a = read_rm8(emu, &m); + u8 r = (u8)alu_op(emu, m.reg, a, imm, 1); + if (m.reg != 7) write_rm8(emu, &m, r); + break; + } + case 0x81: { + modrm_dec m = decode_modrm(emu); + if (osz == 2) { + u16 imm = fetch16(emu); + u16 a = read_rm16(emu, &m); + u16 r = (u16)alu_op(emu, m.reg, a, imm, 2); + if (m.reg != 7) write_rm16(emu, &m, r); + } else { + u32 imm = fetch32(emu); + u32 a = read_rm32(emu, &m); + u32 r = alu_op(emu, m.reg, a, imm, 4); + if (m.reg != 7) write_rm32(emu, &m, r); + } + break; + } + case 0x83: { + modrm_dec m = decode_modrm(emu); + u32 imm = (u32)(i32)(i8)fetch8(emu); + if (osz == 2) { + u16 a = read_rm16(emu, &m); + u16 r = (u16)alu_op(emu, m.reg, a, imm & 0xFFFF, 2); + if (m.reg != 7) write_rm16(emu, &m, r); + } else { + u32 a = read_rm32(emu, &m); + u32 r = alu_op(emu, m.reg, a, imm, 4); + if (m.reg != 7) write_rm32(emu, &m, r); + } + break; + } + /* ---- TEST ---- */ + case 0x84: case 0x85: { + modrm_dec m = decode_modrm(emu); + if (op == 0x84) { + u8 a = read_rm8(emu, &m); u8 b = get_reg8(emu, m.reg); + alu_op(emu, 4, a, b, 1); /* AND 不写回 */ + } else if (osz == 2) { + u16 a = read_rm16(emu, &m); u16 b = get_reg16(emu, m.reg); + alu_op(emu, 4, a, b, 2); + } else { + u32 a = read_rm32(emu, &m); u32 b = get_reg32(emu, m.reg); + alu_op(emu, 4, a, b, 4); + } + break; + } + /* ---- XCHG ---- */ + case 0x86: case 0x87: { + modrm_dec m = decode_modrm(emu); + if (op == 0x86) { + u8 a = read_rm8(emu, &m); u8 b = get_reg8(emu, m.reg); + write_rm8(emu, &m, b); set_reg8(emu, m.reg, a); + } else if (osz == 2) { + u16 a = read_rm16(emu, &m); u16 b = get_reg16(emu, m.reg); + write_rm16(emu, &m, b); set_reg16(emu, m.reg, a); + } else { + u32 a = read_rm32(emu, &m); u32 b = get_reg32(emu, m.reg); + write_rm32(emu, &m, b); set_reg32(emu, m.reg, a); + } + break; + } + /* ---- MOV ---- */ + case 0x88: { modrm_dec m = decode_modrm(emu); write_rm8(emu, &m, get_reg8(emu, m.reg)); break; } + case 0x89: { + modrm_dec m = decode_modrm(emu); + if (osz == 2) write_rm16(emu, &m, get_reg16(emu, m.reg)); + else write_rm32(emu, &m, get_reg32(emu, m.reg)); + break; + } + case 0x8A: { modrm_dec m = decode_modrm(emu); set_reg8(emu, m.reg, read_rm8(emu, &m)); break; } + case 0x8B: { + modrm_dec m = decode_modrm(emu); + if (osz == 2) set_reg16(emu, m.reg, read_rm16(emu, &m)); + else set_reg32(emu, m.reg, read_rm32(emu, &m)); + break; + } + /* ---- LEA ---- */ + case 0x8D: { + modrm_dec m = decode_modrm(emu); + if (osz == 2) set_reg16(emu, m.reg, (u16)m.ea); + else set_reg32(emu, m.reg, m.ea); + break; + } + /* ---- NOP / XCHG EAX,reg ---- */ + case 0x90: break; /* NOP */ + case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: { + int r = op - 0x90; + u32 tmp = emu->regs[0]; emu->regs[0] = emu->regs[r]; emu->regs[r] = tmp; + break; + } + /* ---- CWDE/CDQ ---- */ + case 0x98: /* CWDE (32位) / CBW (16位) */ + if (osz == 2) set_reg16(emu, 0, (u16)(i16)(i8)get_reg8(emu, 0)); + else emu->regs[0] = (u32)(i32)(i16)get_reg16(emu, 0); + break; + case 0x99: /* CDQ (32位) / CWD (16位) */ + if (osz == 2) set_reg16(emu, 2, (get_reg16(emu, 0) & 0x8000) ? 0xFFFF : 0); + else emu->regs[2] = (emu->regs[0] & 0x80000000) ? 0xFFFFFFFF : 0; + break; + /* ---- MOV moffs ---- */ + case 0xA0: { u32 addr = fetch32(emu); set_reg8(emu, 0, x86emu32_read8(emu, addr)); break; } + case 0xA1: { u32 addr = fetch32(emu); if (osz==2) set_reg16(emu,0,x86emu32_read16(emu,addr)); else set_reg32(emu,0,x86emu32_read32(emu,addr)); break; } + case 0xA2: { u32 addr = fetch32(emu); x86emu32_write8(emu, addr, get_reg8(emu, 0)); break; } + case 0xA3: { u32 addr = fetch32(emu); if (osz==2) x86emu32_write16(emu,addr,get_reg16(emu,0)); else x86emu32_write32(emu,addr,get_reg32(emu,0)); break; } + /* ---- 字符串指令(简化:无 REP 单步)---- */ + case 0xA4: { /* MOVSB */ + u32 si = emu->regs[X86_ESI], di = emu->regs[X86_EDI]; + u8 v = x86emu32_read8(emu, si); + x86emu32_write8(emu, di, v); + int df = get_flag(emu, X86_DF); + si += df ? -1 : 1; di += df ? -1 : 1; + emu->regs[X86_ESI] = si; emu->regs[X86_EDI] = di; + break; + } + case 0xA5: { /* MOVSD/W */ + u32 si = emu->regs[X86_ESI], di = emu->regs[X86_EDI]; + int df = get_flag(emu, X86_DF); int step = (osz==2)?2:4; + if (osz==2) { u16 v=x86emu32_read16(emu,si); x86emu32_write16(emu,di,v); } + else { u32 v=x86emu32_read32(emu,si); x86emu32_write32(emu,di,v); } + si += df ? -step : step; di += df ? -step : step; + emu->regs[X86_ESI]=si; emu->regs[X86_EDI]=di; + break; + } + case 0xAA: { /* STOSB */ + u32 di = emu->regs[X86_EDI]; + x86emu32_write8(emu, di, get_reg8(emu, 0)); + int df = get_flag(emu, X86_DF); + di += df ? -1 : 1; emu->regs[X86_EDI] = di; + break; + } + case 0xAB: { /* STOSD/W */ + u32 di = emu->regs[X86_EDI]; int df = get_flag(emu, X86_DF); int step=(osz==2)?2:4; + if (osz==2) x86emu32_write16(emu,di,get_reg16(emu,0)); + else x86emu32_write32(emu,di,emu->regs[0]); + di += df?-step:step; emu->regs[X86_EDI]=di; + break; + } + case 0xAC: { /* LODSB */ + u32 si = emu->regs[X86_ESI]; + set_reg8(emu, 0, x86emu32_read8(emu, si)); + int df = get_flag(emu, X86_DF); + si += df ? -1 : 1; emu->regs[X86_ESI] = si; + break; + } + case 0xAD: { /* LODSD/W */ + u32 si = emu->regs[X86_ESI]; int df=get_flag(emu,X86_DF); int step=(osz==2)?2:4; + if (osz==2) set_reg16(emu,0,x86emu32_read16(emu,si)); else set_reg32(emu,0,x86emu32_read32(emu,si)); + si += df?-step:step; emu->regs[X86_ESI]=si; + break; + } + /* ---- TEST AL/EAX, imm ---- */ + case 0xA8: { u8 imm = fetch8(emu); alu_op(emu, 4, get_reg8(emu,0), imm, 1); break; } + case 0xA9: { + if (osz==2) { u16 imm=fetch16(emu); alu_op(emu,4,get_reg16(emu,0),imm,2); } + else { u32 imm=fetch32(emu); alu_op(emu,4,emu->regs[0],imm,4); } + break; + } + /* ---- MOV reg, imm ---- */ + case 0xB0: case 0xB1: case 0xB2: case 0xB3: + case 0xB4: case 0xB5: case 0xB6: case 0xB7: + set_reg8(emu, op - 0xB0, fetch8(emu)); + break; + case 0xB8: case 0xB9: case 0xBA: case 0xBB: + case 0xBC: case 0xBD: case 0xBE: case 0xBF: + if (osz==2) set_reg16(emu, op-0xB8, fetch16(emu)); + else set_reg32(emu, op-0xB8, fetch32(emu)); + break; + /* ---- shift r/m, imm8 ---- */ + case 0xC0: case 0xC1: { + modrm_dec m = decode_modrm(emu); + u8 count = fetch8(emu); + if (op == 0xC0) { u8 v = read_rm8(emu,&m); write_rm8(emu,&m,(u8)shift_op(emu,m.reg,v,count,1)); } + else if (osz==2) { u16 v=read_rm16(emu,&m); write_rm16(emu,&m,(u16)shift_op(emu,m.reg,v,count,2)); } + else { u32 v=read_rm32(emu,&m); write_rm32(emu,&m,shift_op(emu,m.reg,v,count,4)); } + break; + } + /* ---- RET ---- */ + case 0xC2: { u16 imm = fetch16(emu); emu->eip = x86emu32_pop32(emu); emu->regs[X86_ESP] += imm; break; } + case 0xC3: { emu->eip = x86emu32_pop32(emu); break; } + /* ---- MOV r/m, imm ---- */ + case 0xC6: { modrm_dec m = decode_modrm(emu); u8 imm = fetch8(emu); write_rm8(emu,&m,imm); break; } + case 0xC7: { + modrm_dec m = decode_modrm(emu); + if (osz==2) { u16 imm=fetch16(emu); write_rm16(emu,&m,imm); } + else { u32 imm=fetch32(emu); write_rm32(emu,&m,imm); } + break; + } + /* ---- LEAVE ---- */ + case 0xC9: + emu->regs[X86_ESP] = emu->regs[X86_EBP]; + emu->regs[X86_EBP] = x86emu32_pop32(emu); + break; + /* ---- INT3 / INT ---- */ + case 0xCC: /* INT3 */ + emu->error = 1; emu->error_msg = "INT3 breakpoint"; + break; + case 0xCD: { /* INT imm8 */ + u8 v = fetch8(emu); (void)v; + /* 简化:忽略软件中断 */ + break; + } + /* ---- shift r/m, 1 / CL ---- */ + case 0xD0: case 0xD1: case 0xD2: case 0xD3: { + modrm_dec m = decode_modrm(emu); + u8 count = (op & 2) ? (get_reg8(emu, 1) & 0x1F) : 1; + int is8 = !(op & 1); + if (is8) { u8 v=read_rm8(emu,&m); write_rm8(emu,&m,(u8)shift_op(emu,m.reg,v,count,1)); } + else if (osz==2) { u16 v=read_rm16(emu,&m); write_rm16(emu,&m,(u16)shift_op(emu,m.reg,v,count,2)); } + else { u32 v=read_rm32(emu,&m); write_rm32(emu,&m,shift_op(emu,m.reg,v,count,4)); } + break; + } + /* ---- CALL rel32 ---- */ + case 0xE8: { + i32 rel = (i32)fetch32(emu); + x86emu32_push32(emu, emu->eip); /* return address */ + emu->eip += (u32)rel; + break; + } + /* ---- JMP rel32 ---- */ + case 0xE9: { i32 rel = (i32)fetch32(emu); emu->eip += (u32)rel; break; } + /* ---- JMP rel8 ---- */ + case 0xEB: { i8 rel = (i8)fetch8(emu); emu->eip += (u32)(i32)rel; break; } + /* ---- LOOP ---- */ + case 0xE0: case 0xE1: case 0xE2: { + i8 rel = (i8)fetch8(emu); + u32 cx = emu->regs[X86_ECX] - 1; + emu->regs[X86_ECX] = cx; + int do_jump = (cx != 0); + if (op == 0xE0 && get_flag(emu, X86_ZF)) do_jump = 0; /* LOOPNE */ + if (op == 0xE1 && !get_flag(emu, X86_ZF)) do_jump = 0; /* LOOPE */ + if (do_jump) emu->eip += (u32)(i32)rel; + break; + } + case 0xE3: { /* JECXZ */ + i8 rel = (i8)fetch8(emu); + if (emu->regs[X86_ECX] == 0) emu->eip += (u32)(i32)rel; + break; + } + /* ---- CALL/JMP r/m ---- */ + case 0xFF: { + modrm_dec m = decode_modrm(emu); + switch (m.reg) { + case 0: { /* INC r/m */ + int cf=get_flag(emu,X86_CF); + if (osz==2) { u16 v=read_rm16(emu,&m); write_rm16(emu,&m,(u16)alu_op(emu,0,v,1,2)); } + else { u32 v=read_rm32(emu,&m); write_rm32(emu,&m,alu_op(emu,0,v,1,4)); } + set_flag(emu,X86_CF,cf); + break; + } + case 1: { /* DEC r/m */ + int cf=get_flag(emu,X86_CF); + if (osz==2) { u16 v=read_rm16(emu,&m); write_rm16(emu,&m,(u16)alu_op(emu,5,v,1,2)); } + else { u32 v=read_rm32(emu,&m); write_rm32(emu,&m,alu_op(emu,5,v,1,4)); } + set_flag(emu,X86_CF,cf); + break; + } + case 2: { /* CALL r/m32 */ + u32 target = read_rm32(emu, &m); + if (try_iat_call(emu, target)) break; + x86emu32_push32(emu, emu->eip); + emu->eip = target; + break; + } + case 4: { /* JMP r/m32 */ + u32 target = read_rm32(emu, &m); + if (try_iat_call(emu, target)) { emu->exited = 1; break; } + emu->eip = target; + break; + } + case 6: { /* PUSH r/m32 */ + x86emu32_push32(emu, read_rm32(emu, &m)); + break; + } + default: + emu->error = 1; emu->error_msg = "FF /reg unsupported"; + break; + } + break; + } + /* ---- F6/F7 组 ---- */ + case 0xF6: { + modrm_dec m = decode_modrm(emu); + if (m.reg == 0 || m.reg == 1) { /* TEST r/m8, imm8 */ + u8 imm = fetch8(emu); + alu_op(emu, 4, read_rm8(emu,&m), imm, 1); + } else if (m.reg == 2) { /* NOT r/m8 */ + write_rm8(emu, &m, ~read_rm8(emu, &m)); + } else if (m.reg == 3) { /* NEG r/m8 */ + u8 v = read_rm8(emu, &m); + u8 r = (u8)(-(i8)v); + set_flag(emu, X86_CF, v != 0); + set_zf_sf_pf(emu, r, 1); + write_rm8(emu, &m, r); + } else if (m.reg == 4) { /* MUL r/m8 */ + u16 result = (u16)get_reg8(emu, 0) * (u16)read_rm8(emu, &m); + set_reg8(emu, 0, (u8)result); + set_reg8(emu, 4, (u8)(result >> 8)); + set_flag(emu, X86_CF, result >> 8 != 0); + set_flag(emu, X86_OF, get_flag(emu, X86_CF)); + } else { + emu->error = 1; emu->error_msg = "F6 /reg unsupported"; + } + break; + } + case 0xF7: { + modrm_dec m = decode_modrm(emu); + if (m.reg == 0 || m.reg == 1) { /* TEST r/m32, imm32 */ + if (osz==2) { u16 imm=fetch16(emu); alu_op(emu,4,read_rm16(emu,&m),imm,2); } + else { u32 imm=fetch32(emu); alu_op(emu,4,read_rm32(emu,&m),imm,4); } + } else if (m.reg == 2) { /* NOT */ + if (osz==2) write_rm16(emu,&m,~read_rm16(emu,&m)); + else write_rm32(emu,&m,~read_rm32(emu,&m)); + } else if (m.reg == 3) { /* NEG */ + if (osz==2) { u16 v=read_rm16(emu,&m); u16 r=(u16)(-(i16)v); set_flag(emu,X86_CF,v!=0); set_zf_sf_pf(emu,r,2); write_rm16(emu,&m,r); } + else { u32 v=read_rm32(emu,&m); u32 r=(u32)(-(i32)v); set_flag(emu,X86_CF,v!=0); set_zf_sf_pf(emu,r,4); write_rm32(emu,&m,r); } + } else if (m.reg == 4) { /* MUL */ + if (osz==2) { u32 result=(u32)get_reg16(emu,0)*(u32)read_rm16(emu,&m); set_reg16(emu,0,(u16)result); set_reg16(emu,2,(u16)(result>>16)); set_flag(emu,X86_CF,result>>16!=0); set_flag(emu,X86_OF,get_flag(emu,X86_CF)); } + else { u64 result=(u64)emu->regs[0]*(u64)read_rm32(emu,&m); emu->regs[0]=(u32)result; emu->regs[2]=(u32)(result>>32); set_flag(emu,X86_CF,result>>32!=0); set_flag(emu,X86_OF,get_flag(emu,X86_CF)); } + } else if (m.reg == 5) { /* IMUL */ + if (osz==2) { i32 result=(i32)(i16)get_reg16(emu,0)*(i32)(i16)read_rm16(emu,&m); set_reg16(emu,0,(u16)result); set_reg16(emu,2,(u16)(result>>16)); set_flag(emu,X86_OF,result>32767||result<-32768); set_flag(emu,X86_CF,get_flag(emu,X86_OF)); } + else { i64 result=(i64)(i32)emu->regs[0]*(i64)(i32)read_rm32(emu,&m); emu->regs[0]=(u32)result; emu->regs[2]=(u32)(result>>32); set_flag(emu,X86_OF,result>0x7FFFFFFFLL||result<-0x80000000LL); set_flag(emu,X86_CF,get_flag(emu,X86_OF)); } + } else if (m.reg == 6) { /* DIV */ + if (osz==2) { u32 dividend=((u32)get_reg16(emu,2)<<16)|get_reg16(emu,0); u16 divisor=read_rm16(emu,&m); if(divisor){set_reg16(emu,0,(u16)(dividend/divisor));set_reg16(emu,2,(u16)(dividend%divisor));} } + else { u64 dividend=((u64)emu->regs[2]<<32)|emu->regs[0]; u32 divisor=read_rm32(emu,&m); if(divisor){emu->regs[0]=(u32)(dividend/divisor);emu->regs[2]=(u32)(dividend%divisor);} } + } else if (m.reg == 7) { /* IDIV */ + if (osz==2) { i32 dividend=((i32)(i16)get_reg16(emu,2)<<16)|(i16)get_reg16(emu,0); i16 divisor=(i16)read_rm16(emu,&m); if(divisor){set_reg16(emu,0,(u16)(dividend/divisor));set_reg16(emu,2,(u16)(dividend%divisor));} } + else { i64 dividend=((i64)(i32)emu->regs[2]<<32)|(i32)emu->regs[0]; i32 divisor=(i32)read_rm32(emu,&m); if(divisor){emu->regs[0]=(u32)(dividend/divisor);emu->regs[2]=(u32)(dividend%divisor);} } + } + break; + } + /* ---- CLC/STC/CLI/STI/CLD/STD ---- */ + case 0xF8: set_flag(emu, X86_CF, 0); break; + case 0xF9: set_flag(emu, X86_CF, 1); break; + case 0xFA: break; /* CLI(忽略) */ + case 0xFB: break; /* STI(忽略) */ + case 0xFC: set_flag(emu, X86_DF, 0); break; + case 0xFD: set_flag(emu, X86_DF, 1); break; + /* ---- INC/DEC r/m8 ---- */ + case 0xFE: { + modrm_dec m = decode_modrm(emu); + int cf = get_flag(emu, X86_CF); + u8 v = read_rm8(emu, &m); + if (m.reg == 0) write_rm8(emu, &m, (u8)alu_op(emu, 0, v, 1, 1)); + else if (m.reg == 1) write_rm8(emu, &m, (u8)alu_op(emu, 5, v, 1, 1)); + set_flag(emu, X86_CF, cf); + break; + } + /* ---- MOVSX/MOVZX 在 0x0F 两字节 opcode 中处理 ---- */ + case 0x0F: { /* 两字节 opcode */ + u8 op2 = fetch8(emu); + if (op2 >= 0x80 && op2 <= 0x8F) { /* Jcc rel32 */ + i32 rel = (i32)fetch32(emu); + if (check_jcc(emu, op2 & 0xF)) emu->eip += (u32)rel; + } else if (op2 == 0xAF) { /* IMUL r32, r/m32 */ + modrm_dec m = decode_modrm(emu); + i64 result = (i64)(i32)get_reg32(emu, m.reg) * (i64)(i32)read_rm32(emu, &m); + set_reg32(emu, m.reg, (u32)result); + set_flag(emu, X86_OF, result > 0x7FFFFFFFLL || result < -0x80000000LL); + set_flag(emu, X86_CF, get_flag(emu, X86_OF)); + } else if (op2 == 0xB6 || op2 == 0xB7) { /* MOVZX r32, r/m8/16 */ + modrm_dec m = decode_modrm(emu); + if (op2 == 0xB6) { set_reg32(emu, m.reg, (u32)read_rm8(emu, &m)); } + else { set_reg32(emu, m.reg, (u32)read_rm16(emu, &m)); } + } else if (op2 == 0xBE || op2 == 0xBF) { /* MOVSX r32, r/m8/16 */ + modrm_dec m = decode_modrm(emu); + if (op2 == 0xBE) { set_reg32(emu, m.reg, (u32)(i32)(i8)read_rm8(emu, &m)); } + else { set_reg32(emu, m.reg, (u32)(i32)(i16)read_rm16(emu, &m)); } + } else if (op2 == 0x1F) { /* NOP r/m(多字节 NOP) */ + decode_modrm(emu); + } else if (op2 == 0x05) { /* SYSCALL(不应出现在32位PE) */ + emu->error = 1; emu->error_msg = "SYSCALL in 32-bit PE"; + } else { + emu->error = 1; emu->error_msg = "0F opcode unsupported"; + } + break; + } + default: + emu->error = 1; emu->error_msg = "unsupported opcode"; + break; + } /* end switch(op) */ + + if (emu->error) { + log_hex64("[PE32] error at EIP=", emu->eip); + log_hex64("[PE32] opcode=", op); + log_info("[PE32] "); + log_info(emu->error_msg ? emu->error_msg : "(null)"); + break; + } + } /* end while */ + + if (emu->insn_count >= emu->insn_limit) { + log_error("[PE32] instruction limit reached"); + emu->error = 1; + } + + if (exit_code) *exit_code = emu->exit_code; + return emu->error ? -1 : 0; +} + +int x86emu32_init(x86emu_state *emu, u8 *mem, u64 mem_size, + u32 image_base, u32 image_size, + pe_iat_entry *iat_entries, int iat_count, + u32 stack_top) { + for (int i = 0; i < 8; i++) emu->regs[i] = 0; + emu->eip = 0; + emu->eflags = 0x2; /* 保留位 = 1 */ + emu->mem = mem; + emu->mem_size = mem_size; + emu->image_base = image_base; + emu->image_size = image_size; + emu->iat_entries = iat_entries; + emu->iat_count = iat_count; + emu->stack_base = stack_top; + emu->stack_size = 0x10000; /* 64KB 栈 */ + emu->heap_base = image_base + image_size; + emu->heap_size = 0x100000; /* 1MB heap */ + emu->heap_next = emu->heap_base; + emu->exited = 0; + emu->exit_code = 0; + emu->error = 0; + emu->error_msg = 0; + emu->insn_count = 0; + + /* 设置 ESP 到栈顶,对齐 16 */ + emu->regs[X86_ESP] = stack_top & ~0xFu; + emu->regs[X86_EBP] = 0; + return 0; +} diff --git a/CODE/UTSM/pe/x86emu32.h b/CODE/UTSM/pe/x86emu32.h new file mode 100644 index 0000000..96793aa --- /dev/null +++ b/CODE/UTSM/pe/x86emu32.h @@ -0,0 +1,100 @@ +#ifndef X86EMU32_H +#define X86EMU32_H + +/* x86emu32.h — x86-32 指令解释器(PE32 执行引擎)。 + * + * 寄存器级解释执行 32位 PE 代码。覆盖 mingw-w64 控制台程序常用指令集。 + * 遇不支持的指令时日志告警并返回错误。 + */ + +#include + +/* x86 通用寄存器索引 */ +enum { + X86_EAX = 0, X86_ECX, X86_EDX, X86_EBX, + X86_ESP, X86_EBP, X86_ESI, X86_EDI +}; + +/* EFLAGS 位 */ +#define X86_CF (1u << 0) +#define X86_PF (1u << 2) +#define X86_AF (1u << 4) +#define X86_ZF (1u << 6) +#define X86_SF (1u << 7) +#define X86_OF (1u << 11) +#define X86_DF (1u << 10) + +/* IAT 条目信息(解释器用于 CALL 拦截) */ +typedef struct pe_iat_entry { + u32 iat_rva; /* IAT 中该条目的 RVA */ + int shim_index; /* 对应 shim 表索引,-1 = 未实现 stub */ + u32 target_addr; /* 模拟地址(iat_rva + image_base) */ +} pe_iat_entry; + +typedef struct x86emu_state { + u32 regs[8]; /* EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI */ + u32 eip; /* 指令指针(相对 mem 基址的偏移) */ + u32 eflags; + + u8 *mem; /* guest 内存基址(= PE image_base) */ + u64 mem_size; /* guest 内存大小 */ + + u32 image_base; /* PE 加载基址(= mem,32位截断) */ + u32 image_size; + + /* IAT 表(用于 CALL 拦截) */ + pe_iat_entry *iat_entries; + int iat_count; + + /* 栈区 */ + u32 stack_base; /* 栈底(高地址) */ + u32 stack_size; + + /* heap 区 */ + u32 heap_base; + u32 heap_size; + u32 heap_next; /* 下次分配偏移 */ + + /* 退出状态 */ + int exited; /* ExitProcess 被调用 */ + u64 exit_code; + int error; /* 解释错误 */ + const char *error_msg; + + /* 指令计数限制(防死循环) */ + u64 insn_count; + u64 insn_limit; +} x86emu_state; + +/* 初始化解释器状态。 + * mem = PE image 加载基址(已映射节区+重定位+IAT 填充)。 + * iat_entries / iat_count = pe_loader 解析的 IAT 表。 + * stack_top = 栈顶 ESP 初始值(相对 mem 的偏移)。 + * 返回 0 成功。 */ +int x86emu32_init(x86emu_state *emu, u8 *mem, u64 mem_size, + u32 image_base, u32 image_size, + pe_iat_entry *iat_entries, int iat_count, + u32 stack_top); + +/* 执行直到 ExitProcess 或错误。 + * 返回 0 正常退出(exit_code 有效),负数错误。 */ +int x86emu32_run(x86emu_state *emu, u64 *exit_code); + +/* 从模拟栈读取 N 个参数(cdecl/stdcall)。 + * esp_offset = 参数相对 ESP 的偏移(0=第一个参数)。 + * 返回参数值。 */ +u64 x86emu32_read_arg(x86emu_state *emu, int esp_offset); + +/* 模拟栈 push/pop(供 shim32 回调使用) */ +void x86emu32_push32(x86emu_state *emu, u32 val); +u32 x86emu32_pop32(x86emu_state *emu); + +/* 读写模拟内存 */ +u8 x86emu32_read8(x86emu_state *emu, u32 addr); +u16 x86emu32_read16(x86emu_state *emu, u32 addr); +u32 x86emu32_read32(x86emu_state *emu, u32 addr); +void x86emu32_write8(x86emu_state *emu, u32 addr, u8 val); +void x86emu32_write16(x86emu_state *emu, u32 addr, u16 val); +void x86emu32_write32(x86emu_state *emu, u32 addr, u32 val); + +#endif /* X86EMU32_H */ diff --git a/CODE/UTSM/vmm/linux_boot.c b/CODE/UTSM/vmm/linux_boot.c index b880620..e8f8cd3 100644 --- a/CODE/UTSM/vmm/linux_boot.c +++ b/CODE/UTSM/vmm/linux_boot.c @@ -27,13 +27,9 @@ extern void vmx_vm_exit_handler(void); extern int vmexit_dispatch(void); extern u64 vmexit_get_count(void); -/* Globals shared with vmexit_asm.S (must match vmm.c) */ -extern volatile u64 g_saved_host_rsp; -extern volatile u64 g_saved_return_rip; -extern volatile int g_guest_terminated; -extern volatile u64 g_last_exit_reason; - -/* Host stack for VM-Exit (reuse the one from vmm.c) */ +/* g_saved_host_rsp / g_saved_return_rip / g_guest_terminated / + * g_last_exit_reason / g_linux_guest_active / g_guest_parked / + * g_guest_regs / g_host_stack 通过 声明(vmm.c 定义)。 */ extern u8 g_host_stack[8192]; /* CR helpers */ @@ -288,6 +284,8 @@ int linux_launch(void) { /* Reset VM-Exit state */ g_guest_terminated = 0; g_last_exit_reason = 0xFFFFFFFFULL; + g_linux_guest_active = 1; /* 标记当前 guest 为 Linux(HLT 时 park) */ + g_guest_parked = 0; /* Configure VMCS */ linux_vmcs_setup_guest_state(gi); @@ -331,11 +329,14 @@ int linux_launch(void) { if (failed) { u64 error = vmx_vmcs_read(VMCS_VMX_INSTRUCTION_ERROR); log_hex64("[LINUX] vmlaunch failed, error=", error); + g_linux_guest_active = 0; return -3; } - /* Guest terminated */ - log_info("[LINUX] guest terminated"); + /* Guest parked (HLT) — Linux daemon 已驻留,等待 linux_resume() 唤醒 */ + g_linux_guest_active = 0; /* 回到 host 上下文,清除 active 标志 */ + g_guest_parked = 1; /* 标记 guest 已 park,linux_resume() 可唤醒 */ + log_info("[LINUX] guest parked (daemon ready)"); log_hex64("[LINUX] last exit reason=", g_last_exit_reason); log_hex64("[LINUX] vmexit count=", vmexit_get_count()); diff --git a/CODE/UTSM/vmm/linux_compat.c b/CODE/UTSM/vmm/linux_compat.c new file mode 100644 index 0000000..34a618a --- /dev/null +++ b/CODE/UTSM/vmm/linux_compat.c @@ -0,0 +1,198 @@ +/* linux_compat.c — Deshab Linux 程序兼容层服务实现。 + * + * 依赖 UTSM+Linux 双内核架构与 IPC 共享内存。Linux guest 作为 daemon + * 在 VMX guest 中 park-and-resume 驻留。UTSM 侧通过 IPC ring 向 daemon + * 发送 exec 请求,并 vmresume 唤醒 guest 执行。 + * + * 执行流程(exec()): + * 1. 构建 ipc_exec_request,通过 ipc_shm_send(UTSM_MSG_EXEC_REQUEST) 入队 + * 2. 调用 linux_resume() → vmresume → Linux daemon 读取请求 → fork+exec + * 3. daemon 通过 linux_to_utsm ring 回传 UTSM_MSG_EXEC_STDOUT/STDERR/EXIT + * 4. daemon HLT park → linux_resume() 返回 + * 5. exec() 从 ring 读取所有响应消息,拼接 stdout,取 exit code + * + * 服务指针通过 dsk_boot_context.reserved[5] 暴露给 shell/cmd/desktop。 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* ===== 内部辅助 ===== */ + +static u64 my_strlen(const char *s) { + u64 n = 0; + if (s) while (s[n]) n++; + return n; +} + +static void my_memcpy(void *d, const void *s, u64 n) { + u8 *dd = (u8 *)d; + const u8 *ss = (const u8 *)s; + while (n--) *dd++ = *ss++; +} + +static void my_memzero(void *d, u64 n) { + u8 *p = (u8 *)d; + while (n--) *p++ = 0; +} + +/* ===== 服务状态 ===== */ + +static int g_compat_ready; /* 1 = linux_launch 已成功,服务可用 */ + +void linux_compat_init(void) { + /* 在 linux_launch 成功(guest parked)后由 kernel_main 调用 */ + if (linux_is_parked() && ipc_shm_is_ready()) { + g_compat_ready = 1; + log_info("[LNXC] service ready (guest parked, IPC ok)"); + } else { + g_compat_ready = 0; + log_warn("[LNXC] service not ready (guest not parked or IPC down)"); + } +} + +/* ===== 服务方法 ===== */ + +static int lxc_is_available(void) { + return (g_compat_ready && linux_is_parked() && ipc_shm_is_ready()) ? 1 : 0; +} + +/* 等待 Linux daemon 的响应消息。 + * linux_resume() 返回后,daemon 已 park,所有响应消息应已在 ring 中。 + * 我们循环 pop 直到拿到 EXEC_EXIT 或 ring 空(超时保护)。 */ +static int lxc_drain_responses(char *out_buf, u64 out_cap, u64 *out_len, + u64 *exit_code) { + u64 written = 0; + int got_exit = 0; + u64 exit_val = 0; + + /* 最多消费 256 条消息(防止异常情况下死循环) */ + for (int i = 0; i < 256; i++) { + struct utsm_ipc_msg msg; + if (ipc_shm_recv(&msg) != 0) { + /* ring 空 */ + break; + } + + switch (msg.type) { + case UTSM_MSG_EXEC_STDOUT: + case UTSM_MSG_EXEC_STDERR: { + /* 拼接到 stdout 缓冲区(stderr 也并入,简化调用方处理) */ + u32 n = msg.data_len; + if (out_buf && out_cap > written) { + u32 space = (u32)(out_cap - written); + u32 to_copy = (n < space) ? n : space; + my_memcpy(out_buf + written, msg.data, to_copy); + written += to_copy; + } + break; + } + case UTSM_MSG_EXEC_EXIT: { + if (msg.data_len >= sizeof(struct ipc_exec_exit)) { + struct ipc_exec_exit ex; + my_memcpy(&ex, msg.data, sizeof(ex)); + exit_val = ex.exit_code; + } + got_exit = 1; + break; + } + default: + /* 忽略其他消息(HELLO/PONG 等) */ + break; + } + + if (got_exit) break; + } + + if (out_len) *out_len = written; + if (exit_code) *exit_code = exit_val; + + return got_exit ? 0 : -4; /* -4 = 超时无 exit 响应 */ +} + +static int lxc_exec(const char *path, int argc, const char *const *argv, + char *stdout_buf, u64 stdout_cap, u64 *stdout_len, + u64 *exit_code) { + if (!lxc_is_available()) { + return -1; + } + if (!path) { + return -1; + } + + /* 1. 构建 exec 请求 */ + struct ipc_exec_request req; + my_memzero(&req, sizeof(req)); + req.argc = (u32)argc; + req.flags = 0; + + u64 plen = my_strlen(path); + if (plen >= UTSM_EXEC_PATH_MAX) plen = UTSM_EXEC_PATH_MAX - 1; + my_memcpy(req.path, path, plen); + req.path[plen] = 0; + + /* 拼接 argv_blob:每个 argv[i] NUL-terminated */ + u64 blob_off = 0; + if (argc > UTSM_EXEC_MAX_ARGS) argc = UTSM_EXEC_MAX_ARGS; + for (int i = 0; i < argc; i++) { + if (!argv[i]) continue; + u64 alen = my_strlen(argv[i]); + if (blob_off + alen + 1 > UTSM_EXEC_ARGV_MAX) break; + my_memcpy(req.argv_blob + blob_off, argv[i], alen); + blob_off += alen; + req.argv_blob[blob_off++] = 0; + } + + /* 2. 发送 EXEC_REQUEST */ + if (ipc_shm_send(UTSM_MSG_EXEC_REQUEST, &req, sizeof(req)) != 0) { + log_warn("[LNXC] exec: failed to enqueue request"); + return -2; + } + + /* 3. 唤醒 Linux guest 执行 */ + int r = linux_resume(); + if (r != 0) { + log_hex64("[LNXC] exec: linux_resume failed=", (u64)(i64)r); + return -3; + } + + /* 4. 读取响应(daemon 已再次 park,响应在 ring 中) */ + return lxc_drain_responses(stdout_buf, stdout_cap, stdout_len, exit_code); +} + +static int lxc_status(char *buf, u64 cap) { + if (!buf || cap == 0) return 0; + const char *s; + if (!g_compat_ready) { + s = "linux_compat: not initialized"; + } else if (!linux_is_parked()) { + s = "linux_compat: guest not parked"; + } else if (!ipc_shm_is_ready()) { + s = "linux_compat: IPC down"; + } else { + s = "linux_compat: ready"; + } + u64 n = my_strlen(s); + if (n >= cap) n = cap - 1; + my_memcpy(buf, s, n); + buf[n] = 0; + return (int)n; +} + +/* ===== 服务表 ===== */ + +static const linux_compat_service g_lxc_service = { + LINUX_COMPAT_MAGIC, + lxc_is_available, + lxc_exec, + lxc_status, +}; + +const linux_compat_service *linux_compat_get_service(void) { + return &g_lxc_service; +} diff --git a/CODE/UTSM/vmm/linux_resume.c b/CODE/UTSM/vmm/linux_resume.c new file mode 100644 index 0000000..d6b5f35 --- /dev/null +++ b/CODE/UTSM/vmm/linux_resume.c @@ -0,0 +1,68 @@ +/* linux_resume.c — Linux guest park-and-resume 唤醒实现。 + * + * linux_resume() 通过 vmresume 唤醒已 HLT park 的 Linux guest。 + * guest daemon 处理 IPC 请求后再次 HLT park,控制权返回 host。 + * + * 控制流与 linux_launch() 的 vmlaunch 对称: + * - 保存 host RSP 和返回 RIP + * - 执行 vmresume(成功则进入 guest,不返回) + * - guest HLT → VM-Exit → handle_hlt park → g_guest_terminated=1 + * - vmexit_asm.S 恢复 host RSP,jmp 到返回 RIP + * - linux_resume() 继续执行,返回 0 + */ + +#include +#include +#include +#include +#include + +int linux_is_parked(void) { + return g_guest_parked ? 1 : 0; +} + +int linux_resume(void) { + if (!g_guest_parked) { + log_warn("[LINUX] resume: guest not parked"); + return -1; + } + + /* 设置 active 标志:handle_hlt 检查此标志决定 park(而非 terminate) */ + g_linux_guest_active = 1; + g_guest_terminated = 0; + g_guest_parked = 0; + + /* vmresume 控制流(与 linux_launch 的 vmlaunch 对称): + * - 失败:执行下一条指令,failed=1 + * - 成功:进入 guest,不返回。VM-Exit 后跳到 vmx_vm_exit_handler。 + * - guest park 时,handler 恢复 saved_rsp 并 jmp 到 post_resume 标签。 */ + int failed; + __asm__ volatile( + "movq %%rsp, g_saved_host_rsp(%%rip)\n\t" /* 保存当前 RSP */ + "leaq 1f(%%rip), %%rax\n\t" /* 取 post_resume 标签地址 */ + "movq %%rax, g_saved_return_rip(%%rip)\n\t" /* 保存返回 RIP */ + "vmresume\n\t" /* 唤醒 guest */ + /* ---- 失败路径 ---- */ + "movl $1, %0\n\t" + "jmp 2f\n\t" + /* ---- post_resume: park 路径 jmp 到这里 ---- */ + "1:\n\t" + "movl $0, %0\n\t" + "2:\n\t" + : "=r"(failed) + :: "rax", "rcx", "rdx", "rsi", "rdi", + "r8", "r9", "r10", "r11", "memory" + ); + + /* 回到 host 上下文 */ + g_linux_guest_active = 0; + g_guest_parked = 1; /* guest 再次 park */ + + if (failed) { + u64 error = vmx_vmcs_read(VMCS_VMX_INSTRUCTION_ERROR); + log_hex64("[LINUX] vmresume failed, error=", error); + return -2; + } + + return 0; +} diff --git a/CODE/UTSM/vmm/vmexit.c b/CODE/UTSM/vmm/vmexit.c index 176bf5f..08eee02 100644 --- a/CODE/UTSM/vmm/vmexit.c +++ b/CODE/UTSM/vmm/vmexit.c @@ -23,11 +23,21 @@ static void log_exit_diagnostics(u64 reason, u64 qualification, u64 rip, u64 len log_hex64("[VMEXIT] ilen=", len); } -/* 处理 HLT:Phase 1.1 自检场景下表示 guest 完成测试,停止 guest。 */ +/* 处理 HLT: + * - self-test guest:终止(guest 完成测试) + * - Linux guest:park(推进 RIP 越过 HLT,exit-to-host,设 g_guest_parked=1) + * host 调用 linux_resume() 时 vmresume 唤醒 guest 从 HLT 之后继续执行。 */ static int handle_hlt(u64 rip, u64 instr_len, int *out_resume) { - *out_resume = 0; /* 不 resume,guest 终止 */ - log_info("[VMEXIT] HLT - guest halted"); - (void)rip; (void)instr_len; + *out_resume = 0; /* 两种情况都 exit-to-host */ + if (g_linux_guest_active) { + /* Linux guest: park — 推进 RIP 越过 HLT 指令 */ + vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len); + g_guest_parked = 1; + log_info("[VMEXIT] HLT - Linux guest parked"); + } else { + /* self-test guest: 终止 */ + log_info("[VMEXIT] HLT - guest halted"); + } return 0; } diff --git a/CODE/UTSM/vmm/vmm.c b/CODE/UTSM/vmm/vmm.c index b973286..4561416 100644 --- a/CODE/UTSM/vmm/vmm.c +++ b/CODE/UTSM/vmm/vmm.c @@ -46,6 +46,12 @@ volatile u64 g_saved_return_rip; volatile int g_guest_terminated; volatile u64 g_last_exit_reason; +/* Linux guest park-and-resume 状态。 + * g_linux_guest_active: 1 = 当前运行的 guest 是 Linux(HLT 时 park,不终止) + * g_guest_parked: 1 = Linux guest 已 HLT 驻留,等待 vmresume 唤醒 */ +volatile int g_linux_guest_active; +volatile int g_guest_parked; + /* Guest GPR save area (referenced by vmexit_asm.S). * VM-Exit 时 CPU 不自动保存 guest GPR,由汇编入口保存到此结构。 * C 处理器可读写此结构(如 VMCALL 返回值写入 rax)。 */ diff --git a/CODE/cmd/MAKEFILE b/CODE/cmd/MAKEFILE new file mode 100644 index 0000000..616e02b --- /dev/null +++ b/CODE/cmd/MAKEFILE @@ -0,0 +1,25 @@ +CC := clang +LD := ld.lld + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/deshab64/cmd.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../UTSM/include -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 + @if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/cmd/linker.ld b/CODE/cmd/linker.ld new file mode 100644 index 0000000..473e5fc --- /dev/null +++ b/CODE/cmd/linker.ld @@ -0,0 +1,24 @@ +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) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic +} diff --git a/CODE/cmd/main.c b/CODE/cmd/main.c new file mode 100644 index 0000000..edef93f --- /dev/null +++ b/CODE/cmd/main.c @@ -0,0 +1,961 @@ +/* Deshab CMD — Windows 风格命令行(PE/EXE 兼容层前端) + * + * 黑底白字 cmd.exe 风格,PS/2 键盘输入。 + * 内置命令:dir/cd/type/copy/del/ren/mkdir/rmdir/echo/cls/ver/set/help/exit + * PE 执行:输入 foo.exe 或 foo → 从 FAT32 根读取 → 调用 UTSM PE 服务执行 + * - PE32+ (64位):UTSM 原生 Ring0 执行 + * - PE32 (32位):UTSM 内嵌 x86-32 解释器执行 + * Esc 返回桌面。 + */ + +#include "../UTSM/include/utsm/dsk.h" +#include "../UTSM/include/utsm/pe.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define COM1 0x3F8 + +static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); } +static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; } + +/* ---- TSC 计时 ---- */ +static u64 g_tsc_per_ms = 0; + +static __inline__ u64 rdtsc_cmd(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +static void tsc_calibrate_cmd(void) { + outb(0x43, 0x30); + outb(0x40, 0x7c); + outb(0x40, 0x2e); + u64 tsc_start = rdtsc_cmd(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_cmd(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +static void serial_wait_tx(void) { + if (g_tsc_per_ms == 0) tsc_calibrate_cmd(); + if (g_tsc_per_ms == 0) { + for (u32 i = 0; i < 100000; i++) { if (inb(COM1 + 5) & 0x20) break; } + return; + } + u64 deadline = rdtsc_cmd() + g_tsc_per_ms / 10; + while (rdtsc_cmd() < deadline) { + if (inb(COM1 + 5) & 0x20) break; + __asm__ volatile("pause"); + } +} + +static void sputc(char c) { serial_wait_tx(); outb(COM1, (unsigned char)c); } +static void swrite(const char *s) { while (*s) { if (*s == '\n') sputc('\r'); sputc(*s++); } } +static void logl(const char *s) { swrite(s); swrite("\n"); } + +/* ASCII 字体 — 必须在 deshab_ui.h 之前包含 */ +#include "../firstInit/ascii_bitmaps.c" +#include "../UTSM/include/utsm/deshab_ui.h" +/* FAT32 共享读写库 */ +#include "../tools/fat32_io.h" + +/* ============================================================ + * 颜色定义 — Windows cmd.exe 风格(黑底浅灰字) + * ============================================================ */ + +#define CMD_BG 0xFF000000u /* 纯黑背景 */ +#define CMD_TEXT 0xFFC0C0C0u /* 浅灰文字(cmd.exe 默认) */ +#define CMD_PROMPT 0xFFFFFFFFu /* 白色提示符 */ +#define CMD_ERROR 0xFFFF6060u /* 浅红错误 */ +#define CMD_OK 0xFF60FF60u /* 浅绿成功 */ +#define CMD_INFO 0xFF60A0FFu /* 浅蓝信息 */ +#define CMD_DIM 0xFF808080u /* 灰色暗淡 */ +#define CMD_WARN 0xFFFFFF60u /* 黄色警告 */ + +/* ============================================================ + * 终端缓冲区与渲染 + * ============================================================ */ + +#define TERM_COLS 100 +#define TERM_ROWS 40 +#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS) + +static du_context g_ctx; +static u64 fb_a, fb_w, fb_h, fb_p; + +static u8 term_ch[TERM_MAX_CHARS]; +static u32 term_fg[TERM_MAX_CHARS]; +static int term_w = TERM_COLS; +static int term_h = TERM_ROWS; +static int cur_col = 0; +static int cur_row = 0; +static int cur_visible = 1; +static int term_start_x, term_start_y; + +static const dsk_boot_context *g_boot_ctx = 0; +static u64 g_kernel_api = 0; +static const pe_service *g_pe_svc = 0; + +/* dev_mode 串口镜像 */ +static int g_serial_mirror = 0; + +static void term_init_pos(void) { + int margin = 4; + term_start_x = margin; + term_start_y = margin; + int avail_w = (int)fb_w - margin * 2; + int avail_h = (int)fb_h - margin * 2; + term_w = avail_w / (int)DU_ASCII_STEP; + term_h = avail_h / (int)DU_ASCII_LINE_H; + if (term_w < 10) term_w = 10; + if (term_h < 5) term_h = 5; + if (term_w > TERM_COLS) term_w = TERM_COLS; + if (term_h > TERM_ROWS) term_h = TERM_ROWS; +} + +static void term_clear_buf(void) { + for (int i = 0; i < TERM_MAX_CHARS; i++) { + term_ch[i] = ' '; + term_fg[i] = CMD_TEXT; + } + cur_col = 0; + cur_row = 0; +} + +static void term_scroll(void) { + for (int r = 1; r < term_h; r++) { + for (int c = 0; c < term_w; c++) { + int src = r * TERM_COLS + c; + int dst = (r - 1) * TERM_COLS + c; + term_ch[dst] = term_ch[src]; + term_fg[dst] = term_fg[src]; + } + } + for (int c = 0; c < term_w; c++) { + term_ch[(term_h - 1) * TERM_COLS + c] = ' '; + term_fg[(term_h - 1) * TERM_COLS + c] = CMD_TEXT; + } + cur_row = term_h - 1; + cur_col = 0; +} + +static void term_newline(void) { + cur_col = 0; + cur_row++; + if (cur_row >= term_h) term_scroll(); +} + +static void term_putc_color(char c, u32 color) { + if (g_serial_mirror) { if (c == '\n') sputc('\r'); sputc(c); } + if (c == '\n') { term_newline(); return; } + if (c == '\r') { cur_col = 0; return; } + if (c == '\t') { + int spaces = 8 - (cur_col % 8); + for (int i = 0; i < spaces; i++) term_putc_color(' ', color); + return; + } + if (c < ' ' || c > '~') c = '.'; + if (cur_col >= term_w) term_newline(); + int idx = cur_row * TERM_COLS + cur_col; + term_ch[idx] = (u8)c; + term_fg[idx] = color; + cur_col++; +} + +static void term_putc(char c) { term_putc_color(c, CMD_TEXT); } +static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); } +static void term_puts(const char *s) { term_puts_color(s, CMD_TEXT); } + +/* ---- 数字转字符串 ---- */ +static void term_putu(u32 v) { + char buf[12]; + int i = 11; + buf[i--] = 0; + if (v == 0) { buf[i--] = '0'; } + while (v > 0 && i >= 0) { buf[i--] = (char)('0' + (v % 10)); v /= 10; } + term_puts(&buf[i + 1]); +} + +static void term_putx(u32 v) { + static const char hex[] = "0123456789ABCDEF"; + term_putc('0'); term_putc('x'); + for (int i = 28; i >= 0; i -= 4) { + u32 nib = (v >> i) & 0xF; + if (nib != 0 || i <= 12) term_putc(hex[nib]); + } +} + +/* ---- 终端全屏渲染 ---- */ +static void term_redraw_all(void) { + du_fill_rect(&g_ctx, 0, 0, (i64)fb_w, (i64)fb_h, CMD_BG); + for (int r = 0; r < term_h; r++) { + i64 y = term_start_y + (i64)r * (i64)DU_ASCII_LINE_H; + for (int c = 0; c < term_w; c++) { + int idx = r * TERM_COLS + c; + i64 x = term_start_x + (i64)c * (i64)DU_ASCII_STEP; + du_draw_char(&g_ctx, (char)term_ch[idx], x, y, term_fg[idx], CMD_BG); + } + } +} + +static void term_redraw_cursor(void) { + if (cur_col >= term_w) return; + i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + du_fill_rect(&g_ctx, x, y, (i64)DU_ASCII_STEP - 1, (i64)DU_ASCII_CELL_H, CMD_PROMPT); +} + +static void term_clear_cursor(void) { + if (cur_col >= term_w) return; + i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + int idx = cur_row * TERM_COLS + cur_col; + du_fill_rect(&g_ctx, x, y, (i64)DU_ASCII_STEP - 1, (i64)DU_ASCII_CELL_H, CMD_BG); + du_draw_char(&g_ctx, (char)term_ch[idx], x, y, term_fg[idx], CMD_BG); +} + +/* ============================================================ + * 键盘输入(PS/2 Scan Code Set 1) + * ============================================================ */ + +static char scan_to_ascii(u8 sc, int shift) { + 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,' ' + }; + if (sc >= 58) return 0; + return shift ? shifted[sc] : normal[sc]; +} + +/* ============================================================ + * 字符串工具 + * ============================================================ */ + +static int str_eq(const char *a, const char *b) { + while (*a && *b) { if (*a != *b) return 0; a++; b++; } + return *a == *b; +} + +static int str_len(const char *s) { + int n = 0; + while (s[n]) n++; + return n; +} + +static void str_copy(char *dst, const char *src, int max) { + int i = 0; + while (src[i] && i < max - 1) { dst[i] = src[i]; i++; } + dst[i] = 0; +} + +static int str_ieq(const char *a, const char *b) { + while (*a && *b) { + char ca = *a, cb = *b; + if (ca >= 'a' && ca <= 'z') ca -= 32; + if (cb >= 'a' && cb <= 'z') cb -= 32; + if (ca != cb) return 0; + a++; b++; + } + return *a == *b; +} + +/* ============================================================ + * 输入行编辑 + * ============================================================ */ + +static char input_buf[256]; +static int input_len = 0; +static int input_cursor = 0; + +static const char *PROMPT = "C:\\> "; + +static void redraw_input_line(void) { + int prompt_len = str_len(PROMPT); + i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H; + du_fill_rect(&g_ctx, term_start_x, y, + (i64)term_w * (i64)DU_ASCII_STEP, + (i64)DU_ASCII_CELL_H, CMD_BG); + for (int i = 0; i < prompt_len; i++) { + i64 x = term_start_x + (i64)i * (i64)DU_ASCII_STEP; + du_draw_char(&g_ctx, PROMPT[i], x, y, CMD_PROMPT, CMD_BG); + } + for (int i = 0; i < input_len; i++) { + i64 x = term_start_x + (i64)(prompt_len + i) * (i64)DU_ASCII_STEP; + du_draw_char(&g_ctx, input_buf[i], x, y, CMD_TEXT, CMD_BG); + } + cur_col = prompt_len + input_cursor; + term_redraw_cursor(); +} + +static void insert_char(char c) { + if (input_len >= 255) return; + for (int i = input_len; i > input_cursor; i--) + input_buf[i] = input_buf[i - 1]; + input_buf[input_cursor] = c; + input_len++; + input_cursor++; + redraw_input_line(); +} + +static void delete_char_back(void) { + if (input_cursor == 0) return; + for (int i = input_cursor - 1; i < input_len - 1; i++) + input_buf[i] = input_buf[i + 1]; + input_len--; + input_cursor--; + input_buf[input_len] = 0; + redraw_input_line(); +} + +static void cursor_left(void) { + if (input_cursor > 0) { input_cursor--; redraw_input_line(); } +} + +static void cursor_right(void) { + if (input_cursor < input_len) { input_cursor++; redraw_input_line(); } +} + +static void cursor_home(void) { + input_cursor = 0; + redraw_input_line(); +} + +static void cursor_end(void) { + input_cursor = input_len; + redraw_input_line(); +} + +static void kill_line(void) { + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + redraw_input_line(); +} + +/* ============================================================ + * 内置命令 + * ============================================================ */ + +static void cmd_help(void) { + term_puts_color("Deshab CMD — 命令列表:\r\n", CMD_INFO); + term_puts(" dir 列出文件\r\n"); + term_puts(" cd 显示当前目录\r\n"); + term_puts(" type 显示文件内容\r\n"); + term_puts(" copy 复制文件\r\n"); + term_puts(" del 删除文件\r\n"); + term_puts(" ren 重命名文件\r\n"); + term_puts(" echo 显示文本 (echo text > file 写入)\r\n"); + term_puts(" cls 清屏\r\n"); + term_puts(" ver 显示版本\r\n"); + term_puts(" set 显示环境变量\r\n"); + term_puts(" help 显示此帮助\r\n"); + term_puts(" exit 返回桌面\r\n"); + term_puts_color("\nPE 程序执行:\r\n", CMD_WARN); + term_puts(" .exe 运行 PE32+/PE32 程序 (如 hello.exe)\r\n"); + term_puts(" 自动查找 name.exe 并运行\r\n"); +} + +static void cmd_ver(void) { + term_puts_color("Deshab CMD [Version 0.1.0]\r\n", CMD_OK); + term_puts_color("(c) 2026 Deshab Project. PE/EXE 兼容层 by UTSM.\r\n", CMD_DIM); +} + +static void cmd_cls(void) { + term_clear_buf(); + term_redraw_all(); +} + +static void cmd_cd(void) { + term_puts("C:\\\r\n"); +} + +static void cmd_set(void) { + term_puts_color("环境变量:\r\n", CMD_INFO); + term_puts(" PATH=C:\\;C:\\bin\r\n"); + term_puts(" PROMPT=$P$G\r\n"); + term_puts(" OS=Deshab\r\n"); + term_puts(" PE_COMPAT=UTSM_PE32+_PE32\r\n"); +} + +/* dir — 列出 FAT32 根目录文件 */ +typedef struct { + int count; + u32 total_bytes; +} dir_ctx_static; + +static int dir_list_cb(const char *name, u32 size, u8 attr, void *ud) { + dir_ctx_static *st = (dir_ctx_static *)ud; + if (attr & 0x10) { + term_puts_color(name, CMD_INFO); + term_puts_color(" ", CMD_DIM); + } else { + term_puts(name); + term_puts(" "); + /* 右对齐文件大小 */ + u32 sz = size; + char numbuf[12]; + int ni = 11; + numbuf[ni--] = 0; + if (sz == 0) numbuf[ni--] = '0'; + while (sz > 0 && ni >= 0) { numbuf[ni--] = (char)('0' + sz % 10); sz /= 10; } + while (ni >= 0) numbuf[ni--] = ' '; + term_puts(&numbuf[0]); + st->total_bytes += size; + } + term_puts("\r\n"); + st->count++; + return 0; +} + +static void cmd_dir(void) { + if (!f32_blk_read) { + term_puts_color("dir: 无块设备\r\n", CMD_ERROR); + return; + } + term_puts_color(" C 盘的卷是 DESHAB\r\n", CMD_DIM); + term_puts_color(" 卷序列号为 DE5A-B002\r\n", CMD_DIM); + term_puts("\r\n C:\\ 的目录\r\n\r\n"); + + dir_ctx_static st = {0, 0}; + f32_list_root(dir_list_cb, &st); + + term_puts("\r\n"); + term_putu((u32)st.count); + term_puts(" 个文件 "); + term_putu(st.total_bytes); + term_puts(" 字节\r\n"); +} + +/* type — 显示文件内容 */ +static void cmd_type(const char *args) { + if (!*args) { term_puts_color("type: 缺少文件名\r\n", CMD_ERROR); return; } + if (!f32_blk_read) { term_puts_color("type: 无块设备\r\n", CMD_ERROR); return; } + + char name11[11]; + if (f32_name_to_83(args, name11) != 0) { + term_puts_color("type: 无效文件名\r\n", CMD_ERROR); + return; + } + u8 *data = 0; u32 size = 0; + if (f32_read_root_file(name11, &data, &size) != 0) { + term_puts_color("type: 文件不存在\r\n", CMD_ERROR); + return; + } + /* 输出文件内容(按字符) */ + for (u32 i = 0; i < size; i++) { + char c = (char)data[i]; + if (c == '\n') term_putc('\r'); + term_putc(c); + } + if (size > 0 && data[size - 1] != '\n') term_putc('\r'); + term_putc('\n'); +} + +/* echo — 显示文本或重定向到文件 */ +static void cmd_echo(const char *args) { + /* 检查重定向: echo text > file */ + char text[256]; + str_copy(text, args, sizeof(text)); + + /* 查找 > */ + int redir = -1; + for (int i = 0; text[i]; i++) { + if (text[i] == '>') { redir = i; break; } + } + + if (redir >= 0) { + /* 重定向到文件 */ + text[redir] = 0; + /* 去尾部空格 */ + int tl = str_len(text); + while (tl > 0 && text[tl - 1] == ' ') text[--tl] = 0; + + /* 获取文件名 */ + char *fn = text + redir + 1; + while (*fn == ' ') fn++; + if (!*fn) { term_puts_color("echo: 重定向缺少文件名\r\n", CMD_ERROR); return; } + + char name11[11]; + if (f32_name_to_83(fn, name11) != 0) { + term_puts_color("echo: 无效文件名\r\n", CMD_ERROR); + return; + } + if (!f32_blk_write) { term_puts_color("echo: 无块设备写能力\r\n", CMD_ERROR); return; } + + /* 构造写入内容 */ + char content[256]; + int ci = 0; + for (int i = 0; i < tl && ci < 254; i++) content[ci++] = text[i]; + content[ci++] = '\r'; + content[ci++] = '\n'; + + if (f32_write_root_file(name11, (const u8 *)content, (u32)ci) != 0) { + term_puts_color("echo: 写入失败\r\n", CMD_ERROR); + return; + } + term_puts_color("echo: 已写入 ", CMD_OK); + term_puts(fn); + term_puts("\r\n"); + } else { + /* 直接显示 */ + if (*args) { + term_puts(args); + } + term_puts("\r\n"); + } +} + +/* copy — 复制文件 */ +static void cmd_copy(const char *args) { + char src[64], dst[64]; + int si = 0, di = 0; + const char *p = args; + while (*p == ' ') p++; + while (*p && *p != ' ' && si < 63) src[si++] = *p++; + src[si] = 0; + while (*p == ' ') p++; + while (*p && di < 63) dst[di++] = *p++; + dst[di] = 0; + + if (!src[0] || !dst[0]) { + term_puts_color("copy: 用法 copy <源文件> <目标文件>\r\n", CMD_ERROR); + return; + } + if (!f32_blk_read || !f32_blk_write) { + term_puts_color("copy: 无块设备\r\n", CMD_ERROR); + return; + } + + char s83[11], d83[11]; + if (f32_name_to_83(src, s83) != 0 || f32_name_to_83(dst, d83) != 0) { + term_puts_color("copy: 无效文件名\r\n", CMD_ERROR); + return; + } + + u8 *data = 0; u32 size = 0; + if (f32_read_root_file(s83, &data, &size) != 0) { + term_puts_color("copy: 源文件不存在\r\n", CMD_ERROR); + return; + } + if (f32_write_root_file(d83, data, size) != 0) { + term_puts_color("copy: 写入失败\r\n", CMD_ERROR); + return; + } + term_puts_color(" 1 个文件已复制。\r\n", CMD_OK); +} + +/* del — 删除文件 */ +static void cmd_del(const char *args) { + if (!*args) { term_puts_color("del: 缺少文件名\r\n", CMD_ERROR); return; } + if (!f32_blk_write) { term_puts_color("del: 无块设备写能力\r\n", CMD_ERROR); return; } + + char name11[11]; + if (f32_name_to_83(args, name11) != 0) { + term_puts_color("del: 无效文件名\r\n", CMD_ERROR); + return; + } + if (f32_delete_root_file(name11) != 0) { + term_puts_color("del: 文件不存在或删除失败\r\n", CMD_ERROR); + return; + } + term_puts_color("del: 已删除\r\n", CMD_OK); +} + +/* ren — 重命名文件 */ +static void cmd_ren(const char *args) { + char old[64], nw[64]; + int oi = 0, ni = 0; + const char *p = args; + while (*p == ' ') p++; + while (*p && *p != ' ' && oi < 63) old[oi++] = *p++; + old[oi] = 0; + while (*p == ' ') p++; + while (*p && ni < 63) nw[ni++] = *p++; + nw[ni] = 0; + + if (!old[0] || !nw[0]) { + term_puts_color("ren: 用法 ren <旧名> <新名>\r\n", CMD_ERROR); + return; + } + if (!f32_blk_read || !f32_blk_write) { + term_puts_color("ren: 无块设备\r\n", CMD_ERROR); + return; + } + + char o83[11], n83[11]; + if (f32_name_to_83(old, o83) != 0 || f32_name_to_83(nw, n83) != 0) { + term_puts_color("ren: 无效文件名\r\n", CMD_ERROR); + return; + } + + u8 *data = 0; u32 size = 0; + if (f32_read_root_file(o83, &data, &size) != 0) { + term_puts_color("ren: 源文件不存在\r\n", CMD_ERROR); + return; + } + if (f32_write_root_file(n83, data, size) != 0) { + term_puts_color("ren: 写入新文件失败\r\n", CMD_ERROR); + return; + } + if (f32_delete_root_file(o83) != 0) { + term_puts_color("ren: 删除旧文件失败(警告)\r\n", CMD_WARN); + } + term_puts_color("ren: 已重命名\r\n", CMD_OK); +} + +static void cmd_mkdir(void) { term_puts_color("mkdir: FAT32 根目录不支持子目录\r\n", CMD_WARN); } +static void cmd_rmdir(void) { term_puts_color("rmdir: FAT32 根目录不支持子目录\r\n", CMD_WARN); } + +/* ============================================================ + * PE/EXE 程序执行 + * ============================================================ */ + +/* 检查文件名是否以 .exe/.com/.bat 结尾(不区分大小写) */ +static int has_exec_ext(const char *name) { + int len = str_len(name); + if (len < 4) return 0; + const char *ext = name + len - 4; + if (ext[0] != '.') return 0; + if (str_ieq(ext, ".exe")) return 1; + if (str_ieq(ext, ".com")) return 1; + if (str_ieq(ext, ".bat")) return 2; + return 0; +} + +/* 执行 PE/EXE 程序。 + * prog_name = 程序名(可能带或不带 .exe 后缀) + * full_cmdline = 完整命令行(程序名 + 参数,即 GetCommandLineA 返回值) + */ +static void exec_pe(const char *prog_name, const char *full_cmdline) { + if (!g_pe_svc) { + term_puts_color("PE 兼容层不可用(UTSM PE 服务未初始化)\r\n", CMD_ERROR); + return; + } + if (!f32_blk_read) { + term_puts_color("无块设备,无法读取 PE 文件\r\n", CMD_ERROR); + return; + } + + /* 构造 8.3 文件名 */ + char base[64]; + str_copy(base, prog_name, sizeof(base)); + + /* 如果没有扩展名,自动补 .exe */ + int has_dot = 0; + for (int i = 0; base[i]; i++) { + if (base[i] == '.') { has_dot = 1; break; } + } + if (!has_dot) { + int bl = str_len(base); + if (bl > 59) bl = 59; + base[bl] = '.'; base[bl + 1] = 'e'; base[bl + 2] = 'x'; + base[bl + 3] = 'e'; base[bl + 4] = 0; + } + + char name11[11]; + if (f32_name_to_83(base, name11) != 0) { + term_puts_color("无效文件名\r\n", CMD_ERROR); + return; + } + + /* 从 FAT32 根目录读取 */ + u8 *pe_data = 0; u32 pe_size = 0; + if (f32_read_root_file(name11, &pe_data, &pe_size) != 0) { + term_puts_color("'", CMD_ERROR); + term_puts_color(prog_name, CMD_ERROR); + term_puts_color("' 不是内部或外部命令,也不是可运行的程序。\r\n", CMD_ERROR); + return; + } + + /* 验证 PE 头(MZ 签名) */ + if (pe_size < 2 || pe_data[0] != 'M' || pe_data[1] != 'Z') { + term_puts_color("不是有效的 PE/EXE 文件(缺少 MZ 签名)\r\n", CMD_ERROR); + return; + } + + term_puts_color("正在执行 ", CMD_INFO); + term_puts_color(prog_name, CMD_WARN); + term_puts_color(" ...\r\n", CMD_INFO); + term_redraw_all(); + + /* 调用 UTSM PE 服务执行 */ + logl("[cmd] PE run begin"); + swrite("[cmd] cmdline: "); swrite(full_cmdline); swrite("\n"); + + u64 exit_code = 0; + int rc = g_pe_svc->run(pe_data, (u64)pe_size, full_cmdline, &exit_code); + + if (rc != 0) { + term_puts_color("PE 执行失败,错误码: ", CMD_ERROR); + term_putx((u32)rc); + term_puts("\r\n"); + } else { + term_puts_color("程序退出,返回码: ", CMD_DIM); + term_putu((u32)exit_code); + term_puts("\r\n"); + } + + logl("[cmd] PE run end"); +} + +/* ============================================================ + * 命令解析与分发 + * ============================================================ */ + +static int g_should_exit = 0; + +static void execute_command(const char *cmd) { + while (*cmd == ' ') cmd++; + if (*cmd == 0) return; + + /* 提取命令名 */ + const char *args = cmd; + while (*args && *args != ' ') args++; + int cmd_name_len = (int)(args - cmd); + char name[64]; + if (cmd_name_len >= 64) cmd_name_len = 63; + for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i]; + name[cmd_name_len] = 0; + while (*args == ' ') args++; + + /* 内置命令(不区分大小写) */ + if (str_ieq(name, "help") || str_eq(name, "?")) { cmd_help(); return; } + if (str_ieq(name, "ver")) { cmd_ver(); return; } + if (str_ieq(name, "cls") || str_ieq(name, "clear")) { cmd_cls(); return; } + if (str_ieq(name, "dir") || str_ieq(name, "ls")) { cmd_dir(); return; } + if (str_ieq(name, "cd")) { cmd_cd(); return; } + if (str_ieq(name, "type") || str_ieq(name, "cat")) { cmd_type(args); return; } + if (str_ieq(name, "copy") || str_ieq(name, "cp")) { cmd_copy(args); return; } + if (str_ieq(name, "del") || str_ieq(name, "rm")) { cmd_del(args); return; } + if (str_ieq(name, "ren") || str_ieq(name, "mv")) { cmd_ren(args); return; } + if (str_ieq(name, "mkdir") || str_ieq(name, "md")) { cmd_mkdir(); return; } + if (str_ieq(name, "rmdir") || str_ieq(name, "rd")) { cmd_rmdir(); return; } + if (str_ieq(name, "echo")) { cmd_echo(args); return; } + if (str_ieq(name, "set")) { cmd_set(); return; } + if (str_ieq(name, "exit") || str_ieq(name, "quit")) { + term_puts_color("返回桌面...\r\n", CMD_DIM); + g_should_exit = 1; + return; + } + if (str_ieq(name, "reboot")) { + term_puts_color("重启中...\r\n", CMD_WARN); + term_redraw_all(); + for (u64 i = 0; i < 100000000ULL; i++) __asm__ volatile("pause"); + /* 触发三重故障重启 */ + __asm__ volatile("int $0x03"); + return; + } + + /* PE/EXE 执行:检查是否为可执行文件 */ + int ext_type = has_exec_ext(name); + if (ext_type == 1) { + /* .exe/.com → PE 执行 */ + exec_pe(name, cmd); + return; + } + if (ext_type == 2) { + /* .bat → 批处理(暂不支持) */ + term_puts_color("批处理文件 (.bat) 暂不支持\r\n", CMD_WARN); + return; + } + /* 无扩展名 → 尝试自动补 .exe 执行 */ + if (cmd_name_len > 0 && name[0] != '.') { + exec_pe(name, cmd); + return; + } + + /* 未知命令 */ + term_puts_color("'", CMD_ERROR); + term_puts_color(name, CMD_ERROR); + term_puts_color("' 不是内部或外部命令,也不是可运行的程序。\r\n", CMD_ERROR); +} + +/* ============================================================ + * 主入口 + * ============================================================ */ + +__attribute__((visibility("default"))) +void dsk_entry(const dsk_boot_context *ctx) { + __asm__ volatile("cli"); + logl("[cmd] boot"); + + if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { + logl("[cmd] bad context"); + for (;;) __asm__("hlt"); + } + + fb_a = ctx->framebuffer_address; + fb_w = ctx->framebuffer_width; + fb_h = ctx->framebuffer_height; + fb_p = ctx->framebuffer_pitch; + + g_kernel_api = ctx->dkm_kernel_api; + g_boot_ctx = ctx; + + /* 获取 block 设备 read/write */ + { + u64 api = ctx->dkm_kernel_api; + u64 blk = *(u64 *)(api + 0xA8); + f32_block_read_fn bread = blk ? (f32_block_read_fn)*(u64 *)(blk + 16) : 0; + f32_block_write_fn bwrite = blk ? (f32_block_write_fn)*(u64 *)(blk + 24) : 0; + f32_init(bread, bwrite); + } + if (f32_blk_read) logl("[cmd] block_read ok"); + if (f32_blk_write) logl("[cmd] block_write ok"); + + /* 获取 PE 服务(reserved[4]) */ + { + u64 pe_addr = ctx->reserved[4]; + if (pe_addr) { + const pe_service *pe = (const pe_service *)pe_addr; + if (pe->magic == PE_SERVICE_MAGIC) { + g_pe_svc = pe; + logl("[cmd] PE service ok"); + } else { + logl("[cmd] PE service magic mismatch"); + } + } else { + logl("[cmd] PE service not available"); + } + } + + /* dev_mode 串口镜像 */ + if (ctx->reserved[3] == 1) { + g_serial_mirror = 1; + } + + /* 初始化渲染上下文 */ + du_context_init(&g_ctx, fb_a, fb_w, fb_h, fb_p); + term_init_pos(); + term_clear_buf(); + term_redraw_all(); + + /* 启动 banner */ + term_puts_color("Deshab CMD [Version 0.1.0]\r\n", CMD_OK); + term_puts_color("(c) 2026 Deshab Project. PE/EXE 兼容层 by UTSM.\r\n", CMD_DIM); + term_putc('\r'); + term_putc('\n'); + if (g_pe_svc) { + term_puts_color("PE 兼容层已就绪 (PE32+ 原生 + PE32 解释器)\r\n", CMD_OK); + } else { + term_puts_color("警告: PE 兼容层不可用\r\n", CMD_WARN); + } + term_putc('\r'); + term_putc('\n'); + term_redraw_all(); + + /* 主循环 */ + int shift = 0; + int e0 = 0; + u64 blink_start = rdtsc_cmd(); + g_should_exit = 0; + + for (;;) { + /* 绘制提示符 */ + term_puts_color(PROMPT, CMD_PROMPT); + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + int prompt_len = str_len(PROMPT); + cur_col = prompt_len; + cur_visible = 1; + redraw_input_line(); + + int cmd_done = 0; + while (!cmd_done && !g_should_exit) { + /* 光标闪烁 */ + if (g_tsc_per_ms) { + u64 now = rdtsc_cmd(); + u64 elapsed = now - blink_start; + int want_vis = (elapsed / (g_tsc_per_ms * 530)) & 1; + if (want_vis != cur_visible) { + if (want_vis) term_redraw_cursor(); + else term_clear_cursor(); + cur_visible = want_vis; + } + } + + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + if (st & 0x20) continue; /* 忽略鼠标数据 */ + u8 sc = data; + + blink_start = rdtsc_cmd(); + if (!cur_visible) { cur_visible = 1; term_redraw_cursor(); } + + if (sc == 0xE0) { e0 = 1; continue; } + if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } + if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } + + if (sc & 0x80) { + /* 按键释放 */ + e0 = 0; + continue; + } + + if (e0) { + if (sc == 0x4B) cursor_left(); + else if (sc == 0x4D) cursor_right(); + else if (sc == 0x47) cursor_home(); + else if (sc == 0x4F) cursor_end(); + else if (sc == 0x53) { /* Delete */ } + e0 = 0; + continue; + } + + if (sc == 0x1C) { + /* Enter — 提交命令 */ + term_clear_cursor(); + input_buf[input_len] = 0; + term_putc('\r'); + term_putc('\n'); + execute_command(input_buf); + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + cmd_done = 1; + term_redraw_all(); + continue; + } + + if (sc == 0x0E) { delete_char_back(); continue; } + if (sc == 0x01) { + /* Esc — 返回桌面 */ + logl("[cmd] esc -> return to DSK"); + g_should_exit = 1; + cmd_done = 1; + continue; + } + if (sc == 0x15 && !shift) { kill_line(); continue; } + + char c = scan_to_ascii(sc, shift); + if (c && c >= 32 && c <= 126) { + insert_char(c); + } + } + + if (g_should_exit) break; + } + + logl("[cmd] exit"); +} diff --git a/CODE/desktop/main.c b/CODE/desktop/main.c index bbfffe7..70d2e53 100644 --- a/CODE/desktop/main.c +++ b/CODE/desktop/main.c @@ -3,12 +3,11 @@ * 布局结构与 D:\Code\Winux-Kate 完全一致: * app-shell * ├── topbar (34px):品牌 + 工作区切换 + 页面切换 + PG 时钟 - * ├── page-host + * page-host * │ ├── 1·DASHBOARD:TERM-01 / TERM-02 / EDITOR / FILES 四面板 + StatusBar * │ ├── 2·IDE:标签栏 + 编辑宿主 - * │ ├── 3·IM:栏 + 分屏槽(未检测卡片) - * │ ├── 4·DESKTOP:图标网格 + 窗口 + 任务栏 - * │ └── 5+/CUSTOM:外部 .elf 全屏宿主(双击启动) + * │ ├── 3·DESKTOP:图标网格 + 窗口 + 任务栏 + * │ └── 4+/CUSTOM:外部 .elf 全屏宿主(双击启动) * └── task-view overlay(工作区卡片) * * 新 UI 特性(Kate 之外): @@ -204,6 +203,10 @@ typedef struct { static du_context g_fb; static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch; static const void *g_kernel_api; + +/* 双缓冲:渲染到 sprite buffer,完成后一次性 flip 到真实 framebuffer */ +#define SPRITE_BUF_ADDR 0x7000000ULL /* 112MB,在 identity-mapped 区域 */ +static u8 *g_real_fb = 0; /* 真实 framebuffer 地址 */ static desktop_block_read_fn g_block_read; static desktop_block_write_fn g_block_write; static const dsk_boot_context *g_boot_ctx = 0; @@ -246,7 +249,7 @@ static int g_icon_count = 0; /* ---- Kate 外壳状态 ---- */ static int g_booted = 0; -static int g_page = 1; /* 1..4 内置, 5..8 自定义 */ +static int g_page = 1; /* 1..3 内置, 4..8 自定义 */ static int g_taskview = 0; static int g_dash_focus = 0; /* 0=t1 1=t2 2=ed 3=files */ @@ -307,7 +310,6 @@ static int g_ctx_item_ws[MAX_WS + 1]; /* 菜单行 → 真实工作区索引 static int g_ide_host[4]; static int g_ide_tabx[4]; /* IDE 标签 × */ static int g_ide_new_rect[4]; /* IDE "+ NEW IDE" 按钮 */ -static int g_im_toggle_rect[4]; /* IM bar 切换视图按钮 */ static int g_cp_host[4]; /* 自定义页宿主 */ /* ============================================================ @@ -511,16 +513,47 @@ static void cursor_draw(int mx, int my) { } } +/* ---- 双缓冲 flip ---- */ + +/* 将 sprite buffer 完整复制到真实 framebuffer */ +static void flip_buffer(void) { + if (!g_real_fb) return; + u64 total = g_fb_h * g_fb_pitch / 4; + u32 *dst = (u32 *)g_real_fb; + u32 *src = (u32 *)SPRITE_BUF_ADDR; + for (u64 i = 0; i < total; i++) dst[i] = src[i]; +} + +/* 仅复制指定矩形区域(用于鼠标移动时的局部更新) */ +static void flip_rect(int x, int y, int w, int h) { + if (!g_real_fb) return; + for (int r = 0; r < h; r++) { + int ry = y + r; + if (ry < 0 || (u64)ry >= g_fb_h) continue; + u32 *dst = (u32 *)(g_real_fb + (u64)ry * g_fb_pitch); + u32 *src = (u32 *)((u8 *)SPRITE_BUF_ADDR + (u64)ry * g_fb_pitch); + for (int c = 0; c < w; c++) { + int rx = x + c; + if (rx < 0 || (u64)rx >= g_fb_w) continue; + dst[rx] = src[rx]; + } + } +} + /* 非阻塞延时:动画期间持续轮询鼠标,保持光标响应。 */ +__attribute__((unused)) static void anim_delay_poll_mouse(u32 ms) { for (u32 i = 0; i < ms; i++) { for (volatile u32 j = 0; j < 50000; j++) { __asm__ volatile("pause"); } if (ps2_mouse_poll()) { + int old_x = g_cursor_old_x, old_y = g_cursor_old_y; cursor_restore_bg(); + flip_rect(old_x, old_y, CURSOR_SIZE, CURSOR_SIZE); cursor_save_bg(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y); + flip_rect(g_mouse_x, g_mouse_y, CURSOR_SIZE, CURSOR_SIZE); } } } @@ -598,28 +631,8 @@ static void win_destroy(desktop_window *w) { } if (idx < 0) return; - /* 窗口关闭动画 — 收缩矩形 */ - { - int wx = w->x, wy = w->y, ww = w->w, wh = w->h; - int was_cur = (w->ws == g_ws_cur); - w->visible = 0; - if (was_cur) { - for (int step = 5; step >= 0; step--) { - int scale = (step + 1) * 100 / 6; - int aw = ww * scale / 100; - int ah = wh * scale / 100; - int ax = wx + (ww - aw) / 2; - int ay = wy + (wh - ah) / 2; - cursor_restore_bg(); - redraw_all(); - cursor_restore_bg(); - du_rect_outline(&g_fb, ax, ay, aw, ah, KS_ACCENT, DU_RADIUS_SM); - cursor_save_bg(g_mouse_x, g_mouse_y); - cursor_draw(g_mouse_x, g_mouse_y); - anim_delay_poll_mouse(16); - } - } - } + /* 直接隐藏窗口(去掉关闭动画以避免卡顿和闪屏) */ + w->visible = 0; 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); @@ -977,21 +990,25 @@ static void bash_draw_to(bash_state *s, int cx, int cy, int cw, int ch) { } /* ---- 窗口适配 ---- */ +__attribute__((unused)) static void *bash_on_create(app_ctx *ctx) { bash_state *s = (bash_state *)ADDR_TERMWIN; bash_reset(s, ctx->client_w, ctx->client_h); bash_puts(s, "Deshab Bash v0.1\nType help for commands\n\n", KS_TEXT_PRIMARY); return s; } +__attribute__((unused)) static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) { if (ev->type != EV_KEY_DOWN) return; bash_key((bash_state *)state, ev->scancode, ev->shift); ctx->win->dirty = 1; } +__attribute__((unused)) static void bash_on_draw(void *state, app_ctx *ctx) { bash_draw_to((bash_state *)state, ctx->client_x, ctx->client_y, ctx->client_w, ctx->client_h); } +__attribute__((unused)) static void bash_on_destroy(void *state) { (void)state; } /* ============================================================ @@ -1313,27 +1330,34 @@ static void calc_on_destroy(void *state) { (void)state; } * ============================================================ */ static void register_apps(void) { + /* 命令行1:shell.elf(原生命令行 + 可运行 Linux 程序)*/ g_apps[0] = (app_descriptor){ - "bash", "Terminal", 640, 440, - bash_on_create, bash_on_event, bash_on_draw, bash_on_destroy, 0 + "shell", "Terminal", 720, 480, + 0, 0, 0, 0, "SHELL ELF" }; g_app_count++; + /* 命令行2:cmd.elf(Windows 风格命令行 + PE/EXE 执行)*/ g_apps[1] = (app_descriptor){ + "cmd", "CMD", 720, 480, + 0, 0, 0, 0, "CMD ELF" + }; + g_app_count++; + g_apps[2] = (app_descriptor){ "editor", "Editor", 600, 450, editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy, 0 }; g_app_count++; - g_apps[2] = (app_descriptor){ + g_apps[3] = (app_descriptor){ "fileman", "Files", 500, 400, 0, 0, 0, 0, "FILEMAN ELF" }; g_app_count++; - g_apps[3] = (app_descriptor){ + g_apps[4] = (app_descriptor){ "calc", "Calculator", 280, 400, calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy, 0 }; g_app_count++; - g_apps[4] = (app_descriptor){ + g_apps[5] = (app_descriptor){ "browser", "Browser", 700, 500, 0, 0, 0, 0, "BROWSER ELF" }; @@ -1510,21 +1534,7 @@ static void launch_app(int app_id) { win->app_state = app->on_create(&ctx); win->dirty = 1; - /* 窗口打开动画 — 展开矩形 */ - for (int step = 0; step < 6; step++) { - int scale = (step + 1) * 100 / 6; - int aw = win->w * scale / 100; - int ah = win->h * scale / 100; - int ax = win->x + (win->w - aw) / 2; - int ay = win->y + (win->h - ah) / 2; - cursor_restore_bg(); - redraw_all(); - cursor_restore_bg(); - du_rect_outline(&g_fb, ax, ay, aw, ah, KS_ACCENT, DU_RADIUS_SM); - cursor_save_bg(g_mouse_x, g_mouse_y); - cursor_draw(g_mouse_x, g_mouse_y); - anim_delay_poll_mouse(16); - } + /* 直接显示窗口(去掉打开动画以避免卡顿和闪屏) */ slog("app launched"); } @@ -1537,6 +1547,16 @@ static int files_list_cb(const char *name, u32 size, u8 attr, void *ud) { (void)ud; if (attr & 0x10) return 0; /* 跳过子目录 */ if (g_file_count >= MAX_FILES) return 1; /* 停止 */ + + /* 只显示 user 文件:跳过 .ELF 可执行文件和系统模块 */ + int nl = kstrlen(name); + if (nl >= 4 && name[nl-4] == '.' && name[nl-3] == 'E' && + name[nl-2] == 'L' && name[nl-1] == 'F') return 0; + /* FIRSTINI* (FirstInit.elf,Name83=FIRSTINIT → 显示名 FIRSTINI.T) */ + if (nl >= 8 && name[0] == 'F' && name[1] == 'I' && name[2] == 'R' && + name[3] == 'S' && name[4] == 'T' && name[5] == 'I' && name[6] == 'N' && + name[7] == 'I') return 0; + fentry *f = &g_files[g_file_count]; f->size = size; kstrcpy(f->disp, name, 13); @@ -1614,7 +1634,7 @@ static void draw_centered(const char *s, int cx, int y, u32 fg, u32 bg) { du_draw_string(&g_fb, s, cx - l * (int)DU_ASCII_STEP / 2, y, fg, bg, DU_ASCII_STEP); } -/* 虚线矩形(ide-host / im-slot / custom host) */ +/* 虚线矩形(ide-host / custom host) */ static void draw_dashed_rect(int x, int y, int w, int h, u32 color) { for (int i = x; i < x + w; i += 8) { du_fill_rect(&g_fb, i, y, 4, 1, color); @@ -1651,6 +1671,7 @@ static void busy_delay(u32 cycles) { static void boot_screen(void) { int W = (int)g_fb_w, H = (int)g_fb_h; du_fill_bg_solid(&g_fb, KS_BG_PRIMARY); + flip_buffer(); int lw = 46 * (int)DU_ASCII_STEP; int lx = (W - lw) / 2; @@ -1663,13 +1684,15 @@ static void boot_screen(void) { char t[2] = { title[i], 0 }; du_draw_string(&g_fb, t, W / 2 - tl * (int)DU_ASCII_STEP + i * (int)DU_ASCII_STEP * 2, ty, KS_ACCENT, 0, DU_ASCII_STEP * 2); - busy_delay(8000000); + flip_buffer(); + busy_delay(2000000); } du_divider_h(&g_fb, W / 2 - 120, ty + 30, 240, KS_ACCENT); /* boot-subtitle(对应 Kate "由 Deaicup 工作室制作") */ draw_centered("DEAICUP STUDIO", W / 2, ty + 44, KS_ACCENT2, 0); - busy_delay(10000000); + flip_buffer(); + busy_delay(2500000); /* boot-lines 打字机;进度条固定在全部行下方(对应 Kate .boot-bar) */ int ly = ty + 76; @@ -1683,7 +1706,8 @@ static void boot_screen(void) { char t[2] = { s[ci], 0 }; du_draw_string(&g_fb, t, lx + (ci + 1) * (int)DU_ASCII_STEP, ly, fg, 0, DU_ASCII_STEP); - busy_delay(500000); + flip_buffer(); + busy_delay(125000); } ly += (int)DU_ASCII_LINE_H + 2; /* boot-bar:渐变填充(accent → accent2) */ @@ -1691,12 +1715,14 @@ static void boot_screen(void) { du_rect_outline(&g_fb, W / 2 - bw / 2, bar_y, bw, 6, KS_BORDER, 1); if (fill > 2) du_fill_rect_gradient(&g_fb, W / 2 - bw / 2 + 1, bar_y + 1, fill - 2, 4, KS_ACCENT, KS_ACCENT2); + flip_buffer(); } /* boot-hint + credit(对应 Kate "SYSTEM ONLINE" + "© 2026 Deaicup Studio") */ draw_centered("SYSTEM ONLINE", W / 2, H - 64, KS_ACCENT2, 0); draw_centered("(C) 2026 DEAICUP STUDIO", W / 2, H - 40, KS_TEXT_DIM, 0); - busy_delay(40000000); + flip_buffer(); + busy_delay(10000000); } /* ============================================================ @@ -1778,8 +1804,8 @@ static void draw_topbar(void) { x += 4; /* 页面切换 */ - static const char *pg_names[4] = { "1.DASHBOARD", "2.IDE", "3.IM", "4.DESKTOP" }; - for (int i = 0; i < 4; i++) { + static const char *pg_names[3] = { "1.DASHBOARD", "2.IDE", "3.DESKTOP" }; + for (int i = 0; i < 3; i++) { int bw = kstrlen(pg_names[i]) * (int)DU_ASCII_STEP + 16; int active = (g_page == i + 1); int hover = g_mouse_x >= x && g_mouse_x < x + bw && @@ -1816,10 +1842,10 @@ static void draw_topbar(void) { du_draw_string(&g_fb, label, x + 6, ty, active ? KS_TEXT_INVERT : KS_TEXT_DIM, 0, DU_ASCII_STEP); du_draw_string(&g_fb, "x", x + bw - 14, ty, KS_DANGER, 0, DU_ASCII_STEP); - g_pg_rect[4 + i][0] = x; g_pg_rect[4 + i][1] = 5; - g_pg_rect[4 + i][2] = bw; g_pg_rect[4 + i][3] = 24; - g_pg_rm_rect[4 + i][0] = x + bw - 20; g_pg_rm_rect[4 + i][1] = 5; - g_pg_rm_rect[4 + i][2] = 20; g_pg_rm_rect[4 + i][3] = 24; + g_pg_rect[3 + i][0] = x; g_pg_rect[3 + i][1] = 5; + g_pg_rect[3 + i][2] = bw; g_pg_rect[3 + i][3] = 24; + g_pg_rm_rect[3 + i][0] = x + bw - 20; g_pg_rm_rect[3 + i][1] = 5; + g_pg_rm_rect[3 + i][2] = 20; g_pg_rm_rect[3 + i][3] = 24; x += bw + 6; } /* pg-add "+" */ @@ -1948,7 +1974,7 @@ static void draw_statusbar_full(void) { du_draw_string(&g_fb, "WIFI --", x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP); /* 右侧 hint */ - const char *hint = "Ctrl+Tab PAGE . Ctrl+Shift+Tab CTX"; + const char *hint = "Ctrl+Tab PAGE"; du_draw_string(&g_fb, hint, W - 12 - kstrlen(hint) * (int)DU_ASCII_STEP, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP); } @@ -2049,7 +2075,7 @@ static void draw_dashboard(void) { du_fill_rect(&g_fb, fx, fy, fw, fh, KS_BG_PRIMARY); char path_buf[32]; - kstrcpy(path_buf, "FAT32:/ROOT (", 32); + kstrcpy(path_buf, "FAT32:/USER (", 32); int pl = kstrlen(path_buf); if (g_file_count >= 10) path_buf[pl++] = '0' + (char)(g_file_count / 10); path_buf[pl++] = '0' + (char)(g_file_count % 10); @@ -2167,93 +2193,7 @@ static void draw_ide(void) { } } -/* ---- 3·IM ---- */ -static int g_im_view = 0; /* 0=split(WECHAT/QQ) 1=full(WECOM),对应 Kate imView */ - -/* Kate .missing-card:h2 + p + a.btn 下载按钮 */ -static void draw_missing_card(int sx, int sy, int sw, int sh, - const char *title, const char *desc, - const char *btn) { - int cw = 340, ch = 150; - int cx = sx + (sw - cw) / 2, cy = sy + (sh - ch) / 2; - du_kate_panel(&g_fb, cx, cy, cw, ch, 0); - draw_centered(title, sx + sw / 2, cy + 28, KS_ACCENT, 0); - draw_centered(desc, sx + sw / 2, cy + 56, KS_TEXT_DIM, 0); - /* .btn 下载按钮(视觉复刻,Deshab 无网络安装渠道) */ - int bw = kstrlen(btn) * (int)DU_ASCII_STEP + 20; - int bx = sx + sw / 2 - bw / 2, by = cy + 88; - int hover = g_mouse_x >= bx && g_mouse_x < bx + bw && - g_mouse_y >= by && g_mouse_y < by + 22; - if (hover) du_fill_rect(&g_fb, bx, by, bw, 22, KS_ACCENT_DIM); - du_rect_outline(&g_fb, bx, by, bw, 22, KS_BORDER, 1); - du_draw_string(&g_fb, btn, bx + 10, by + 3, KS_ACCENT, 0, DU_ASCII_STEP); -} - -static void draw_im(void) { - int W = (int)g_fb_w; - int top = KATE_TOPBAR_H; - - /* im-bar(36px):dot + 视图标签 + page-credit + 切换按钮(margin-left:auto) */ - du_fill_rect(&g_fb, 0, top, W, 36, 0xFF051828u); - du_divider_h(&g_fb, 0, top + 36, W, KS_BORDER); - int ty = top + (36 - (int)DU_ASCII_LINE_H) / 2 + 1; - - /* .dot(8×8 accent2 发光点) */ - du_fill_rect(&g_fb, 12, top + 14, 8, 8, KS_ACCENT2); - - du_draw_string(&g_fb, - g_im_view ? "WECOM FULLSCREEN" : "WECHAT / QQ SPLIT", - 28, ty, KS_ACCENT, 0, DU_ASCII_STEP); - /* page-credit(对应 Kate "由 Deaicup 工作室制作") */ - du_draw_string(&g_fb, "DEAICUP STUDIO", - 28 + (g_im_view ? 16 : 17) * (int)DU_ASCII_STEP + 12, - ty + 1, KS_TEXT_DIM, 0, DU_ASCII_STEP); - - /* 切换按钮(对应 Kate im_toggle,Ctrl+Shift+Tab) */ - { - const char *tb = "CTRL+SHIFT+TAB SWITCH"; - int bw = kstrlen(tb) * (int)DU_ASCII_STEP + 20; - int bx = W - 12 - bw; - g_im_toggle_rect[0] = bx; g_im_toggle_rect[1] = top + 7; - g_im_toggle_rect[2] = bw; g_im_toggle_rect[3] = 22; - int hover = g_mouse_x >= bx && g_mouse_x < bx + bw && - g_mouse_y >= top + 7 && g_mouse_y < top + 29; - if (hover) du_fill_rect(&g_fb, bx, top + 7, bw, 22, KS_ACCENT_DIM); - du_rect_outline(&g_fb, bx, top + 7, bw, 22, KS_BORDER, 1); - du_draw_string(&g_fb, tb, bx + 10, ty, KS_ACCENT, 0, DU_ASCII_STEP); - } - - if (g_im_view == 0) { - /* im-split 双槽(grid 1fr 1fr, gap/padding 8px) */ - int pad = 8, gap = 8; - int hy = top + 36 + pad; - int hh = (int)g_fb_h - hy - pad; - int sw = (W - 2 * pad - gap) / 2; - for (int i = 0; i < 2; i++) { - int sx = pad + i * (sw + gap); - draw_dashed_rect(sx, hy, sw, hh, KS_BORDER); - } - draw_missing_card(pad, hy, sw, hh, - "WECHAT NOT DETECTED", - "IM MODULE NOT AVAILABLE IN DESHAB", - "DOWNLOAD WECHAT"); - draw_missing_card(pad + sw + gap, hy, sw, hh, - "QQ NOT DETECTED", - "IM MODULE NOT AVAILABLE IN DESHAB", - "DOWNLOAD QQ"); - } else { - /* im-full 单槽(margin 8px) */ - int hx = 8, hy = top + 36 + 8; - int hw = W - 16, hh = (int)g_fb_h - hy - 8; - draw_dashed_rect(hx, hy, hw, hh, KS_BORDER); - draw_missing_card(hx, hy, hw, hh, - "WECOM NOT DETECTED", - "IM MODULE NOT AVAILABLE IN DESHAB", - "DOWNLOAD WECOM"); - } -} - -/* ---- 4·DESKTOP ---- */ +/* ---- 3·DESKTOP ---- */ static void draw_desktop_page(void) { /* 图标网格 */ draw_desktop_icons(); @@ -2610,8 +2550,7 @@ static void redraw_all(void) { /* 页面 */ if (g_page == 1) draw_dashboard(); else if (g_page == 2) draw_ide(); - else if (g_page == 3) draw_im(); - else if (g_page == 4) draw_desktop_page(); + else if (g_page == 3) draw_desktop_page(); else { int idx = -1; for (int i = 0; i < g_cpage_count; i++) @@ -2672,7 +2611,7 @@ static void ws_close(int id) { } static void page_switch(int p) { - if (p < 1 || p > 4 + g_cpage_count) return; + if (p < 1 || p > 3 + g_cpage_count) return; g_page = p; } @@ -2691,7 +2630,7 @@ static void cpage_add(void) { for (int i = 0; i < n; i++) cp->name[i] = g_apps[a].display_name[i]; cp->name[n] = 0; for (int i = 0; i < 11; i++) cp->elf11[i] = g_apps[a].elf_name[i]; - cp->id = 5 + g_cpage_count; + cp->id = 4 + g_cpage_count; g_cpage_count++; g_page = cp->id; return; @@ -2703,7 +2642,7 @@ static void cpage_remove(int idx) { int removed_page = g_cpages[idx].id; for (int i = idx; i < g_cpage_count - 1; i++) g_cpages[i] = g_cpages[i + 1]; g_cpage_count--; - for (int i = 0; i < g_cpage_count; i++) g_cpages[i].id = 5 + i; + for (int i = 0; i < g_cpage_count; i++) g_cpages[i].id = 4 + i; if (g_page == removed_page) g_page = 1; else if (g_page > removed_page) g_page--; } @@ -2911,9 +2850,9 @@ static int on_left_press(void) { for (int i = 0; i < g_ws_count; i++) if (rect_hit(g_ws_rect[i], mx, my)) { ws_switch(i); return 1; } if (rect_hit(g_wsadd_rect, mx, my)) { ws_create(); return 1; } - for (int i = 0; i < 4 + g_cpage_count; i++) { - if (i >= 4 && rect_hit(g_pg_rm_rect[i], mx, my)) { - cpage_remove(i - 4); + for (int i = 0; i < 3 + g_cpage_count; i++) { + if (i >= 3 && rect_hit(g_pg_rm_rect[i], mx, my)) { + cpage_remove(i - 3); return 1; } if (rect_hit(g_pg_rect[i], mx, my)) { page_switch(i + 1); return 1; } @@ -2925,13 +2864,8 @@ static int on_left_press(void) { /* 页面内容 */ if (g_page == 1) return dash_click(mx, my, dbl); if (g_page == 2) return ide_click(mx, my, dbl); - if (g_page == 3) { - /* IM 切换视图按钮(对应 Kate im_toggle) */ - if (rect_hit(g_im_toggle_rect, mx, my)) { g_im_view ^= 1; return 1; } - return 0; - } - if (g_page == 4) return desktop_click(mx, my, dbl); - if (g_page >= 5) { + if (g_page == 3) return desktop_click(mx, my, dbl); + if (g_page >= 4) { int launch = 0; if (rect_hit(g_cp_new_rect, mx, my)) launch = 1; if (dbl && rect_hit(g_cp_host, mx, my)) launch = 1; @@ -2954,7 +2888,7 @@ static int on_right_press(void) { if (g_taskview) { g_taskview = 0; return 1; } if (g_ctx_open) { g_ctx_open = 0; return 1; } - if (g_page != 4) return 0; + if (g_page != 3) return 0; int tb_y = (int)g_fb_h - KATE_TASKBAR_H; if (my >= tb_y) { @@ -3002,12 +2936,11 @@ static int handle_key(u8 sc) { } if (g_taskview || g_ctx_open) return 0; /* 模态期间吞掉按键 */ - /* Ctrl+Tab 切页 / Ctrl+Shift+Tab 上下文菜单(IM 页为切换视图,对应 Kate imView) */ + /* Ctrl+Tab 切页 / Ctrl+Shift+Tab 上下文菜单(DESKTOP 页) */ if (g_ctrl && sc == 0x0F) { if (g_shift) { - if (g_page == 3) { g_im_view ^= 1; return 1; } /* Ctrl+Shift+Tab:对当前聚焦窗口打开"移到工作区"菜单 */ - if (g_page == 4 && win_find(g_focused_win)) { + if (g_page == 3 && win_find(g_focused_win)) { g_ctx_win = g_focused_win; g_ctx_x = g_mouse_x; g_ctx_y = g_mouse_y; g_ctx_open = 1; @@ -3015,7 +2948,7 @@ static int handle_key(u8 sc) { } return 0; } - int maxp = 4 + g_cpage_count; + int maxp = 3 + g_cpage_count; int p = g_page + 1; if (p > maxp) p = 1; page_switch(p); @@ -3047,7 +2980,7 @@ static int handle_key(u8 sc) { } return 0; } - if (g_page == 4) { + if (g_page == 3) { desktop_window *fw = win_find(g_focused_win); if (fw && fw->ws == g_ws_cur && fw->app_id >= 0 && fw->app_id < g_app_count && g_apps[fw->app_id].on_event && fw->app_state) { @@ -3095,6 +3028,9 @@ void dsk_entry(const dsk_boot_context *ctx) { } du_context_init(&g_fb, g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch); + /* 双缓冲:保存真实 fb,切换到 sprite buffer 渲染 */ + g_real_fb = (u8 *)g_fb_addr; + g_fb.fb = (du_u32 *)SPRITE_BUF_ADDR; f32_init(g_block_read, g_block_write); tsc_calibrate(); @@ -3201,13 +3137,16 @@ void dsk_entry(const dsk_boot_context *ctx) { } if (need_redraw) { - cursor_restore_bg(); redraw_all(); + flip_buffer(); /* 全屏 flip:无闪烁 */ } else if (moved) { - /* 仅光标移动:局部更新 */ + /* 仅光标移动:局部 flip(双缓冲 → 无闪烁) */ + int old_x = g_cursor_old_x, old_y = g_cursor_old_y; cursor_restore_bg(); + flip_rect(old_x, old_y, CURSOR_SIZE, CURSOR_SIZE); cursor_save_bg(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y); + flip_rect(g_mouse_x, g_mouse_y, CURSOR_SIZE, CURSOR_SIZE); } __asm__("pause"); diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c index c8aa172..73b3988 100644 --- a/CODE/dsk/main.c +++ b/CODE/dsk/main.c @@ -207,7 +207,7 @@ static int fat32_find_entry(const u8 *clus, u32 clus_sectors, const char *target if (dir[e].attr==0x0F) continue; if (dir[e].attr & 0x08) continue; if (neq11(dir[e].name, target)) { - *out_clus = r16((const u8*)&dir[e].clow); + *out_clus = r16((const u8*)&dir[e].clow) | ((u32)r16((const u8*)&dir[e].chigh) << 16); *out_size = dir[e].fsize; return 0; } @@ -436,10 +436,9 @@ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) { e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0; e->chigh = (u16)((first_clus >> 16) & 0xFFFF); e->wtime = 0; e->wdate = 0; e->clow = (u16)(first_clus & 0xFFFF); + e->fsize = size; } else { - dir[free_entry >= 0 ? (u32)free_entry : 0].chigh = (u16)((first_clus >> 16) & 0xFFFF); - dir[free_entry >= 0 ? (u32)free_entry : 0].clow = (u16)(first_clus & 0xFFFF); - /* Find the existing entry again to update it */ + /* 文件已存在:只更新匹配到的条目(不触碰 free_entry!) */ for (u32 e2 = 0; e2 < max_entries; e2++) { if (neq11(dir[e2].name, name11)) { dir[e2].chigh = (u16)((first_clus >> 16) & 0xFFFF); @@ -449,13 +448,6 @@ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) { } } } - /* Set file size in the directory entry */ - for (u32 e2 = 0; e2 < max_entries; e2++) { - if (neq11(dir[e2].name, name11)) { - dir[e2].fsize = size; - break; - } - } /* 6. Write FAT + root directory + data sectors back to disk */ /* Write FAT (both copies) */ diff --git a/CODE/dsk/main.o b/CODE/dsk/main.o index 267f9d9..8398609 100644 Binary files a/CODE/dsk/main.o and b/CODE/dsk/main.o differ diff --git a/CODE/linux/build.sh b/CODE/linux/build.sh index 4dd375b..dcf19b4 100644 --- a/CODE/linux/build.sh +++ b/CODE/linux/build.sh @@ -109,6 +109,17 @@ if [ -d "$INITRAMFS_DIR" ]; then # Copy initramfs contents cp -a "$INITRAMFS_DIR"/* "$INITRAMFS_TMP"/ + # Build utsm_exec_daemon (static binary for initramfs) + DAEMON_SRC="$INITRAMFS_DIR/bin/utsm_exec_daemon.c" + if [ -f "$DAEMON_SRC" ]; then + echo "[build] Compiling utsm_exec_daemon (static)..." + mkdir -p "$INITRAMFS_TMP/bin" + cc -static -O2 -Wall -I"$PROJECT_ROOT/CODE/utsm-ipc" \ + -o "$INITRAMFS_TMP/bin/utsm_exec_daemon" "$DAEMON_SRC" + strip "$INITRAMFS_TMP/bin/utsm_exec_daemon" 2>/dev/null || true + echo "[build] daemon: $(du -h "$INITRAMFS_TMP/bin/utsm_exec_daemon" | cut -f1)" + fi + # Create init script if not present if [ ! -f "$INITRAMFS_TMP/init" ]; then cat > "$INITRAMFS_TMP/init" <<'INITEOF' diff --git a/CODE/linux/initramfs/bin/utsm_exec_daemon.c b/CODE/linux/initramfs/bin/utsm_exec_daemon.c new file mode 100644 index 0000000..dd6d21a --- /dev/null +++ b/CODE/linux/initramfs/bin/utsm_exec_daemon.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * utsm_exec_daemon — Linux guest side exec daemon for Deshab Linux compat layer. + * + * Runs as the first userspace process (PID 2, after init) in the Linux guest. + * Communicates with UTSM via /dev/utsm ioctl interface: + * + * 1. Send EXEC_READY, then ioctl(PARK) → HLT → guest parks + * (this is the first HLT; linux_launch() returns to UTSM) + * 2. On vmresume: ioctl(RECV_MSG) → EXEC_REQUEST + * 3. fork + exec the requested program, capture stdout/stderr via pipe + * 4. Stream output back via ioctl(SEND_MSG, EXEC_STDOUT/STDERR) + * 5. Send EXEC_EXIT with waitpid exit code + * 6. Loop: ioctl(PARK) → wait for next request + * + * Build: static musl/glibc binary, stripped, copied into initramfs /bin/. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Shared IPC protocol — must match UTSM side */ +#include "../../../utsm-ipc/ipc_proto.h" + +/* Ioctl definitions — must match utsm_hcall.c driver */ +#define UTSM_IOCTL_RECV_MSG _IOWR('U', 1, struct utsm_ioctl_msg) +#define UTSM_IOCTL_SEND_MSG _IOWR('U', 2, struct utsm_ioctl_msg) +#define UTSM_IOCTL_PARK _IO('U', 3) +#define UTSM_IOCTL_GET_READY _IOR('U', 4, int) + +struct utsm_ioctl_msg { + unsigned int type; + unsigned int data_len; + unsigned int reserved; + unsigned int buf_size; + unsigned char data[UTSM_IPC_MSG_DATA_SIZE]; +}; + +static int g_utsm_fd = -1; + +/* Send a typed message to UTSM via the linux_to_utsm ring */ +static int send_msg(unsigned int type, const void *data, unsigned int len) +{ + struct utsm_ioctl_msg umsg; + memset(&umsg, 0, sizeof(umsg)); + umsg.type = type; + umsg.data_len = len; + umsg.buf_size = UTSM_IPC_MSG_DATA_SIZE; + if (data && len > 0) { + if (len > UTSM_IPC_MSG_DATA_SIZE) + len = UTSM_IPC_MSG_DATA_SIZE; + memcpy(umsg.data, data, len); + umsg.data_len = len; + } + return ioctl(g_utsm_fd, UTSM_IOCTL_SEND_MSG, &umsg); +} + +/* Receive a message from UTSM (non-blocking). Returns 0 on success, -EAGAIN if empty. */ +static int recv_msg(struct utsm_ioctl_msg *umsg) +{ + memset(umsg, 0, sizeof(*umsg)); + umsg->buf_size = UTSM_IPC_MSG_DATA_SIZE; + return ioctl(g_utsm_fd, UTSM_IOCTL_RECV_MSG, umsg); +} + +/* Park: HLT → VM-Exit → UTSM parks guest. Returns on vmresume. */ +static int park(void) +{ + return ioctl(g_utsm_fd, UTSM_IOCTL_PARK); +} + +/* ===== Exec request handler ===== + * + * Fork a child to exec the requested program. Parent reads stdout/stderr + * from a pipe and streams chunks back to UTSM via EXEC_STDOUT/EXEC_STDERR + * messages. Finally sends EXEC_EXIT with the child's exit status. + * + * argv parsing: argv_blob contains NUL-terminated argv[0..argc-1] + * concatenated. We split it into a char* array for execv. */ +static void handle_exec_request(const struct ipc_exec_request *req) +{ + /* Parse argv_blob into argv[] array */ + char *argv[UTSM_EXEC_MAX_ARGS + 1]; + int argc = (int)req->argc; + if (argc > UTSM_EXEC_MAX_ARGS) argc = UTSM_EXEC_MAX_ARGS; + if (argc < 1) { + struct ipc_exec_exit ex = { .exit_code = 1, .reserved = 0 }; + send_msg(UTSM_MSG_EXEC_EXIT, &ex, sizeof(ex)); + return; + } + + const char *blob = req->argv_blob; + int blob_consumed = 0; + for (int i = 0; i < argc; i++) { + if (blob_consumed >= UTSM_EXEC_ARGV_MAX) { + argv[i] = (char *)""; + continue; + } + argv[i] = (char *)(blob + blob_consumed); + int slen = strnlen(blob + blob_consumed, UTSM_EXEC_ARGV_MAX - blob_consumed); + blob_consumed += slen + 1; + } + argv[argc] = NULL; + + /* Create a pipe for child stdout+stderr */ + int pipefd[2]; + if (pipe(pipefd) != 0) { + struct ipc_exec_exit ex = { .exit_code = 126, .reserved = 0 }; + send_msg(UTSM_MSG_EXEC_EXIT, &ex, sizeof(ex)); + return; + } + + pid_t pid = fork(); + if (pid < 0) { + /* fork failed */ + close(pipefd[0]); + close(pipefd[1]); + struct ipc_exec_exit ex = { .exit_code = 127, .reserved = 0 }; + send_msg(UTSM_MSG_EXEC_EXIT, &ex, sizeof(ex)); + return; + } + + if (pid == 0) { + /* Child: redirect stdout + stderr to pipe write end, then exec */ + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); + + execv(req->path, argv); + /* execv only returns on failure */ + const char *err1 = "utsm_exec_daemon: exec failed: "; + write(STDERR_FILENO, err1, strlen(err1)); + write(STDERR_FILENO, req->path, strnlen(req->path, UTSM_EXEC_PATH_MAX)); + const char *err2 = ": "; + write(STDERR_FILENO, err2, strlen(err2)); + const char *emsg = strerror(errno); + write(STDERR_FILENO, emsg, strlen(emsg)); + write(STDERR_FILENO, "\n", 1); + _exit(127); + } + + /* Parent: read child output from pipe, stream to UTSM */ + close(pipefd[1]); + + char buf[UTSM_IPC_MSG_DATA_SIZE]; + ssize_t n; + while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) { + send_msg(UTSM_MSG_EXEC_STDOUT, buf, (unsigned int)n); + } + close(pipefd[0]); + + /* Wait for child and send exit code */ + int status = 0; + waitpid(pid, &status, 0); + + struct ipc_exec_exit ex; + ex.reserved = 0; + if (WIFEXITED(status)) { + ex.exit_code = (unsigned int)WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + ex.exit_code = 128u + (unsigned int)WTERMSIG(status); + } else { + ex.exit_code = 1; + } + send_msg(UTSM_MSG_EXEC_EXIT, &ex, sizeof(ex)); +} + +/* ===== Main loop ===== */ + +int main(void) +{ + g_utsm_fd = open("/dev/utsm", O_RDWR); + if (g_utsm_fd < 0) { + /* Cannot communicate with UTSM — exit */ + return 1; + } + + /* Announce daemon ready. UTSM's linux_launch() will see the subsequent + * HLT (via PARK ioctl) and return, marking the compat layer ready. */ + send_msg(UTSM_MSG_EXEC_READY, NULL, 0); + + /* First park: this is the HLT that causes linux_launch() to return. */ + park(); + + /* Main loop: process exec requests */ + for (;;) { + struct utsm_ioctl_msg umsg; + + /* Drain any pending messages (there may be multiple if UTSM + * queued several before vmresume). */ + int got_request = 0; + while (recv_msg(&umsg) == 0) { + if (umsg.type == UTSM_MSG_EXEC_REQUEST && + umsg.data_len >= sizeof(struct ipc_exec_request)) { + handle_exec_request((const struct ipc_exec_request *)umsg.data); + got_request = 1; + } + /* Ignore other message types (HELLO, PING, etc.) */ + } + + /* If we processed a request, the response is already sent. + * Park to let UTSM read the response and return to shell. + * If no request (spurious wakeup), just park again. */ + park(); + } + + close(g_utsm_fd); + return 0; +} diff --git a/CODE/linux/initramfs/init b/CODE/linux/initramfs/init new file mode 100644 index 0000000..5850368 --- /dev/null +++ b/CODE/linux/initramfs/init @@ -0,0 +1,47 @@ +#!/bin/sh +# +# init — Linux guest init script for UTSM dual-kernel Linux compat layer. +# +# Boot sequence: +# 1. Mount essential filesystems +# 2. Load UTSM driver (built-in, /dev/utsm auto-created) +# 3. Start utsm_exec_daemon (background) +# 4. Daemon sends EXEC_READY, then HLT (park) → linux_launch() returns to UTSM +# 5. UTSM proceeds to load DSK; Linux guest stays parked until shell +# invokes `linux ` which triggers vmresume. +# +# The daemon runs forever, parking between requests. This init script +# should NOT exit (would panic the kernel); it sleeps indefinitely after +# starting the daemon so the kernel has a runnable init task. + +PATH=/bin:/sbin:/usr/bin:/usr/sbin +export PATH + +# Mount essential filesystems +mount -t proc none /proc 2>/dev/null +mount -t sysfs none /sys 2>/dev/null +mount -t devtmpfs none /dev 2>/dev/null + +echo "[utsm-linux] init started" +echo "[utsm-linux] kernel: $(uname -r)" + +# Verify /dev/utsm exists (driver is built-in, should be auto-created) +if [ ! -e /dev/utsm ]; then + echo "[utsm-linux] WARNING: /dev/utsm not found — UTSM driver not loaded" +fi + +# Start the exec daemon in the background. +# The daemon sends EXEC_READY then HLTs (parks the guest). +# UTSM's linux_launch() returns after this first HLT. +echo "[utsm-linux] starting utsm_exec_daemon..." +/bin/utsm_exec_daemon & +DAEMON_PID=$! +echo "[utsm-linux] daemon PID=$DAEMON_PID" + +# init must not exit — sleep forever to keep the kernel happy. +# The daemon handles all UTSM exec requests; init just keeps the +# root task alive so the kernel doesn't panic. +echo "[utsm-linux] init: daemon running, entering idle hold" +while true; do + sleep 3600 +done diff --git a/CODE/linux/patches/utsm_hcall.c b/CODE/linux/patches/utsm_hcall.c index f61b653..231b6a1 100644 --- a/CODE/linux/patches/utsm_hcall.c +++ b/CODE/linux/patches/utsm_hcall.c @@ -23,12 +23,43 @@ #include #include #include +#include +#include +#include /* Shared IPC protocol header (included via build.sh -I flag) */ #include #define UTSM_DEV_NAME "utsm" +/* ===== Ioctl interface for exec daemon ===== + * + * The exec daemon (userspace) uses these ioctls to: + * 1. RECV_MSG: poll utsm_to_linux ring for EXEC_REQUEST (non-blocking) + * 2. SEND_MSG: push STDOUT/STDERR/EXIT to linux_to_utsm ring + * 3. PARK: execute HLT to trigger VM-Exit → UTSM parks guest, + * returning control to the host. vmresume causes the + * ioctl to return, at which point the daemon re-checks + * the ring for new requests. + * + * This explicit-park model avoids the need for timer emulation or + * IRQ injection: the daemon HLTs only when it has nothing to do, + * and UTSM vmresumes only when there is work. */ + +#define UTSM_IOCTL_RECV_MSG _IOWR('U', 1, struct utsm_ioctl_msg) +#define UTSM_IOCTL_SEND_MSG _IOWR('U', 2, struct utsm_ioctl_msg) +#define UTSM_IOCTL_PARK _IO('U', 3) +#define UTSM_IOCTL_GET_READY _IOR('U', 4, int) + +/* Ioctl message wrapper: carries type + data in a single call */ +struct utsm_ioctl_msg { + u32 type; /* message type (in/out) */ + u32 data_len; /* actual bytes in data (in/out) */ + u32 reserved; + u32 buf_size; /* capacity of data buffer (in) */ + u8 data[UTSM_IPC_MSG_DATA_SIZE]; /* payload */ +}; + /* ===== Hypercall ABI ===== * * Linux calls VMCALL with: @@ -169,10 +200,116 @@ static ssize_t utsm_dev_read(struct file *f, char __user *buf, return (ssize_t)msg.data_len; } +/* ===== Ioctl handlers for exec daemon ===== */ + +static long utsm_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + if (!g_shm_mapped) + return -ENODEV; + + switch (cmd) { + case UTSM_IOCTL_RECV_MSG: { + /* Non-blocking dequeue from utsm_to_linux ring */ + struct utsm_ioctl_msg umsg; + struct utsm_ipc_msg msg; + int ret; + + if (copy_from_user(&umsg, (void __user *)arg, sizeof(umsg))) + return -EFAULT; + + ret = utsm_ipc_ring_pop(&g_shm->utsm_to_linux, &msg); + if (ret != 0) + return -EAGAIN; /* ring empty */ + + umsg.type = msg.type; + umsg.data_len = msg.data_len; + if (umsg.buf_size < msg.data_len) + return -EINVAL; + if (msg.data_len > 0) { + if (copy_to_user(umsg.data, msg.data, msg.data_len)) + return -EFAULT; + } + if (copy_to_user((void __user *)arg, &umsg, sizeof(umsg))) + return -EFAULT; + return 0; + } + + case UTSM_IOCTL_SEND_MSG: { + /* Enqueue to linux_to_utsm ring */ + struct utsm_ioctl_msg umsg; + struct utsm_ipc_msg msg; + + if (copy_from_user(&umsg, (void __user *)arg, sizeof(umsg))) + return -EFAULT; + + memset(&msg, 0, sizeof(msg)); + msg.type = umsg.type; + msg.data_len = min_t(u32, umsg.data_len, UTSM_IPC_MSG_DATA_SIZE); + if (msg.data_len > 0) { + if (copy_from_user(msg.data, umsg.data, msg.data_len)) + return -EFAULT; + } + + if (utsm_ipc_ring_push(&g_shm->linux_to_utsm, &msg) != 0) { + pr_warn("[utsm] linux_to_utsm ring full\n"); + return -EAGAIN; + } + return 0; + } + + case UTSM_IOCTL_PARK: { + /* Execute HLT to trigger VM-Exit → UTSM parks guest. + * Interrupts must be enabled so that on vmresume the guest + * can continue (HLT with interrupts disabled would hang). + * + * Flow: + * 1. daemon calls ioctl(PARK) → driver executes HLT + * 2. HLT → VM-Exit (CPU_BASED_HLT_EXITING) → UTSM handle_hlt + * 3. UTSM advances RIP past HLT, exits to host (park) + * 4. UTSM writes request + vmresume + * 5. Guest continues from after HLT → ioctl returns + * 6. Daemon re-checks ring for new EXEC_REQUEST + */ + mb(); /* ensure prior ring writes are visible before HLT */ + asm volatile("hlt" ::: "memory"); + return 0; + } + + case UTSM_IOCTL_GET_READY: { + int ready = (g_shm->header.utsm_ready && g_shm->header.linux_ready) ? 1 : 0; + if (copy_to_user((void __user *)arg, &ready, sizeof(ready))) + return -EFAULT; + return 0; + } + + default: + return -ENOTTY; + } +} + +static __poll_t utsm_dev_poll(struct file *f, struct poll_table_struct *wait) +{ + __poll_t mask = 0; + + if (!g_shm_mapped) + return EPOLLERR; + + /* Report readable if utsm_to_linux ring has messages */ + if (!utsm_ipc_ring_empty(&g_shm->utsm_to_linux)) + mask |= EPOLLIN | EPOLLRDNORM; + + /* Always writable (ring push may still fail with -EAGAIN) */ + mask |= EPOLLOUT | EPOLLWRNORM; + + return mask; +} + static const struct file_operations utsm_fops = { - .owner = THIS_MODULE, - .write = utsm_dev_write, - .read = utsm_dev_read, + .owner = THIS_MODULE, + .write = utsm_dev_write, + .read = utsm_dev_read, + .unlocked_ioctl = utsm_dev_ioctl, + .poll = utsm_dev_poll, }; static struct miscdevice utsm_miscdev = { diff --git a/CODE/login/main.c b/CODE/login/main.c index ab49451..c5e4aef 100644 --- a/CODE/login/main.c +++ b/CODE/login/main.c @@ -226,6 +226,104 @@ static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char * } } +/* ---- PS/2 鼠标光标 ---- */ +#define CURSZ 16 +static int g_mouse_x = 400, g_mouse_y = 300; +static int g_mouse_btn = 0; +static u32 g_cursor_bg[CURSZ * CURSZ]; +static int g_cursor_saved = 0; +static int g_cursor_old_x = -1, g_cursor_old_y = -1; +static u8 g_mouse_buf[3]; +static int g_mouse_idx = 0; + +/* 16×16 箭头光标(与 desktop 一致) */ +static const u8 cursor_shape[16][16] = { + {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0}, + {1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0}, + {1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0}, + {1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +}; + +static void cursor_save_bg(u32 *fb, int mx, int my) { + for (int r = 0; r < CURSZ; r++) { + for (int c = 0; c < CURSZ; c++) { + int x = mx + c, y = my + r; + if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) { + u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); + g_cursor_bg[r * CURSZ + c] = line[x]; + } else { + g_cursor_bg[r * CURSZ + c] = 0; + } + } + } + g_cursor_saved = 1; + g_cursor_old_x = mx; + g_cursor_old_y = my; +} + +static void cursor_restore_bg(u32 *fb) { + if (!g_cursor_saved) return; + for (int r = 0; r < CURSZ; r++) { + for (int c = 0; c < CURSZ; c++) { + int x = g_cursor_old_x + c, y = g_cursor_old_y + r; + if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) { + u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); + line[x] = g_cursor_bg[r * CURSZ + c]; + } + } + } + g_cursor_saved = 0; +} + +static void cursor_draw(u32 *fb, int mx, int my) { + u32 fg = ACCENT; + for (int r = 0; r < CURSZ; r++) { + for (int c = 0; c < CURSZ; c++) { + if (!cursor_shape[r][c]) continue; + int x = mx + c, y = my + r; + if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) { + u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); + line[x] = fg; + } + } + } +} + +static int ps2_mouse_poll(void) { + u8 st = inb(0x64); + if (!(st & 1)) return 0; + if (!(st & 0x20)) return 0; + u8 data = inb(0x60); + g_mouse_buf[g_mouse_idx++] = data; + if (g_mouse_idx < 3) return 0; + g_mouse_idx = 0; + if (!(g_mouse_buf[0] & 0x08)) return 0; + int dx = (int)(i8)g_mouse_buf[1]; + int dy = -(int)(i8)g_mouse_buf[2]; + g_mouse_x += dx; + g_mouse_y += dy; + if (g_mouse_x < 0) g_mouse_x = 0; + if (g_mouse_y < 0) g_mouse_y = 0; + if (g_mouse_x >= (int)fb_w - CURSZ) g_mouse_x = (int)fb_w - CURSZ; + if (g_mouse_y >= (int)fb_h - CURSZ) g_mouse_y = (int)fb_h - CURSZ; + int old_btn = g_mouse_btn; + g_mouse_btn = g_mouse_buf[0] & 0x03; + if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0; + return 1; +} + /* ---- PS/2 scan code set 1 → ASCII ---- */ static char scan_to_ascii(u8 sc, int shift) { static const char normal[58] = { @@ -406,6 +504,7 @@ static void draw_login_card(u32 *fb, i64 card_x, i64 card_y, const char *pass, i u32 card = CARD_BG; 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); draw_card(fb, card_x, card_y, CARD_W, CARD_H, card); /* Title */ @@ -432,14 +531,19 @@ static void draw_login_card(u32 *fb, i64 card_x, i64 card_y, const char *pass, i /* Bottom hint */ fb_text(fb, "Enter=login Esc=skip Backspace=delete", card_x + 40, card_y + CARD_H - 28, DIM_FG, card); + cursor_save_bg(fb, g_mouse_x, g_mouse_y); + cursor_draw(fb, g_mouse_x, g_mouse_y); } /* Redraw only the password input field (avoids full card redraw on each keystroke) */ static void redraw_password_only(u32 *fb, i64 card_x, i64 card_y, const char *pass, u32 fg) { u32 card = CARD_BG; i64 fy = card_y + 240; + cursor_restore_bg(fb); fill_rect(fb, card_x + 40, fy, CARD_W - 80, 44, card); draw_rounded_input(fb, card_x + 40, fy, CARD_W - 80, 44, pass, 1, fg, card, 1); + cursor_save_bg(fb, g_mouse_x, g_mouse_y); + cursor_draw(fb, g_mouse_x, g_mouse_y); } /* ---- verify password against stored hash or encrypted conf ---- @@ -562,15 +666,26 @@ void dsk_entry(const dsk_boot_context *ctx) { /* Draw initial UI */ draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg); + /* 初始鼠标光标 */ + cursor_save_bg(fb, g_mouse_x, g_mouse_y); + cursor_draw(fb, g_mouse_x, g_mouse_y); + /* Input loop */ int shift = 0; int release = 0; for (;;) { + /* 鼠标轮询:处理鼠标移动并更新光标 */ + if (ps2_mouse_poll()) { + cursor_restore_bg(fb); + cursor_save_bg(fb, g_mouse_x, g_mouse_y); + cursor_draw(fb, g_mouse_x, g_mouse_y); + } + u8 st = inb(0x64); if (!(st & 1)) { __asm__("pause"); continue; } + /* 跳过鼠标数据(由 ps2_mouse_poll 处理) */ + if (st & 0x20) { inb(0x60); continue; } u8 data = inb(0x60); - /* Skip mouse data (bit 5 of status) */ - if (st & 0x20) continue; u8 sc = data; /* Handle extended (E0) sequences — ignore (no arrow keys needed for login) */ diff --git a/CODE/shell/main.c b/CODE/shell/main.c index ecada58..c970248 100644 --- a/CODE/shell/main.c +++ b/CODE/shell/main.c @@ -4,6 +4,8 @@ */ #include "../UTSM/include/utsm/dsk.h" +#include "../UTSM/include/utsm/pe.h" +#include "../UTSM/include/utsm/linux_compat.h" typedef unsigned char u8; typedef unsigned short u16; @@ -72,8 +74,6 @@ static void logl(const char *s) { swrite(s); swrite("\n"); } /* ASCII 字体 — 必须在 deshab_ui.h 之前包含,因为 du_draw_char 引用 g_ascii */ #include "../firstInit/ascii_bitmaps.c" #include "../UTSM/include/utsm/deshab_ui.h" -/* 用户态共享协议栈(ping/curl 内建命令使用) */ -#include "../tools/net_stack.h" /* ============================================================ * Block 设备 + FAT32 读写(移植自 DSK,支持 cp/mv/cat/ls) @@ -190,16 +190,57 @@ static int fat32_find_in_root(const u8 *clus, u32 clus_sectors, const char *targ return -1; } -/* 读取根目录下指定 8.3 名文件到 g_fdata。 +/* 在目录簇链中查找名为 name11 的子目录(attr & 0x10),返回首簇号。 + * dir_clus: 目录起始簇号。返回 0 成功,-1 未找到。 */ +static int fat32_find_dir(u32 dir_clus, const char *name11, u32 *out_clus) { + if (sh_disk_load() != 0) return -2; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 spc = bpb->spc; + u32 clus = dir_clus; + while (clus >= 2 && clus < 0x0FFFFFF8) { + u32 lba = data_lba + (clus - 2) * spc; + const u8 *cb; + if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512) + cb = g_disk + (u64)lba * 512; + else { + if (sh_read_sectors(lba, 8, g_cluster) != 0) return -4; + cb = g_cluster; + } + const shell_fat32_de *dir = (const shell_fat32_de *)cb; + for (u32 e = 0; e * 32 < spc * 512; e++) { + if (dir[e].name[0] == 0) goto done_dir; + if ((u8)dir[e].name[0] == 0xE5) continue; + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + if (!(dir[e].attr & 0x10)) continue; /* 只匹配目录 */ + if (sh_neq11(dir[e].name, name11)) { + *out_clus = (u32)sh_r16((const u8*)&dir[e].clow) + | ((u32)sh_r16((const u8*)&dir[e].chigh) << 16); + return 0; + } + } + u32 fo = fat_byte_off + clus * 4; + if (fo + 4 > sizeof(g_disk)) break; + clus = sh_r32(g_disk + fo) & 0x0FFFFFFF; + } +done_dir: + return -1; +} + +/* 从指定目录首簇读取文件到 g_fdata。 + * dir_clus: 目录起始簇号。name11: 8.3 名。 * 返回 0 成功,*out_data 指向 g_fdata,*out_size 为字节数;非 0 失败。 */ -static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) { +static int fat32_read_file_in_dir(u32 dir_clus, const char *name11, + u8 **out_data, u32 *out_size) { if (sh_disk_load() != 0) return -1; const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; u32 fat_byte_off = bpb->rsvd * 512; u32 spc = bpb->spc; - u32 clus = bpb->root_clus; + u32 clus = dir_clus; u32 found_clus = 0, found_size = 0; int found = 0; while (clus >= 2 && clus < 0x0FFFFFF8 && !found) { @@ -241,6 +282,37 @@ static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size return 0; } +/* 读取根目录下指定 8.3 名文件到 g_fdata。 + * 返回 0 成功,*out_data 指向 g_fdata,*out_size 为字节数;非 0 失败。 */ +static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) { + if (sh_disk_load() != 0) return -1; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + return fat32_read_file_in_dir(bpb->root_clus, name11, out_data, out_size); +} + +/* /bin 目录首簇号缓存 */ +static u32 g_bin_clus = 0; +static int g_bin_checked = 0; + +/* 获取 /bin 目录首簇号。返回 0 成功,-1 未找到。 */ +static int get_bin_cluster(u32 *out_clus) { + if (g_bin_checked) { + if (g_bin_clus == 0) return -1; + *out_clus = g_bin_clus; + return 0; + } + g_bin_checked = 1; + if (sh_disk_load() != 0) { g_bin_clus = 0; return -1; } + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + char bin83[11] = {'B','I','N',' ',' ',' ',' ',' ',' ',' ',' '}; + if (fat32_find_dir(bpb->root_clus, bin83, &g_bin_clus) != 0) { + g_bin_clus = 0; + return -1; + } + *out_clus = g_bin_clus; + return 0; +} + /* 写入根目录下指定 8.3 名文件(存在则替换,不存在则新建)。 * 依赖 g_disk 已加载 BPB+FAT。返回 0 成功。 */ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) { @@ -804,11 +876,14 @@ static void cmd_help(void) { term_puts(" date 显示当前日期\n"); term_puts(" about 关于 Deshab\n"); term_puts(" pci 列出 PCI 设备\n"); - term_puts(" ping [ip] ICMP echo 测试(缺省网关 10.0.2.2)\n"); - term_puts(" curl HTTP GET 请求(http://host[:port]/path)\n"); term_puts(" run 运行根目录下的工具程序\n"); + term_puts(" linux [args] 通过双内核兼容层运行 Linux 程序\n"); term_puts(" reboot 重启系统\n"); term_puts(" halt 关机(停止 CPU)\n"); + term_putc('\n'); + term_puts_color("外部命令(/bin/ 目录,PATH 自动查找):\n", DIM_FG); + term_puts(" ping ICMP echo 测试工具\n"); + term_puts(" curl HTTP GET 请求工具\n"); } static void cmd_version(void) { @@ -1234,193 +1309,9 @@ static void cmd_pci(void) { term_puts_color(tail, DIM_FG); } -/* ============================================================ - * 网络命令:ping / curl(共享协议栈 net_stack.h) - * ============================================================ */ - static u64 g_kernel_api = 0; static const dsk_boot_context *g_boot_ctx = 0; -static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=初始化失败 */ - -static int shell_net_ready(void) { - if (g_ns_status == 1) return 1; - if (g_ns_status < 0) return 0; - int rc = ns_init(g_kernel_api); - g_ns_status = (rc == 0) ? 1 : -1; - if (rc != 0) { - logl("[shell] ns_init failed"); - term_puts_color("网络初始化失败 (rc=", ERROR_FG); - char rcbuf[4]; - rcbuf[0] = (char)('0' + (-rc % 10)); - rcbuf[1] = 0; - term_puts_color(rcbuf, ERROR_FG); - term_puts_color("),无可用网卡或 net API\n", ERROR_FG); - return 0; - } - logl("[shell] ns_init ok"); - return 1; -} - -/* ping [a.b.c.d] — 缺省网关 10.0.2.2,4 次 echo */ -static void cmd_ping(const char *args) { - u32 ip; - if (!args || !*args) { - ip = NS_GATEWAY_IP; - } else if (ns_parse_ip(args, &ip) != 0) { - term_puts_color("用法: ping [a.b.c.d]\n", ERROR_FG); - return; - } - if (!shell_net_ready()) return; - - char ipstr[24]; - ns_fmt_ip(ip, ipstr); - term_puts_color("PING ", PROMPT_FG); - term_puts_color(ipstr, ACCENT_FG); - term_puts_color(" 32 data bytes\n", DIM_FG); - - int sent = 0, recv = 0; - u32 sum = 0, min = 0xFFFFFFFFu, max = 0; - for (u16 seq = 1; seq <= 4; seq++) { - u32 rtt = 0; u8 ttl = 0; - int rc = ns_ping(ip, seq, 2000, &rtt, &ttl); - sent++; - if (rc == 0) { - recv++; - sum += rtt; - if (rtt < min) min = rtt; - if (rtt > max) max = rtt; - term_puts("reply from "); - term_puts(ipstr); - term_puts(": bytes=32 time="); - char nb[12]; ns_u32_dec(nb, rtt); - term_puts(nb); - term_puts("ms TTL="); - ns_u32_dec(nb, ttl); - term_puts(nb); - term_putc('\n'); - } else if (rc == -1) { - term_puts_color("ARP 解析失败,目标不可达\n", ERROR_FG); - break; - } else { - term_puts_color("request timed out\n", DIM_FG); - } - } - /* 统计行 */ - char nb[12]; - ns_u32_dec(nb, (u32)sent); - term_puts_color("--- ", DIM_FG); - term_puts_color(ipstr, DIM_FG); - term_puts_color(" ping statistics ---\n ", DIM_FG); - term_puts(nb); - term_puts(" transmitted, "); - ns_u32_dec(nb, (u32)recv); - term_puts(nb); - term_puts(" received, "); - u32 loss = sent ? (u32)(sent - recv) * 100 / (u32)sent : 100; - ns_u32_dec(nb, loss); - term_puts(nb); - term_puts("% loss"); - if (recv > 0) { - term_puts(", min/avg/max = "); - ns_u32_dec(nb, min); term_puts(nb); term_puts("/"); - ns_u32_dec(nb, sum / (u32)recv); term_puts(nb); term_puts("/"); - ns_u32_dec(nb, max); term_puts(nb); term_puts(" ms"); - } - term_putc('\n'); -} - -/* curl http://host[:port][/path] — HTTP/1.0 GET,打印 status + body */ -static void cmd_curl(const char *args) { - if (!args || !*args) { - term_puts_color("用法: curl http://host[:port][/path]\n", ERROR_FG); - return; - } - if (!shell_net_ready()) return; - - const char *p = args; - if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7; - - char host[128]; int hl = 0; - while (*p && *p != ':' && *p != '/' && hl < 127) host[hl++] = *p++; - host[hl] = 0; - if (!hl) { term_puts_color("curl: 缺少主机名\n", ERROR_FG); return; } - - u16 port = 80; - if (*p == ':') { - p++; - u32 pv = 0; int digits = 0; - while (*p >= '0' && *p <= '9') { pv = pv * 10 + (u32)(*p - '0'); p++; digits++; } - if (digits && pv <= 65535) port = (u16)pv; - } - char path[128]; int pl = 0; - if (*p == '/') { - while (*p && *p != ' ' && pl < 127) path[pl++] = *p++; - } - if (!pl) { path[0] = '/'; pl = 1; } - path[pl] = 0; - - /* 解析主机 */ - u32 ip; - term_puts_color("-> 解析 ", DIM_FG); - term_puts_color(host, DIM_FG); - term_puts_color("...\n", DIM_FG); - if (ns_dns_resolve(host, &ip) != 0) { - term_puts_color("curl: DNS 解析失败\n", ERROR_FG); - return; - } - char ipstr[24]; ns_fmt_ip(ip, ipstr); - term_puts_color("-> ", DIM_FG); - term_puts_color(ipstr, DIM_FG); - term_putc('\n'); - - /* 连接 */ - term_puts_color("-> TCP 连接...\n", DIM_FG); - if (ns_tcp_connect(ip, port, 2000) != 0) { - term_puts_color("curl: TCP 连接失败(超时/RST)\n", ERROR_FG); - return; - } - term_puts_color("-> 已连接,发送请求\n", DIM_FG); - - /* 构造 HTTP/1.0 请求 */ - static char req[512]; - int r = 0; - const char *m = "GET "; while (*m) req[r++] = *m++; - for (int i = 0; i < pl; i++) req[r++] = path[i]; - m = " HTTP/1.0\r\nHost: "; while (*m) req[r++] = *m++; - for (int i = 0; i < hl; i++) req[r++] = host[i]; - m = "\r\nConnection: close\r\n\r\n"; while (*m) req[r++] = *m++; - req[r] = 0; - - if (ns_tcp_send((const u8 *)req, (u32)r) != 0) { - term_puts_color("curl: 发送失败\n", ERROR_FG); - ns_tcp_close(); - return; - } - - /* 收至 FIN(数据在协议栈 ns_tcp_rx 中累计) */ - int n = ns_tcp_recv(0, 0, 5000); - ns_tcp_close(); - if (n < 0) { - term_puts_color("curl: 连接被重置\n", ERROR_FG); - return; - } - if (n == 0) { - term_puts_color("curl: 无响应数据(超时)\n", ERROR_FG); - return; - } - - /* 打印状态行 + body(截断提示) */ - u32 total = ns_tcp_rx_len; - u32 show = (u32)n; - for (u32 i = 0; i < show; i++) term_putc((char)ns_tcp_rx[i]); - if (total > show) { - term_puts_color("\n[... 已截断,共 ", DIM_FG); - char nb[12]; ns_u32_dec(nb, total); - term_puts_color(nb, DIM_FG); - term_puts_color(" 字节]\n", DIM_FG); - } - term_putc('\n'); -} +static const linux_compat_service *g_lxc_svc = 0; /* ============================================================ * run 命令:从 FAT32 根目录加载并跳转 PIE ELF 工具 @@ -1526,6 +1417,185 @@ static void cmd_run(const char *args) { term_redraw_all(); } +/* ============================================================ + * PATH 查找:在 /bin 子目录中搜索 name.ELF 并执行 + * 返回 1 表示找到并已处理(成功或报错),0 表示未找到 + * ============================================================ */ +static int path_search_and_run(const char *name, const char *args) { + (void)args; /* 参数保留供未来扩展 */ + if (!g_block_read) return 0; + + /* 构造 "NAME.ELF" → 8.3 名 */ + char full[64]; + int nl = 0; + while (name[nl] && nl < 55) nl++; + int p = 0; + for (int i = 0; i < nl; i++) full[p++] = name[i]; + full[p++] = '.'; full[p++] = 'E'; full[p++] = 'L'; + full[p++] = 'F'; full[p] = 0; + char n83[11]; + if (name_to_83(full, n83) != 0) return 0; + + /* 获取 /bin 目录首簇 */ + u32 bin_clus; + if (get_bin_cluster(&bin_clus) != 0) return 0; + + /* 从 /bin 子目录读取 ELF */ + u8 *data = 0; u32 size = 0; + if (fat32_read_file_in_dir(bin_clus, n83, &data, &size) != 0) return 0; + + /* 加载 ELF */ + void *entry = 0; + int rc = sh_load_elf(data, &entry); + if (rc != 0) { + term_puts_color(name, ERROR_FG); + term_puts_color(": ELF 加载失败\n", ERROR_FG); + return 1; + } + + /* 执行 */ + logl("[shell] path: running /bin/ tool"); + void (*fn_entry)(const dsk_boot_context *) = (void (*)(const dsk_boot_context *))entry; + fn_entry(g_boot_ctx); + logl("[shell] path: tool returned"); + g_disk_loaded = 0; + term_redraw_all(); + return 1; +} + +/* ============================================================ + * linux 命令:通过双内核兼容层执行 Linux 程序 + * + * 用法: + * linux 显示兼容层状态 + * linux [args] 执行 /bin/(或完整路径) + * linux ls -la → 执行 /bin/ls -la + * linux uname -a → 执行 /bin/uname -a + * + * 依赖 UTSM+Linux 双内核架构: Linux guest 作为 daemon park 在 VMX + * guest 中,shell 通过 g_lxc_svc->exec 写 IPC 请求并 vmresume 唤醒。 + * ============================================================ */ + +/* 简单 token 分词:将 args 按空格切分为 argv[],返回 argc。 + * 不支持引号转义(保持与 shell 其余部分一致的简单分词)。 */ +static int split_args(const char *args, char argv[16][64], int max_argc) { + int argc = 0; + const char *p = args; + while (*p && argc < max_argc) { + while (*p == ' ') p++; + if (*p == 0) break; + int len = 0; + while (*p && *p != ' ' && len < 63) { + argv[argc][len++] = *p++; + } + argv[argc][len] = 0; + argc++; + } + return argc; +} + +static void cmd_linux(const char *args) { + if (!g_lxc_svc) { + term_puts_color("linux: 兼容层服务未初始化(boot context 未提供)\n", ERROR_FG); + return; + } + if (!g_lxc_svc->is_available()) { + term_puts_color("linux: 兼容层不可用(Linux guest 未驻留或 IPC 未就绪)\n", ERROR_FG); + char status[128]; + g_lxc_svc->status(status, sizeof(status)); + term_puts_color(status, ERROR_FG); + term_putc('\n'); + return; + } + + /* 无参数:显示状态 */ + while (*args == ' ') args++; + if (*args == 0) { + term_puts_color("Linux 兼容层已就绪 (双内核 park-and-resume)\n", OK_FG); + term_puts_color("用法: linux <程序> [参数...]\n", DIM_FG); + term_puts_color("示例: linux ls -la / linux uname -a / linux cat /etc/hostname\n", DIM_FG); + return; + } + + /* 分词参数 */ + char argv_buf[16][64]; + int argc = split_args(args, argv_buf, 16); + if (argc == 0) { + term_puts_color("linux: 参数解析失败\n", ERROR_FG); + return; + } + + /* 构造程序路径: + * - 以 '/' 开头视为绝对路径 + * - 否则前缀 "/bin/" */ + char path[160]; + const char *prog = argv_buf[0]; + if (prog[0] == '/') { + int i = 0; + while (prog[i] && i < 159) { path[i] = prog[i]; i++; } + path[i] = 0; + } else { + const char *prefix = "/bin/"; + int i = 0; + while (prefix[i]) { path[i] = prefix[i]; i++; } + int j = 0; + while (prog[j] && i < 159) { path[i++] = prog[j++]; } + path[i] = 0; + } + + /* 构造 argv 指针数组(argv[0] = 程序名) */ + const char *argv_ptrs[16]; + argv_ptrs[0] = prog; /* argv[0] 用程序名(不带 /bin/ 前缀) */ + for (int i = 1; i < argc; i++) argv_ptrs[i] = argv_buf[i]; + + /* 执行:stdout 缓冲区 */ + static char stdout_buf[8192]; + u64 stdout_len = 0; + u64 exit_code = 0; + + logl("[shell] linux: exec "); + logl(path); + int rc = g_lxc_svc->exec(path, argc, argv_ptrs, + stdout_buf, sizeof(stdout_buf) - 1, + &stdout_len, &exit_code); + + if (rc != 0) { + term_puts_color("linux: 执行失败 (code=", ERROR_FG); + char num[16]; + int neg = rc < 0; + unsigned int v = neg ? (unsigned int)(-rc) : (unsigned int)rc; + int n = 0; + if (v == 0) num[n++] = '0'; + while (v) { num[n++] = '0' + (v % 10); v /= 10; } + if (neg) term_putc('-'); + for (int i = n - 1; i >= 0; i--) term_putc(num[i]); + term_puts_color(")\n", ERROR_FG); + return; + } + + /* 输出 stdout */ + if (stdout_len > 0) { + stdout_buf[stdout_len] = 0; + term_puts(stdout_buf); + /* 确保末尾换行 */ + if (stdout_buf[stdout_len - 1] != '\n') { + term_putc('\n'); + } + } + + /* 非零退出码提示 */ + if (exit_code != 0) { + term_puts_color("[exit ", DIM_FG); + char ec[16]; + unsigned int ev = (unsigned int)exit_code; + int en = 0; + if (ev == 0) ec[en++] = '0'; + while (ev) { ec[en++] = '0' + (ev % 10); ev /= 10; } + for (int i = en - 1; i >= 0; i--) term_putc(ec[i]); + term_puts_color("]\n", DIM_FG); + } +} + static void execute_command(const char *cmd) { while (*cmd == ' ') cmd++; if (*cmd == 0) return; @@ -1552,14 +1622,16 @@ static void execute_command(const char *cmd) { else if (str_eq(name, "cp")) cmd_cp(args); else if (str_eq(name, "mv")) cmd_mv(args); else if (str_eq(name, "rm") || str_eq(name, "del")) cmd_rm(args); - else if (str_eq(name, "ping")) cmd_ping(args); - else if (str_eq(name, "curl")) cmd_curl(args); else if (str_eq(name, "run")) cmd_run(args); + else if (str_eq(name, "linux")) cmd_linux(args); else if (str_eq(name, "cd")) cmd_not_impl("cd"); else if (str_eq(name, "pwd")) { term_puts("/\n"); } else if (str_eq(name, "whoami")) { term_puts("root\n"); } else if (str_eq(name, "id")) { term_puts("uid=0(root) gid=0(root)\n"); } - else cmd_unknown(name); + /* ---- PATH 查找:未匹配内建时搜索 /bin/ ---- */ + else if (!path_search_and_run(name, args)) { + cmd_unknown(name); + } } /* ---- CMOS RTC 读取 ---- */ @@ -1721,8 +1793,7 @@ static void run_dev_tests(void) { term_puts_color("[10] ls — 确认清理\n", ACCENT_FG); cmd_ls(); - term_puts_color("[11] ping 10.0.2.2 — 网络回归(失败不阻断)\n", ACCENT_FG); - cmd_ping("10.0.2.2"); + term_puts_color("[11] ping — 外部命令(/bin/ping.elf),跳过内联测试\n", ACCENT_FG); term_putc('\n'); term_puts_color("=== 自动测试完成 ===\n", OK_FG); @@ -1760,6 +1831,22 @@ void dsk_entry(const dsk_boot_context *ctx) { if (g_block_read) logl("[shell] block_read ok"); if (g_block_write) logl("[shell] block_write ok"); + /* 获取 Linux 兼容层服务(reserved[5])—— 双内核 park-and-resume */ + { + u64 lxc_addr = ctx->reserved[5]; + if (lxc_addr) { + const linux_compat_service *lxc = (const linux_compat_service *)lxc_addr; + if (lxc->magic == LINUX_COMPAT_MAGIC) { + g_lxc_svc = lxc; + logl("[shell] linux_compat service ok"); + } else { + logl("[shell] linux_compat magic mismatch"); + } + } else { + logl("[shell] linux_compat service not available"); + } + } + /* 初始化风格系统渲染上下文 */ du_context_init(&g_ctx, fb_a, fb_w, fb_h, fb_p); @@ -1782,9 +1869,13 @@ void dsk_entry(const dsk_boot_context *ctx) { run_dev_tests(); } + /* 将 banner + dev test 输出刷新到 framebuffer */ + term_redraw_all(); + /* 主循环 */ int shift = 0; int e0 = 0; + u64 blink_start = rdtsc_shell(); for (;;) { draw_prompt(); input_len = 0; @@ -1793,15 +1884,30 @@ void dsk_entry(const dsk_boot_context *ctx) { int prompt_len = 0; while (PROMPT[prompt_len]) prompt_len++; cur_col = prompt_len; - term_redraw_cursor(); + cur_visible = 1; + redraw_input_line(); int cmd_done = 0; while (!cmd_done) { + /* 光标闪烁:每 ~530ms 切换一次可见性 */ + if (g_tsc_per_ms) { + u64 now = rdtsc_shell(); + u64 elapsed = now - blink_start; + int want_vis = (elapsed / (g_tsc_per_ms * 530)) & 1; + if (want_vis != cur_visible) { + if (want_vis) term_redraw_cursor(); + else term_clear_cursor(); + cur_visible = want_vis; + } + } u8 st = inb(0x64); if (!(st & 1)) { __asm__("pause"); continue; } u8 data = inb(0x60); if (st & 0x20) continue; u8 sc = data; + /* 有键盘输入时重置闪烁,让光标保持可见 */ + blink_start = rdtsc_shell(); + if (!cur_visible) { cur_visible = 1; term_redraw_cursor(); } if (sc == 0xE0) { e0 = 1; continue; } if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } diff --git a/CODE/shell/main.o b/CODE/shell/main.o index aa4542f..d24563a 100644 Binary files a/CODE/shell/main.o and b/CODE/shell/main.o differ diff --git a/CODE/utsm-ipc/ipc_proto.h b/CODE/utsm-ipc/ipc_proto.h index 5f1d1bd..ce7860a 100644 --- a/CODE/utsm-ipc/ipc_proto.h +++ b/CODE/utsm-ipc/ipc_proto.h @@ -67,8 +67,43 @@ enum utsm_ipc_msg_type { UTSM_MSG_ACK = 6, /* Acknowledgment */ UTSM_MSG_NACK = 7, /* Negative acknowledgment */ UTSM_MSG_SHUTDOWN = 8, /* Shutdown notification */ + /* Linux compat exec protocol (UTSM → Linux → UTSM) */ + UTSM_MSG_EXEC_REQUEST = 16, /* UTSM→Linux: exec a program (payload=ipc_exec_request) */ + UTSM_MSG_EXEC_STDOUT = 17, /* Linux→UTSM: stdout chunk (payload=raw bytes) */ + UTSM_MSG_EXEC_STDERR = 18, /* Linux→UTSM: stderr chunk (payload=raw bytes) */ + UTSM_MSG_EXEC_EXIT = 19, /* Linux→UTSM: process exited (payload=ipc_exec_exit) */ + UTSM_MSG_EXEC_READY = 20, /* Linux→UTSM: daemon ready to accept exec requests */ }; +/* ===== Linux compat exec payload structures ===== + * + * Flow: + * 1. UTSM writes UTSM_MSG_EXEC_REQUEST into utsm_to_linux ring, + * then calls linux_resume() to wake the parked Linux guest. + * 2. Linux daemon reads the request, fork+exec's the program, + * streams stdout/stderr back via UTSM_MSG_EXEC_STDOUT/STDERR. + * 3. Linux daemon writes UTSM_MSG_EXEC_EXIT with exit code, then HLTs (park). + * 4. UTSM reads the responses from linux_to_utsm ring, returns to caller. + * + * All strings are NUL-terminated. argv_blob contains argv[0..argc-1] each + * NUL-terminated, concatenated. path is the program path to execute. */ + +#define UTSM_EXEC_PATH_MAX 128 +#define UTSM_EXEC_ARGV_MAX 96 /* total argv blob size */ +#define UTSM_EXEC_MAX_ARGS 16 + +struct ipc_exec_request { + uint32_t argc; /* number of arguments */ + uint32_t flags; /* reserved (0) */ + char path[UTSM_EXEC_PATH_MAX]; /* program path (NUL-terminated) */ + char argv_blob[UTSM_EXEC_ARGV_MAX]; /* argv strings, each NUL-terminated */ +} __attribute__((packed)); + +struct ipc_exec_exit { + uint32_t exit_code; /* process exit status */ + uint32_t reserved; +} __attribute__((packed)); + /* ===== Ring buffer structures ===== */ /* Single IPC message (256 bytes, cache-line aligned) */ diff --git a/SYSTEM/boot/utsm.elf b/SYSTEM/boot/utsm.elf index 0c3c3d8..06933eb 100644 Binary files a/SYSTEM/boot/utsm.elf and b/SYSTEM/boot/utsm.elf differ diff --git a/SYSTEM/limine/limine.conf b/SYSTEM/limine/limine.conf index 35a874b..a71b13a 100644 --- a/SYSTEM/limine/limine.conf +++ b/SYSTEM/limine/limine.conf @@ -43,8 +43,20 @@ verbose: yes module_path: boot():/driver/test.fat32 module_cmdline: dkm:test_fat32 - # Linux 6.6 LTS guest kernel (for UTSM+Linux dual-kernel architecture) - # Uncomment after building Linux via CODE/linux/build.sh + # Linux 6.6 LTS guest kernel (UTSM+Linux dual-kernel Linux compat layer) + # Uncomment after building Linux via CODE/linux/build.sh (requires WSL/Linux). + # + # Boot flow with these modules: + # 1. Limine loads utsm.elf + all .drv modules + bzImage + initrd + # 2. UTSM boots, init VMM/EPT, self-test + # 3. UTSM linux_loader_init() finds bzImage module, parses + maps guest + # 4. UTSM ipc_shm_init() allocates 1MB IPC shared memory (EPT-mapped) + # 5. UTSM linux_launch() → vmlaunch → Linux boots → init starts daemon + # 6. Daemon sends EXEC_READY, then ioctl(PARK) → HLT → VM-Exit → park + # 7. UTSM linux_compat_init() marks Linux compat layer ready + # 8. UTSM loads DSK (deshab.elf) → shell.elf → `linux ` command + # invokes linux_compat_service->exec() → vmresume → daemon runs prog + # # module_path: boot():/boot/linux-bzImage # module_cmdline: linux:bzImage # module_path: boot():/boot/linux-initrd.img diff --git a/SYSTEM/system/deshab64/cmd.elf b/SYSTEM/system/deshab64/cmd.elf new file mode 100644 index 0000000..97689a0 Binary files /dev/null and b/SYSTEM/system/deshab64/cmd.elf differ diff --git a/SYSTEM/system/deshab64/deshab.elf b/SYSTEM/system/deshab64/deshab.elf index dab2acb..7d1e60a 100644 Binary files a/SYSTEM/system/deshab64/deshab.elf and b/SYSTEM/system/deshab64/deshab.elf differ diff --git a/SYSTEM/system/deshab64/desktop.elf b/SYSTEM/system/deshab64/desktop.elf index 297d550..61bf351 100644 Binary files a/SYSTEM/system/deshab64/desktop.elf and b/SYSTEM/system/deshab64/desktop.elf differ diff --git a/SYSTEM/system/deshab64/shell.elf b/SYSTEM/system/deshab64/shell.elf index e0d0856..596fc60 100644 Binary files a/SYSTEM/system/deshab64/shell.elf and b/SYSTEM/system/deshab64/shell.elf differ diff --git a/SYSTEM/system/user/use/login.elf b/SYSTEM/system/user/use/login.elf index b95c3dc..dfbc081 100644 Binary files a/SYSTEM/system/user/use/login.elf and b/SYSTEM/system/user/use/login.elf differ diff --git a/build.ps1 b/build.ps1 index 477c911..b3711c1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -603,6 +603,25 @@ if (-not (Test-Path $DesktopOutput)) { } Write-Host "[build] desktop Output: $DesktopOutput" +# ---------- CMD (Windows-style command line + PE/EXE compat) ---------- +$CmdDir = Join-Path $Root 'CODE\cmd' +$CmdOutput = Join-Path $SystemDir 'system\deshab64\cmd.elf' + +Write-Host '[build] Building cmd.elf...' +try { + & $make -C "$CmdDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make cmd failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $CmdOutput)) { + throw "cmd build did not produce $CmdOutput" +} +Write-Host "[build] cmd Output: $CmdOutput" + # ---------- Tool applications ---------- $ToolsDir = Join-Path $Root 'CODE\tools' $ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools'