Compare commits

..
3 Commits
Author SHA1 Message Date
xyou 2774c10d0c FUCKYOU 2026-07-28 21:11:10 +08:00
xyou 537890df9e FUCK2 2026-07-28 14:35:14 +08:00
xyou 17a577f2ed FUCK 2026-07-28 12:00:02 +08:00
105 changed files with 17119 additions and 1036 deletions
+4 -4
View File
@@ -5,12 +5,12 @@ OBJCOPY := llvm-objcopy
ROOT := ../..
OUT := $(ROOT)/SYSTEM/boot/utsm.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -Iinclude -O2
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
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -Iinclude -I../utsm-ipc -O2
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)
S_SOURCES := $(wildcard arch/x86_64/*.S)
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)
.PHONY: all clean
+15
View File
@@ -79,3 +79,18 @@ void serial_write(const char *s) {
serial_putc(*s++);
}
}
/* 非阻塞读取 host 串口一个字节(QEMU stdio → guest COM1 输入桥)。
* 返回 0-255,无数据返回 -1。供 vmexit 在 guest resume 前轮询。 */
int serial_try_read(void) {
if (inb(COM1 + 5) & 0x01) {
return inb(COM1);
}
return -1;
}
/* 导出 PIT 校准的 TSC 频率(kHz 量级,单位 ticks/ms),
* 供 VMX preemption timer 与 guest PIT tick 注入使用。 */
u64 serial_tsc_per_ms(void) {
return g_tsc_per_ms;
}
Binary file not shown.
+248 -2
View File
@@ -173,8 +173,8 @@ typedef long long du_i64;
#define DU_RADIUS_NONE 0u
#define DU_RADIUS_SM 4u /* 小按钮、标签 */
#define DU_RADIUS_MD 8u /* 卡片、输入框 */
#define DU_RADIUS_LG 12u /* 大卡片、面板 */
#define DU_RADIUS_MD 0u /* 卡片、输入框(性能优化:改为直角) */
#define DU_RADIUS_LG 0u /* 大卡片、面板(性能优化:改为直角) */
#define DU_RADIUS_XL 16u /* 模态框 */
#define DU_RADIUS_FULL 9999u /* 完全圆角(药丸形) */
@@ -706,4 +706,250 @@ static inline du_u32 du_theme_accent(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_ACCENT : DS_LIGHT_ACCENT;
}
/* ===================================================================
* L0+ — Winux-Kate 调色板 (eDEX-UI Neon Cyber Theme)
*
* 源自 D:\Code\Winux-Kate\src\styles\theme.css
* 风格:深蓝黑底 + 青色霓虹 + 绿色辅助 + 扫描线纹理
* =================================================================== */
/* --- 主色调:深空蓝黑 (Deep Space Black) --- */
#define DP_KATE_BG 0xFF02040Au /* --bg: #02040a */
#define DP_KATE_BG2 0xFF050A18u /* --bg2: #050a18 */
#define DP_KATE_PANEL 0xB20A1228u /* --panel: rgba(10,18,40,0.72) 半透明 */
#define DP_KATE_PANEL_SOLID 0xFF0A1228u /* 不透明版本(用于无 alpha 混合场景) */
/* --- 强调色:霓虹青 (Neon Cyan) --- */
#define DP_KATE_ACCENT 0xFF00E5FFu /* --accent: #00e5ff */
#define DP_KATE_ACCENT_DIM 0x2E00E5FFu /* --accent-dim: rgba(0,229,255,0.18) */
#define DP_KATE_ACCENT_DIM_SOLID 0xFF2A4A5Au /* 不透明近似(提升亮度改善对比度) */
#define DP_KATE_PANEL_BORDER 0x5900E5FFu /* --panel-border: rgba(0,229,255,0.35) */
#define DP_KATE_PANEL_BORDER_SOLID 0xFF2A6070u /* 不透明近似(提升亮度) */
/* --- 辅助色:霓虹绿 (Neon Green) --- */
#define DP_KATE_ACCENT2 0xFF39FF14u /* --accent2: #39ff14 */
/* --- 文本色 --- */
#define DP_KATE_TEXT 0xFFCFEFFBu /* --text: #cfeffb */
#define DP_KATE_TEXT_DIM 0xFF6F8AA8u /* --text-dim: #6f8aa8 */
/* --- 状态色 --- */
#define DP_KATE_WARN 0xFFFFCC00u /* --warn: #ffcc00 */
#define DP_KATE_DANGER 0xFFFF4D6Du /* --danger: #ff4d6d */
/* ===================================================================
* L1 — Winux-Kate 语义色值
* =================================================================== */
#define KS_BG_PRIMARY DP_KATE_BG
#define KS_BG_SECONDARY DP_KATE_PANEL_SOLID
#define KS_BG_TERTIARY DP_KATE_BG2
#define KS_TEXT_PRIMARY DP_KATE_TEXT
#define KS_TEXT_DIM DP_KATE_TEXT_DIM
#define KS_TEXT_INVERT DP_KATE_BG
#define KS_ACCENT DP_KATE_ACCENT
#define KS_ACCENT2 DP_KATE_ACCENT2
#define KS_ACCENT_DIM DP_KATE_ACCENT_DIM_SOLID
#define KS_BORDER DP_KATE_PANEL_BORDER_SOLID
#define KS_BORDER_FOCUS DP_KATE_ACCENT
#define KS_BORDER_DIM 0xFF1A2840u /* 暗边框 */
#define KS_DANGER DP_KATE_DANGER
#define KS_WARN DP_KATE_WARN
/* ===================================================================
* Winux-Kate 布局常量(源自 theme.css / pages.css)
* =================================================================== */
#define KATE_TOPBAR_H 34 /* .topbar height */
#define KATE_STATUSBAR_H 32 /* .statusbar height */
#define KATE_TASKBAR_H 40 /* .taskbar height */
#define KATE_PANEL_HEADER_H 24 /* .panel-header */
#define KATE_TITLEBAR_H 28 /* 窗口标题栏 */
#define KATE_ICON_W 104 /* .desktop-icon width */
#define KATE_ICON_H 90 /* .desktop-icon height (含 label) */
#define KATE_ICON_IMG 40 /* .di-img / .di-fallback */
#define KATE_ICON_GAP 18 /* .desktop-grid gap */
#define KATE_ICON_PAD 24 /* .desktop-canvas padding */
#define KATE_TB_ITEM_MAX_W 200 /* .tb-item max-width */
#define KATE_PAGE_PAD 8 /* 页面 padding */
/* ===================================================================
* Winux-Kate 特效函数
* =================================================================== */
/* 绘制扫描线纹理(已禁用以提升性能)
* 原效果:在指定矩形区域内叠加半透明青色水平细线 */
static inline void du_kate_scanlines(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h) {
/* 性能优化:禁用扫描线纹理,减少约50%的像素处理开销 */
(void)ctx; (void)x; (void)y; (void)w; (void)h;
}
/* 绘制带光晕的矩形边框(box-shadow 效果)
* 外发光:在矩形外围绘制渐变青色光晕 */
static inline void du_kate_glow_border(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, du_u32 glow_color) {
/* 2px 渐变光晕,alpha 混合到实际背景(逐像素读-混合-写) */
for (du_i64 i = 2; i >= 1; i--) {
du_u32 alpha = (i == 2) ? 50 : 100;
/* 上下边 */
for (du_i64 c = -i; c < w + i; c++) {
du_i64 xt = x + c;
du_pixel(ctx, xt, y - i,
du_blend(du_pixel_read(ctx, xt, y - i), glow_color, alpha));
du_pixel(ctx, xt, y + h + i - 1,
du_blend(du_pixel_read(ctx, xt, y + h + i - 1), glow_color, alpha));
}
/* 左右边 */
for (du_i64 r = -i; r < h + i; r++) {
du_i64 yt = y + r;
du_pixel(ctx, x - i, yt,
du_blend(du_pixel_read(ctx, x - i, yt), glow_color, alpha));
du_pixel(ctx, x + w + i - 1, yt,
du_blend(du_pixel_read(ctx, x + w + i - 1, yt), glow_color, alpha));
}
}
}
/* 绘制 Winux-Kate 面板(.panel 样式)
* - 半透明深蓝背景
* - 青色边框
* - 内外光晕
* - 扫描线纹理
* - 可选标题栏 */
static inline void du_kate_panel(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, const char *header) {
/* 1. 背景 */
du_fill_rounded_rect(ctx, x, y, w, h, KS_BG_SECONDARY, 2);
/* 2. 边框 */
du_rect_outline(ctx, x, y, w, h, KS_BORDER, 2);
/* 3. 外光晕 */
du_kate_glow_border(ctx, x, y, w, h, KS_ACCENT);
/* 4. 扫描线纹理 */
du_kate_scanlines(ctx, x + 1, y + 1, w - 2, h - 2);
/* 5. 标题栏 */
if (header) {
du_fill_rect(ctx, x, y, w, KATE_PANEL_HEADER_H, 0xFF062838u); /* 深青色标题栏 */
du_divider_h(ctx, x, y + KATE_PANEL_HEADER_H, w, KS_BORDER);
/* 标题文字:青色,大写,letter-spacing */
du_draw_string(ctx, header, x + 10, y + (KATE_PANEL_HEADER_H - DU_ASCII_LINE_H) / 2 + 2,
KS_ACCENT, 0xFF062838u, DU_ASCII_STEP);
/* 标题前的小绿点 */
du_fill_rect(ctx, x + 4, y + KATE_PANEL_HEADER_H / 2 - 3, 6, 6, KS_ACCENT2);
}
}
/* 绘制 Winux-Kate 顶部栏(.topbar 样式)
* - 34px 高
* - 渐变背景
* - 底部边框
* - 品牌名 + 时钟槽位 */
static inline void du_kate_topbar(du_context *ctx, du_i64 w,
const char *brand, const char *clock) {
/* 渐变背景:左青色微亮 → 右深色 */
du_fill_rect_gradient(ctx, 0, 0, w, KATE_TOPBAR_H,
0xFF0A3050u, 0xFF02040Au);
/* 底部分隔线 */
du_divider_h(ctx, 0, KATE_TOPBAR_H, w, KS_BORDER);
/* 品牌名(青色,加粗,letter-spacing 4px)*/
if (brand) {
du_draw_string(ctx, brand, 12, (KATE_TOPBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_ACCENT, 0, DU_ASCII_STEP);
}
/* 时钟(右侧,绿色)*/
if (clock) {
du_i64 cl = 0;
while (clock[cl]) cl++;
du_draw_string(ctx, clock, w - 12 - cl * DU_ASCII_STEP,
(KATE_TOPBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_ACCENT2, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 状态栏(.statusbar 样式)
* - 32px 高
* - 顶部边框
* - 半透明青色背景 */
static inline void du_kate_statusbar(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, const char *status_text) {
du_fill_rect(ctx, x, y, w, KATE_STATUSBAR_H, 0xFF051828u);
du_divider_h(ctx, x, y, w, KS_BORDER);
if (status_text) {
du_draw_string(ctx, status_text, x + 12,
y + (KATE_STATUSBAR_H - DU_ASCII_LINE_H) / 2 + 1,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 桌面图标(.desktop-icon 样式)
* - 104px 宽
* - 40x40 图标区域
* - 下方文字标签
* - 选中态:青色边框 + 光晕 */
static inline void du_kate_desktop_icon(du_context *ctx, du_i64 x, du_i64 y,
const char *label, int selected) {
/* 悬停/选中背景 */
if (selected) {
du_fill_rect(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT_DIM);
du_rect_outline(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT, 1);
du_kate_glow_border(ctx, x, y, KATE_ICON_W, KATE_ICON_H, KS_ACCENT);
}
/* 图标占位区域(40x40 居中)*/
du_i64 ix = x + (KATE_ICON_W - KATE_ICON_IMG) / 2;
du_i64 iy = y + 10;
du_rect_outline(ctx, ix, iy, KATE_ICON_IMG, KATE_ICON_IMG, KS_BORDER, 1);
/* 标签 */
if (label) {
du_i64 ll = 0;
while (label[ll]) ll++;
du_i64 lx = x + (KATE_ICON_W - ll * DU_ASCII_STEP) / 2;
if (lx < x + 2) lx = x + 2;
du_draw_string(ctx, label, lx, y + KATE_ICON_IMG + 18,
KS_TEXT_PRIMARY, 0, DU_ASCII_STEP);
}
}
/* 绘制 Winux-Kate 任务栏项(.tb-item 样式) */
static inline void du_kate_tb_item(du_context *ctx, du_i64 x, du_i64 y,
const char *title, int active) {
du_i64 tl = 0;
while (title[tl]) tl++;
du_i64 w = tl * DU_ASCII_STEP + 24;
if (w > KATE_TB_ITEM_MAX_W) w = KATE_TB_ITEM_MAX_W;
du_u32 fg = active ? KS_ACCENT : KS_TEXT_PRIMARY;
if (active) {
du_fill_rect(ctx, x, y, w, 28, KS_ACCENT_DIM);
}
du_rect_outline(ctx, x, y, w, 28, KS_BORDER, 1);
if (active) {
du_kate_glow_border(ctx, x, y, w, 28, KS_ACCENT);
}
du_draw_string(ctx, title, x + 8, y + (28 - DU_ASCII_LINE_H) / 2 + 1,
fg, 0, DU_ASCII_STEP);
}
/* 绘制 Winux-Kate 按钮 (.btn 样式) */
static inline void du_kate_button(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h, const char *label,
int hover) {
du_u32 fg = KS_ACCENT;
if (hover) {
du_fill_rect(ctx, x, y, w, h, KS_ACCENT_DIM);
}
du_rect_outline(ctx, x, y, w, h, KS_BORDER, 1);
if (hover) {
du_kate_glow_border(ctx, x, y, w, h, KS_ACCENT);
}
du_i64 ll = 0;
while (label[ll]) ll++;
du_i64 lx = x + (w - ll * DU_ASCII_STEP) / 2;
du_draw_string(ctx, label, lx, y + (h - DU_ASCII_LINE_H) / 2 + 1,
fg, hover ? KS_ACCENT_DIM : KS_BG_SECONDARY, DU_ASCII_STEP);
}
#endif /* DESHAB_UI_H */
+33
View File
@@ -0,0 +1,33 @@
#ifndef UTSM_EPT_H
#define UTSM_EPT_H
#include <utsm/types.h>
/* EPT 页表结构:4 级页表(PML4 / PDPT / PD / PT),每级 512 项,每页 4KB。 */
#define EPT_PAGE_SIZE 4096ULL
#define EPT_ENTRIES 512ULL
/* EPTP 构造:bits [5:0]=memory type, bits [7:6]=walk length-1, bits [51:12]=PML4 HPA */
#define EPTP_WALK_LEN_4 (3ULL << 6) /* 4 级页表 */
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
int ept_init(void);
/* 构造 EPTP 值(用于 VMCS EPT_POINTER 字段)。 */
u64 ept_get_eptp(void);
/* 在 EPT 中映射一段 GPA→HPA 区间,权限由 flags 决定(EPT_READ/WRITE/EXECUTE)。
* 自动按 4KB 分配中间页表页。返回 0 成功。 */
int ept_map_range(u64 gpa, u64 hpa, u64 size, u64 flags);
/* 便利函数:1:1 映射(GPA==HPA)。 */
int ept_identity_map(u64 gpa, u64 size, u64 flags);
/* Walk the EPT to translate a GPA to its mapped HPA.
* Returns the HPA on success, 0 if the GPA is not mapped.
* Handles 4KB pages and 2MB large pages. */
u64 ept_gpa_to_hpa(u64 gpa);
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef UTSM_HYPERCALL_H
#define UTSM_HYPERCALL_H
/* UTSM-side VMCALL hypercall handler.
*
* When the Linux guest executes a VMCALL instruction, it triggers a VM-Exit
* with exit reason EXIT_VMCALL (18). The guest places:
* RAX = UTSM_HCALL_MAGIC | op (identifies this as a UTSM hypercall)
* RDI = arg0
* RSI = arg1
* RDX = arg2
*
* The handler validates the magic, dispatches to the operation handler,
* writes the return value into GUEST_RAX, and advances GUEST_RIP past
* the VMCALL instruction so the guest resumes normally.
*
* If the magic doesn't match, the handler returns a VMFAIL indication
* (by setting the VMCS vm-exit instruction error), which causes the guest
* to see VMCALL fail. Phase 1.3: we just return -UTSM_HCALL_NOSYS.
*
* Protocol details in CODE/utsm-ipc/ipc_proto.h.
*/
#include <utsm/types.h>
/* VM-Exit handler for VMCALL (exit reason 18).
*
* Reads guest RAX/RDI/RSI/RDX from the VMCS save area (passed in by the
* assembly exit stub), dispatches the hypercall, writes the result to
* GUEST_RAX, and advances GUEST_RIP.
*
* Returns 1 to resume the guest, 0 to terminate. */
int hypercall_handle(u64 guest_rax, u64 guest_rdi, u64 guest_rsi, u64 guest_rdx,
u64 guest_rip, u64 instr_len);
/* Statistics */
u64 hypercall_get_count(void);
#endif /* UTSM_HYPERCALL_H */
+68
View File
@@ -0,0 +1,68 @@
#ifndef UTSM_IPC_SHM_H
#define UTSM_IPC_SHM_H
/* UTSM-side IPC shared memory manager.
*
* Allocates a 1MB contiguous physical region, initializes the
* utsm_ipc_shm structure (header + two ring buffers + payload pool),
* and EPT-maps it at LINUX_GUEST_IPC_SHM_GPA so the Linux guest can
* access it after querying the GPA via UTSM_HCALL_SHM_INFO.
*
* The shared protocol structures are defined in CODE/utsm-ipc/ipc_proto.h,
* which is included by both UTSM and the Linux utsm-ipc driver.
*
* Ring buffer discipline:
* utsm_to_linux: UTSM produces (ipc_shm_send), Linux consumes
* linux_to_utsm: Linux produces, UTSM consumes (ipc_shm_recv)
*/
#include <utsm/types.h>
#include <ipc_proto.h> /* shared UTSM↔Linux IPC protocol (struct utsm_ipc_msg, etc.) */
/* ===== Initialization ===== */
/* Allocate, initialize, and EPT-map the IPC shared memory region.
* Must be called after vmm_init() (EPT is required) and before linux_launch().
* Returns 0 on success, negative on error. */
int ipc_shm_init(void);
/* Shut down IPC: mark UTSM as not ready.
* Does not free the shared memory (it may still be referenced by EPT). */
void ipc_shm_shutdown(void);
/* Query state */
int ipc_shm_is_ready(void);
/* Get the HPA of the shared memory region (for EPT mapping).
* Returns 0 if not initialized. */
u64 ipc_shm_get_hpa(void);
/* Get the GPA of the shared memory region (passed to Linux via hypercall). */
u64 ipc_shm_get_gpa(void);
/* Get the size of the shared memory region. */
u64 ipc_shm_get_size(void);
/* ===== Message API (UTSM-side) ===== */
/* Send a message from UTSM to Linux (enqueues into utsm_to_linux ring).
* Returns 0 on success, -1 if the ring is full. */
int ipc_shm_send(u32 msg_type, const void *data, u32 data_len);
/* Receive a message from Linux to UTSM (dequeues from linux_to_utsm ring).
* Returns 0 on success (msg filled in), -1 if the ring is empty. */
int ipc_shm_recv(struct utsm_ipc_msg *msg_out);
/* Check if there are pending messages from Linux. */
int ipc_shm_has_pending(void);
/* Notify Linux that a message is waiting (placeholder for IPI injection).
* Phase 1.3: logs the event; Phase 1.4+ will inject a guest IRQ. */
void ipc_shm_notify_linux(void);
/* ===== Debug ===== */
/* Dump ring buffer statistics to the UTSM serial console. */
void ipc_shm_dump_stats(void);
#endif /* UTSM_IPC_SHM_H */
+56
View File
@@ -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 <utsm/types.h>
#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 */
+199
View File
@@ -0,0 +1,199 @@
#ifndef UTSM_LINUX_LOADER_H
#define UTSM_LINUX_LOADER_H
#include <utsm/types.h>
/* ===== Linux boot protocol structures (Documentation/x86/boot.rst) ===== */
/* bzImage setup_header — lives at offset 0x1f1 inside bzImage and boot_params.
* Only the fields we actually use are listed; gaps are reserved/padding. */
struct linux_setup_header {
u8 setup_sects; /* 0x1f1: number of setup sectors */
u16 root_flags; /* 0x1f2 */
u32 syssize; /* 0x1f4: protected-mode code size / 16 */
u16 ram_size; /* 0x1f8 */
u16 vid_mode; /* 0x1fa */
u16 boot_flag; /* 0x1fe: 0xAA55 */
u16 jump; /* 0x200: jump insn */
u32 header; /* 0x202: "HdrS" magic */
u16 version; /* 0x206: boot protocol version */
u32 realmode_swtch; /* 0x208 */
u16 start_sys_seg; /* 0x20c */
u16 kernel_version; /* 0x20e: offset to version string */
u8 type_of_loader; /* 0x210 */
u8 loadflags; /* 0x211 */
u16 setup_move_size; /* 0x212 */
u32 code32_start; /* 0x214: 32-bit entry (also 64-bit entry base) */
u32 ramdisk_image; /* 0x218 */
u32 ramdisk_size; /* 0x21c */
u32 bootsect_kludge; /* 0x220 */
u16 heap_end_ptr; /* 0x224 */
u8 ext_loader_ver; /* 0x226 */
u8 ext_loader_type; /* 0x227 */
u32 cmd_line_ptr; /* 0x228: command line physical address */
u32 init_size; /* 0x22c: total init memory needed */
u32 acpi_rsdp_addr; /* 0x230 */
u8 _pad0[4]; /* 0x234: relocatable_kernel + min_alignment */
u16 xloadflags; /* 0x236: bit0=XLF_KERNEL_64 */
u32 cmdline_size; /* 0x238 */
u32 _pad1; /* 0x23c */
u64 setup_data; /* 0x240: linked list of setup_data */
u64 pref_address; /* 0x248 → wait, this is payload_offset */
/* NOTE: The real layout continues with payload_offset at 0x248.
* However, pref_address is at 0x258 and init_size at 0x260.
* The struct above is intentionally simplified — we read fields
* by offset rather than relying on this struct for the full layout. */
} __attribute__((packed));
/* boot_params is 4096 bytes. The setup_header is at offset 0x1f1. */
#define BOOT_PARAMS_SIZE 4096
#define SETUP_HEADER_OFFSET 0x1f1
/* Key field offsets within bzImage / boot_params */
#define BZ_BOOT_FLAG_OFF 0x1fe
#define BZ_HEADER_MAGIC 0x53726448 /* "HdrS" */
#define BZ_HEADER_OFF 0x202
#define BZ_VERSION_OFF 0x206
#define BZ_TYPE_OF_LOADER_OFF 0x210
#define BZ_LOADFLAGS_OFF 0x211
#define BZ_CODE32_START_OFF 0x214
#define BZ_RAMDISK_IMAGE_OFF 0x218
#define BZ_RAMDISK_SIZE_OFF 0x21c
#define BZ_CMD_LINE_PTR_OFF 0x228
#define BZ_INIT_SIZE_OFF 0x22c /* wait — actually init_size is at 0x260 */
#define BZ_XLOADFLAGS_OFF 0x236
#define BZ_CMDLINE_SIZE_OFF 0x238
#define BZ_PAYLOAD_OFFSET_OFF 0x248
#define BZ_PAYLOAD_LENGTH_OFF 0x24c
#define BZ_PREF_ADDRESS_OFF 0x258
#define BZ_INIT_SIZE_ACTUAL_OFF 0x260
/* xloadflags bits */
#define XLF_KERNEL_64 0x0001
#define XLF_CAN_BE_LOADED_ABOVE 0x0002
#define XLF_EFI_HANDOVER 0x0010
#define XLF_EFI_KEXEC 0x0020
/* loadflags bits */
#define LOADFLAG_LOADED_HIGH 0x01
#define LOADFLAG_KEEP_ROOT 0x02
#define LOADFLAG_CAN_USE_HEAP 0x80
/* e820 entry types */
#define E820_TYPE_RAM 1
#define E820_TYPE_RESERVED 2
#define E820_TYPE_ACPI 3
#define E820_TYPE_NVS 4
#define E820_TYPE_UNUSABLE 5
#define E820_TYPE_PMEM 7
/* e820_table entry in boot_params at offset 0x2e0 (actually 0x2d0 in older,
* 0x2e0 in newer). We use 0x2e0 and the e820_entries count at 0x1e8.
* Each entry is 20 bytes: u64 addr, u64 size, u32 type. */
#define E820_ENTRIES_OFFSET 0x1e8
#define E820_TABLE_OFFSET 0x2e0
#define E820_MAX_ENTRIES 128
#define E820_ENTRY_SIZE 20
/* ===== Guest memory layout (GPA) =====
* Based on utsm-linux-dual-kernel-architecture.md.
* 0x00000000-0x003FFFFF : UTSM reserved (denied to Linux)
* 0x00400000-0x004FFFFF : boot_params / cmdline / GDT / page tables / stack
* 0x01000000-0x02FFFFFF : Linux kernel image
* 0x03000000-0x03FFFFFF : Linux initrd
* 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW)
* 0x05000000-MAX : Linux general RAM
*/
#define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */
#define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */
#define LINUX_GUEST_CMDLINE_GPA 0x00500000ULL /* 5 MB */
#define LINUX_GUEST_INITRD_GPA 0x03000000ULL /* 48 MB (within kernel+initrd region) */
#define LINUX_GUEST_STACK_GPA 0x00800000ULL /* 8 MB (stack grows down) */
#define LINUX_GUEST_GDT_GPA 0x00600000ULL /* 6 MB (guest GDT) */
#define LINUX_GUEST_PGT_GPA 0x00700000ULL /* 7 MB (guest page tables) */
#define LINUX_GUEST_CMDLINE_MAX 4096
/* IPC shared memory GPA (must match CODE/utsm-ipc/ipc_proto.h) */
#define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */
#define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
/* General RAM starts after IPC region */
#define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */
/* ===== virtio-mmio device GPA layout =====
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
* guest 通过 cmdline 参数 virtio_mmio.device= 发现这些设备。
* 每个设备占 0x200 字节寄存器空间(virtio-mmio 规范要求 4KB 对齐)。
*
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
* 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。
*
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x09000000)。 */
#define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL
#define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE)
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
/* ===== Linux loader API ===== */
/* Loaded Linux guest state */
struct linux_guest_info {
u64 kernel_gpa; /* GPA where kernel was loaded */
u64 kernel_entry; /* 64-bit entry point GPA */
u64 kernel_size; /* Size of loaded kernel image */
u64 bootparams_gpa; /* GPA of boot_params */
u64 cmdline_gpa; /* GPA of command line */
u64 cmdline_size; /* Command line length (with NUL) */
u64 initrd_gpa; /* GPA of initrd (0 = none) */
u64 initrd_size; /* Size of initrd */
u64 pgt_gpa; /* GPA of guest page tables */
u64 gdt_gpa; /* GPA of guest GDT */
int loaded; /* 1 = successfully loaded */
};
/* Find the Linux bzImage boot module among Limine modules.
* Returns pointer to the module data, or NULL if not found.
* Sets *size_out to the module size. */
void *linux_find_bzimage_module(u64 *size_out);
/* Parse bzImage header and extract key parameters.
* Returns 0 on success, negative on error. */
int linux_parse_bzimage(const void *bzimage, u64 size,
u64 *payload_offset_out, u64 *payload_length_out,
u64 *init_size_out, u64 *entry_offset_out);
/* Initialize the Linux guest: parse bzImage, allocate guest memory,
* set up boot_params, page tables, GDT, and EPT mappings.
* Returns 0 on success, negative on error. */
int linux_loader_init(void);
/* Get the loaded Linux guest info (NULL if not loaded). */
const struct linux_guest_info *linux_get_guest_info(void);
/* Configure VMCS for Linux guest and perform vmlaunch.
* Must be called after vmm_init() + linux_loader_init().
* Returns 0 on successful guest termination, negative on error. */
int linux_launch(void);
/* 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.
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
* virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5,
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。 */
#define LINUX_DEFAULT_CMDLINE \
"console=ttyS0,115200 earlyprintk=serial nokaslr " \
"no_timer_check loglevel=7 nohlt idle=poll " \
"noapic nolapic nosmp " \
"virtio_mmio.device=4K@0xF4000000:5 " \
"virtio_mmio.device=4K@0xF4001000:6"
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef UTSM_LINUX_RESUME_H
#define UTSM_LINUX_RESUME_H
#include <utsm/types.h>
/* 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 */
+42
View File
@@ -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 <utsm/types.h>
#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 */
+4
View File
@@ -9,6 +9,10 @@
#include <utsm/segment.h>
#include <utsm/capability.h>
#include <utsm/utrw.h>
#include <utsm/vmm.h>
#include <utsm/linux_loader.h>
#include <utsm/ipc_shm.h>
#include <utsm/hypercall.h>
void utsm_init(void);
int utsm_selftest_run(void);
+156
View File
@@ -0,0 +1,156 @@
#ifndef UTSM_VIRTIO_MMIO_H
#define UTSM_VIRTIO_MMIO_H
/* virtio_mmio.h — UTSM virtio-mmio 设备模拟后端框架。
*
* 为 Linux guest 提供 virtio-mmio 设备(block + net)。
* 设备 GPA 位于 VIRTIO_MMIO_GPA_BASE 起的高位 MMIO 区,EPT 故意不映射,
* guest 访问时触发 EPT violation,vmexit handler 调用本模块的
* virtio_mmio_read/write 模拟寄存器行为。
*
* 参考 Virtio Spec 1.1 §4.2 (virtio-mmio)。
* guest 通过 cmdline virtio_mmio.device= 参数发现设备(无需 PCI/ACPI)。
*/
#include <utsm/types.h>
/* virtio-mmio 寄存器偏移(Virtio Spec 1.1 §4.2.2.1,均为 32 位寄存器) */
#define VIRTIO_MMIO_MAGIC_VALUE 0x000 /* R "virt" = 0x74726976 */
#define VIRTIO_MMIO_VERSION 0x004 /* R 1=legacy, 2=modern */
#define VIRTIO_MMIO_DEVICE_ID 0x008 /* R 1=net, 2=blk, 3=console... */
#define VIRTIO_MMIO_VENDOR_ID 0x00C /* R */
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* R features[31:0] (sel) */
#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* W */
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* W features[31:0] (sel) */
#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* W */
#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 /* W legacy only */
#define VIRTIO_MMIO_QUEUE_SEL 0x030 /* W */
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 /* R */
#define VIRTIO_MMIO_QUEUE_NUM 0x038 /* W */
#define VIRTIO_MMIO_QUEUE_ALIGN 0x03C /* W legacy only */
#define VIRTIO_MMIO_QUEUE_PFN 0x040 /* RW legacy only */
#define VIRTIO_MMIO_QUEUE_READY 0x044 /* RW modern */
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 /* W */
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 /* R */
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 /* W */
#define VIRTIO_MMIO_STATUS 0x070 /* RW */
#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 /* W modern */
#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 /* W modern */
#define VIRTIO_MMIO_QUEUE_DRIVER_LOW 0x090 /* W modern */
#define VIRTIO_MMIO_QUEUE_DRIVER_HIGH 0x094 /* W modern */
#define VIRTIO_MMIO_QUEUE_DEVICE_LOW 0x0A0 /* W modern */
#define VIRTIO_MMIO_QUEUE_DEVICE_HIGH 0x0A4 /* W modern */
#define VIRTIO_MMIO_CONFIG_GENERATION 0x0FC /* R modern */
#define VIRTIO_MMIO_CONFIG 0x100 /* RW device-specific config */
/* device IDs */
#define VIRTIO_ID_NET 1
#define VIRTIO_ID_BLOCK 2
#define VIRTIO_ID_CONSOLE 3
/* device status bits */
#define VIRTIO_STATUS_ACKNOWLEDGE 1
#define VIRTIO_STATUS_DRIVER 2
#define VIRTIO_STATUS_DRIVER_OK 4
#define VIRTIO_STATUS_FEATURES_OK 8
#define VIRTIO_STATUS_FAILED 128
/* 通用 feature bits */
#define VIRTIO_F_VERSION_1 32 /* modern 设备必须提供(bit32=高32位word bit0) */
/* ===== 后端框架 API ===== */
/* 初始化 virtio-mmio 后端框架(注册 block + net 设备)。
* 在 linux_loader_init 之后、linux_launch 之前调用。 */
void virtio_mmio_init(void);
/* 处理一次 virtio-mmio 寄存器读/写(由 vmexit EPT violation 调用)。
* gpa : 触发访问的 GPA(落在某设备 4KB 寄存器区)
* width : 访问宽度(字节),通常为 4
* value : 写操作的数据
* 返回读操作的数据(写操作返回 0)。 */
u64 virtio_mmio_read(u64 gpa, int width);
void virtio_mmio_write(u64 gpa, u64 value, int width);
/* 后端驱动注册接口(virtio_blk.c / virtio_net.c 调用) */
struct virtio_backend {
u32 device_id; /* VIRTIO_ID_* */
u64 gpa_base; /* 该设备的 MMIO GPA 起始 */
u32 irq; /* guest ISA IRQ 号(cmdline virtio_mmio.device=:IRQ,
* legacy PIC 下 vector = 0x30 + irq;0 = 不注入中断) */
u32 num_queues; /* 队列数(blk=1, net=2) */
u32 queue_size; /* 每队列描述符数 */
u64 device_features; /* device 提供的 feature 位 */
u32 config_len; /* config 空间长度(从 0x100 起) */
/* 读取 device config 空间(offset 相对 0x100)。返回 32 位值。 */
u32 (*read_config)(u32 offset, int width);
/* 通知:guest 写入 QUEUE_NOTIFY,后端处理该队列的请求。 */
void (*queue_notify)(u32 queue_idx);
/* 复位设备。 */
void (*reset)(void);
};
/* 注册一个后端设备(内部使用,按 gpa_base 排序查找)。 */
int virtio_mmio_register(struct virtio_backend *dev);
/* 提供 guest GPA→host 虚拟地址转换(后端访问 virtqueue/描述符用)。
* 通过 EPT 翻译 GPA 到 HPA,再加 HHDM offset 得到 host 虚拟地址。
* 返回 NULL 表示 GPA 未映射。 */
void *virtio_gpa_to_host(u64 gpa);
/* ===== virtqueue 结构(Virtio Spec 1.1 §2.6 split ring) ===== */
/* 描述符:16 字节 */
struct virtq_desc {
u64 addr; /* GPA of data buffer */
u32 len; /* data length */
u16 flags; /* VIRTQ_DESC_F_* */
u16 next; /* next descriptor if F_NEXT */
} __attribute__((packed));
#define VIRTQ_DESC_F_NEXT 1
#define VIRTQ_DESC_F_WRITE 2
#define VIRTQ_DESC_F_INDIRECT 4
/* avail ring(driver → device) */
struct virtq_avail {
u16 flags;
u16 idx;
u16 ring[1]; /* queue_size entries */
} __attribute__((packed));
/* used ring 元素 */
struct virtq_used_elem {
u32 id; /* descriptor chain head index */
u32 len; /* bytes written (device→driver) */
} __attribute__((packed));
/* used ring(device → driver) */
struct virtq_used {
u16 flags;
u16 idx;
struct virtq_used_elem ring[1];
} __attribute__((packed));
/* ===== 队列状态访问 API(后端 queue_notify 用) =====
*
* 后端在其 queue_notify(queue_idx) 回调中需要:
* 1. 读 avail ring 取请求描述符链
* 2. 处理请求(读/写 guest 数据 buffer)
* 3. 写 used ring 回执
* 以下函数按 device_id + queue_idx 定位队列,操作 guest 内存。 */
/* 取队列的 desc/avail/used 表 host 地址 + 队列大小。
* 返回 0 成功(指针填充),负值失败(队列未 ready/未映射)。 */
int virtio_queue_get_ptrs(u32 device_id, u32 queue_idx,
struct virtq_desc **desc_out,
struct virtq_avail **avail_out,
struct virtq_used **used_out,
u32 *queue_num_out);
/* 更新 used ring idx(后端处理完一批请求后调用,让 guest 看到新回执)。
* 由后端在写 used ring 元素后调用,内部做内存屏障。 */
void virtio_queue_bump_used(struct virtq_used *used, u16 new_idx);
#endif /* UTSM_VIRTIO_MMIO_H */
+87
View File
@@ -0,0 +1,87 @@
#ifndef UTSM_VMM_H
#define UTSM_VMM_H
#include <utsm/types.h>
/* VMM 顶层 API:协调 VMX/EPT/VMCS/VMEXIT,启动 guest。
*
* Phase 1.1(当前):vmm_init() 完成 VMX root 启用 + EPT 初始化,
* vmm_self_test() 启动一个最小 guest(HLT 指令)验证 VMEXIT 路径。
*
* Phase 1.2+:vmm_launch_linux() 加载并启动 Linux 6.6 LTS guest。
*/
/* ===== Guest register save area =====
*
* VMCS does NOT save guest GPRs (RAX-R15) on VM-Exit. The assembly VM-Exit
* handler (vmexit_asm.S) saves all 16 GPRs into this structure before
* calling the C dispatcher, and restores them before vmresume.
*
* C handlers (e.g. hypercall.c) can read guest arguments from this structure
* and write return values (e.g. g_guest_regs.rax = retval) which will be
* loaded into the CPU before vmresume resumes the guest. */
struct vmexit_guest_regs {
u64 rax;
u64 rbx;
u64 rcx;
u64 rdx;
u64 rsi;
u64 rdi;
u64 rbp;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
u64 r12;
u64 r13;
u64 r14;
u64 r15;
};
/* Global guest register save area (referenced by vmexit_asm.S).
* 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。
* 返回 0 成功,负值失败。 */
int vmm_init(void);
/* 关闭 VMM:vmxoff。 */
void vmm_shutdown(void);
/* Phase 1.1 自检:启动最小 guest 验证 VMX 路径。
* guest 代码为内嵌的 HLT 指令,预期触发 HLT VMEXIT。
* 返回 0 成功。 */
int vmm_self_test(void);
/* VMM 状态查询 */
int vmm_is_ready(void);
u64 vmm_get_eptp(void);
#endif
+288
View File
@@ -0,0 +1,288 @@
#ifndef UTSM_VMX_H
#define UTSM_VMX_H
#include <utsm/types.h>
/* ===== VMX MSRs ===== */
#define IA32_VMX_BASIC 0x480
#define IA32_VMX_PINBASED_CTLS 0x481
#define IA32_VMX_PROCBASED_CTLS 0x482
#define IA32_VMX_EXIT_CTLS 0x483
#define IA32_VMX_ENTRY_CTLS 0x484
#define IA32_VMX_PROCBASED_CTLS2 0x48B
#define IA32_VMX_EPT_VPID_CAP 0x48C
#define IA32_VMX_TRUE_PINBASED_CTLS 0x48D
#define IA32_VMX_TRUE_PROCBASED_CTLS 0x48E
#define IA32_VMX_TRUE_EXIT_CTLS 0x48F
#define IA32_VMX_TRUE_ENTRY_CTLS 0x490
/* ===== CR4 / CR0 bits ===== */
#define CR4_VMXE_BIT 13ULL
#define CR4_VMXE (1ULL << CR4_VMXE_BIT)
#define CR4_PAE (1ULL << 5)
#define CR4_PGE (1ULL << 7)
#define CR4_PSE (1ULL << 4)
#define CR0_PE (1ULL << 0)
#define CR0_NE (1ULL << 5)
#define CR0_WP (1ULL << 16)
#define CR0_PG (1ULL << 31)
/* ===== VMCS field encodings (Intel SDM Vol 3, Appendix H) ===== */
/* Field encoding: bit 13=1 -> host root, bit 12=1 -> guest, bits [11:10] = type, bits [9:1] = index */
#define VMCS_FIELD_HOST 0x2000ULL
#define VMCS_FIELD_GUEST 0x1000ULL
#define VMCS_FIELD_CTRL 0x4000ULL
#define VMCS_FIELD_READ 0x6000ULL
/* Control fields */
#define VMCS_VPID 0x0000
#define VMCS_GUEST_ES_ADDR 0x0006
#define VMCS_GUEST_CS_ADDR 0x0008
#define VMCS_GUEST_SS_ADDR 0x000A
#define VMCS_GUEST_DS_ADDR 0x000C
#define VMCS_GUEST_FS_ADDR 0x000E
#define VMCS_GUEST_GS_ADDR 0x0010
#define VMCS_GUEST_LDTR_ADDR 0x0012
#define VMCS_GUEST_TR_ADDR 0x0014
#define VMCS_GUEST_INTR_STATUS 0x0009
#define VMCS_IO_BITMAP_A 0x2000
#define VMCS_IO_BITMAP_B 0x2002
#define VMCS_MSR_BITMAP 0x2004
#define VMCS_TSC_OFFSET 0x2010
#define VMCS_EPT_POINTER 0x201A
/* Read-only data fields */
#define VMCS_GUEST_PHYSICAL_ADDR 0x2400
#define VMCS_EXIT_REASON 0x4402
#define VMCS_EXIT_QUALIFICATION 0x6400
#define VMCS_IO_RCX 0x6404
#define VMCS_IO_RSI 0x6406
#define VMCS_IO_RDI 0x6408
#define VMCS_IO_RIP 0x640A
#define VMCS_GUEST_LINEAR_ADDR 0x640A
#define VMCS_INSTRUCTION_LENGTH 0x440C
#define VMCS_VMX_INSTRUCTION_ERROR 0x4406
/* Guest-state fields */
#define VMCS_GUEST_ES_SELECTOR 0x0800
#define VMCS_GUEST_CS_SELECTOR 0x0802
#define VMCS_GUEST_SS_SELECTOR 0x0804
#define VMCS_GUEST_DS_SELECTOR 0x0806
#define VMCS_GUEST_FS_SELECTOR 0x0808
#define VMCS_GUEST_GS_SELECTOR 0x080A
#define VMCS_GUEST_LDTR_SELECTOR 0x080C
#define VMCS_GUEST_TR_SELECTOR 0x080E
#define VMCS_GUEST_CS_LIMIT 0x4800
#define VMCS_GUEST_SS_LIMIT 0x4804
#define VMCS_GUEST_DS_LIMIT 0x4806
#define VMCS_GUEST_ES_LIMIT 0x4801
#define VMCS_GUEST_FS_LIMIT 0x4808
#define VMCS_GUEST_GS_LIMIT 0x480A
#define VMCS_GUEST_LDTR_LIMIT 0x480C
#define VMCS_GUEST_TR_LIMIT 0x480E
#define VMCS_GUEST_CS_ACCESS 0x4816
#define VMCS_GUEST_SS_ACCESS 0x481A
#define VMCS_GUEST_DS_ACCESS 0x4818
#define VMCS_GUEST_ES_ACCESS 0x4814
#define VMCS_GUEST_FS_ACCESS 0x481C
#define VMCS_GUEST_GS_ACCESS 0x481E
#define VMCS_GUEST_LDTR_ACCESS 0x4820
#define VMCS_GUEST_TR_ACCESS 0x4822
#define VMCS_GUEST_INTERRUPTIBILITY 0x4824
#define VMCS_GUEST_ACTIVITY_STATE 0x4826
#define VMCS_GUEST_SYSENTER_CS 0x482A
#define VMCS_GUEST_PREEMPTION_TIMER 0x482E
/* 64-bit guest-state fields(与 PDPTR 共享编码,仅在 EPT 启用时为 EFER/PAT) */
#define VMCS_GUEST_IA32_EFER 0x2806
#define VMCS_GUEST_IA32_PAT 0x2804
#define VMCS_GUEST_IA32_PERF_GLOBAL_CTRL 0x2808
#define VMCS_GUEST_CR0 0x6800
#define VMCS_GUEST_CR3 0x6802
#define VMCS_GUEST_CR4 0x6804
#define VMCS_GUEST_ES_BASE 0x6806
#define VMCS_GUEST_CS_BASE 0x6808
#define VMCS_GUEST_SS_BASE 0x680A
#define VMCS_GUEST_DS_BASE 0x680C
#define VMCS_GUEST_FS_BASE 0x680E
#define VMCS_GUEST_GS_BASE 0x6810
#define VMCS_GUEST_LDTR_BASE 0x6812
#define VMCS_GUEST_TR_BASE 0x6814
#define VMCS_GUEST_GDTR_BASE 0x6816
#define VMCS_GUEST_GDTR_LIMIT 0x4810
#define VMCS_GUEST_IDTR_BASE 0x6818
#define VMCS_GUEST_IDTR_LIMIT 0x4812
#define VMCS_GUEST_DR7 0x681A
#define VMCS_GUEST_RSP 0x681C
#define VMCS_GUEST_RIP 0x681E
#define VMCS_GUEST_RFLAGS 0x6820
#define VMCS_GUEST_SYSENTER_ESP 0x6824
#define VMCS_GUEST_SYSENTER_EIP 0x6826
/* Host-state fields (Intel SDM Vol 3D, Appendix H, Table H-6) */
#define VMCS_HOST_CR0 0x6C00
#define VMCS_HOST_CR3 0x6C02
#define VMCS_HOST_CR4 0x6C04
#define VMCS_HOST_ES_SELECTOR 0x0C00
#define VMCS_HOST_CS_SELECTOR 0x0C02
#define VMCS_HOST_SS_SELECTOR 0x0C04
#define VMCS_HOST_DS_SELECTOR 0x0C06
#define VMCS_HOST_FS_SELECTOR 0x0C08
#define VMCS_HOST_GS_SELECTOR 0x0C0A
#define VMCS_HOST_TR_SELECTOR 0x0C0C
#define VMCS_HOST_FS_BASE 0x6C06
#define VMCS_HOST_GS_BASE 0x6C08
#define VMCS_HOST_TR_BASE 0x6C0A
#define VMCS_HOST_GDTR_BASE 0x6C0C
#define VMCS_HOST_IDTR_BASE 0x6C0E
#define VMCS_HOST_SYSENTER_CS 0x4C00
#define VMCS_HOST_RSP 0x6C14
#define VMCS_HOST_RIP 0x6C16
#define VMCS_HOST_SYSENTER_ESP 0x6C10
#define VMCS_HOST_SYSENTER_EIP 0x6C12
/* 64-bit host-state fields */
#define VMCS_HOST_IA32_EFER 0x2C02
#define VMCS_HOST_IA32_PAT 0x2C00
#define VMCS_HOST_IA32_PERF_GLOBAL_CTRL 0x2C04
/* IA32_EFER MSR (0xC0000080) bits */
#define EFER_SCE (1ULL << 0)
#define EFER_LME (1ULL << 8)
#define EFER_LMA (1ULL << 10)
#define EFER_NXE (1ULL << 11)
#define EFER_SVME (1ULL << 12)
#define EFER_LMSLE (1ULL << 13)
#define EFER_FFXSR (1ULL << 14)
#define EFER_TCE (1ULL << 15)
/* VM-execution control fields */
#define VMCS_PIN_BASED_VM_EXEC_CONTROL 0x4000
#define VMCS_CPU_BASED_VM_EXEC_CONTROL 0x4002
#define VMCS_EXCEPTION_BITMAP 0x4004
#define VMCS_VM_EXIT_CONTROLS 0x400C
#define VMCS_VM_ENTRY_CONTROLS 0x4012
#define VMCS_VM_ENTRY_INTERRUPT_INFO 0x4016
#define VMCS_SECONDARY_VM_EXEC_CONTROL 0x401E
/* ===== Pin-based controls ===== */
#define PIN_EXT_INTERRUPT_EXITING (1ULL << 0)
#define PIN_NMI_EXITING (1ULL << 3)
#define PIN_VMX_PREEMPTION_TIMER (1ULL << 6)
/* ===== Primary processor-based controls ===== */
#define CPU_BASED_INTR_WINDOW_EXITING (1ULL << 2)
#define CPU_BASED_HLT_EXITING (1ULL << 7)
#define CPU_BASED_INVLPG_EXITING (1ULL << 9)
#define CPU_BASED_MWAIT_EXITING (1ULL << 10)
#define CPU_BASED_RDPMC_EXITING (1ULL << 11)
#define CPU_BASED_RDTSC_EXITING (1ULL << 12)
#define CPU_BASED_CR8_LOAD_EXITING (1ULL << 19)
#define CPU_BASED_CR8_STORE_EXITING (1ULL << 20)
#define CPU_BASED_MOV_DR_EXITING (1ULL << 23)
#define CPU_BASED_UNCOND_IO_EXITING (1ULL << 24)
#define CPU_BASED_USE_IO_BITMAPS (1ULL << 25)
#define CPU_BASED_PAUSE_EXITING (1ULL << 30)
#define CPU_BASED_ACTIVATE_SECONDARY (1ULL << 31)
#define CPU_BASED_USE_MSR_BITMAPS (1ULL << 28)
/* ===== Secondary processor-based controls ===== */
#define SEC_EXEC_ENABLE_EPT (1ULL << 1)
#define SEC_EXEC_ENABLE_VPID (1ULL << 5)
#define SEC_EXEC_UNRESTRICTED_GUEST (1ULL << 7)
/* ===== VM-exit controls ===== */
#define VM_EXIT_SAVE_DEBUG_CONTROLS (1ULL << 2)
#define VM_EXIT_HOST_ADDR_SPACE_SIZE (1ULL << 9)
#define VM_EXIT_LOAD_PERF_GLOBAL_CTRL (1ULL << 12)
#define VM_EXIT_ACK_INTR_ON_EXIT (1ULL << 15)
#define VM_EXIT_SAVE_GUEST_EFER (1ULL << 20)
#define VM_EXIT_LOAD_HOST_EFER (1ULL << 21)
/* ===== VM-entry controls ===== */
#define VM_ENTRY_LOAD_DEBUG_CONTROLS (1ULL << 2)
#define VM_ENTRY_IA32E_MODE_GUEST (1ULL << 9)
#define VM_ENTRY_LOAD_GUEST_EFER (1ULL << 15)
/* ===== Exit reasons ===== */
#define EXIT_EXCEPTION_NMI 0
#define EXIT_EXTERNAL_INTERRUPT 1
#define EXIT_TRIPLE_FAULT 2
#define EXIT_INIT_SIGNAL 3
#define EXIT_INTERRUPT_WINDOW 7
#define EXIT_CPUID 10
#define EXIT_INVD 13
#define EXIT_RDMSR 31
#define EXIT_WRMSR 32
#define EXIT_MONITOR 39
#define EXIT_MWAIT 36
#define EXIT_VMCALL 18
#define EXIT_VMCLEAR 19
#define EXIT_VMLAUNCH 20
#define EXIT_VMPTRLD 21
#define EXIT_VMPTRST 22
#define EXIT_VMREAD 23
#define EXIT_VMRESUME 24
#define EXIT_VMWRITE 25
#define EXIT_VMXOFF 26
#define EXIT_VMXON 27
#define EXIT_CONTROL_ACCESS 28
#define EXIT_IO_INSTRUCTION 30
#define EXIT_RDPMC 15
#define EXIT_RDTSC 16
#define EXIT_HLT 12
#define EXIT_INVLPG 14
#define EXIT_EPT_VIOLATION 48
#define EXIT_EPT_MISCONFIG 49
#define EXIT_VMX_PREEMPTION_TIMER 52
#define EXIT_APIC_ACCESS 44
/* ===== VM-entry interruption-information field 编码 ===== */
#define VM_ENTRY_INTR_INFO_VALID 0x80000000ULL
#define VM_ENTRY_INTR_TYPE_HW_IRQ 0 /* external interrupt (bits 10:8) */
/* ===== EPT memory types ===== */
#define EPT_MEMORY_TYPE_UC 0ULL
#define EPT_MEMORY_TYPE_WC 1ULL
#define EPT_MEMORY_TYPE_WT 4ULL
#define EPT_MEMORY_TYPE_WP 5ULL
#define EPT_MEMORY_TYPE_WB 6ULL
/* ===== EPT entry bits ===== */
#define EPT_READ (1ULL << 0)
#define EPT_WRITE (1ULL << 1)
#define EPT_EXECUTE (1ULL << 2)
#define EPT_RWX (EPT_READ | EPT_WRITE | EPT_EXECUTE)
#define EPT_LARGE_PAGE (1ULL << 7)
/* ===== VMX instruction errors ===== */
#define VMXERR_VMCALL_INVALID 1
/* ===== Public API ===== */
int vmx_supported(void);
int vmx_enable(void);
int vmx_disable(void);
int vmx_vmcs_alloc(u64 *phys_out);
int vmx_vmcs_load(u64 phys);
u64 vmx_vmcs_read(u64 field);
void vmx_vmcs_write(u64 field, u64 value);
int vmx_vmlaunch(void);
int vmx_vmresume(void);
u64 vmx_read_msr(u32 msr);
u64 vmx_get_host_cr3(void);
/* ===== 虚拟中断注入(vmexit.c 实现) ===== */
/* 向 guest 队列注入一个 ISA IRQ 向量(legacy PIC:vector = 0x30 + irq)。
* 仅排队;实际在下一次 vmresume 前(guest RFLAGS.IF=1 且无阻塞时)注入,
* 否则 arm interrupt-window exiting,待 guest 可接收时注入。 */
void vmx_guest_queue_irq(u32 vector);
/* VMX preemption timer:返回 1ms 对应的 timer 计数值(按 IA32_VMX_BASIC
* bits[55:48] 的 scale 换算 TSC 频率)。 */
u64 vmx_preemption_quantum_1ms(void);
/* 检查 CPU 是否支持 VMX preemption timer。 */
int vmx_preemption_timer_supported(void);
#endif
+14
View File
@@ -3,6 +3,8 @@
#include <utsm/dkm.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/pe.h>
#include <utsm/linux_compat.h>
#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;
Binary file not shown.
+48
View File
@@ -7,6 +7,8 @@
#include <utsm/dsk.h>
#include <utsm/block.h>
#include <utsm/net.h>
#include <utsm/ipc_shm.h>
#include <utsm/linux_compat.h>
void arch_halt_forever(void);
@@ -43,6 +45,52 @@ void kernel_main(void) {
int result = utsm_selftest_run();
if (result == 0) {
log_info("[UTSM] SELFTEST PASS");
/* Phase 1.1: VMM (VMX/EPT) 初始化 + 自检
* 若 VMX 不可用(如 QEMU 未启用),仅警告,不阻断 DSK 启动 */
if (vmm_init() == 0) {
log_info("[UTSM] VMM init ok");
int vmm_st = vmm_self_test();
if (vmm_st == 0) {
log_info("[UTSM] VMM self-test PASS");
/* Phase 1.2: Linux 加载器 + 启动
* 若 bzImage 模块存在,加载并启动 Linux guest。
* 若无 bzImage,跳过 Linux 启动,继续 DSK 流程。 */
if (linux_loader_init() == 0) {
log_info("[UTSM] Linux loader init ok");
/* Phase 1.3: Initialize IPC shared memory before
* launching Linux. Allocates 1MB, initializes ring
* buffers, and EPT-maps at GPA 0x04000000 so the
* Linux guest can access it via UTSM_HCALL_SHM_INFO. */
if (ipc_shm_init() == 0) {
log_info("[UTSM] IPC shm init ok");
} else {
log_error("[UTSM] IPC shm init failed (non-fatal)");
}
int lin_st = linux_launch();
if (lin_st == 0) {
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);
}
} else {
log_warn("[UTSM] Linux loader unavailable (no bzImage)");
}
} else {
log_error("[UTSM] VMM self-test FAIL");
log_hex64("[UTSM] VMM st=", (u64)(i64)vmm_st);
}
} else {
log_warn("[UTSM] VMM unavailable (VMX not supported or disabled)");
}
if (dsk_load_and_jump() != 0) {
log_error("[UTSM] DSK jump failed");
}
Binary file not shown.
+368
View File
@@ -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 <utsm/arena.h>
#include <utsm/log.h>
#include <utsm/types.h>
/* 合成 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;
}
+185
View File
@@ -0,0 +1,185 @@
#ifndef PE_LOADER_H
#define PE_LOADER_H
/* pe_loader.h — PE32+/PE32 解析与加载器内部接口。 */
#include <utsm/types.h>
#include <utsm/pe.h>
/* ===== 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 */
+290
View File
@@ -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 <utsm/arena.h>
#include <utsm/log.h>
#include <utsm/types.h>
/* 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 */
}
+421
View File
@@ -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 <utsm/arena.h>
#include <utsm/log.h>
#include <utsm/types.h>
/* 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]));
}
+65
View File
@@ -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 <utsm/types.h>
/* 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 */
+975
View File
@@ -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 <utsm/log.h>
#include <utsm/types.h>
/* ===== 内存访问(带边界检查) ===== */
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;
}
+100
View File
@@ -0,0 +1,100 @@
#ifndef X86EMU32_H
#define X86EMU32_H
/* x86emu32.h — x86-32 指令解释器(PE32 执行引擎)。
*
* 寄存器级解释执行 32位 PE 代码。覆盖 mingw-w64 控制台程序常用指令集。
* 遇不支持的指令时日志告警并返回错误。
*/
#include <utsm/types.h>
/* 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 */
+168
View File
@@ -0,0 +1,168 @@
#include <utsm/ept.h>
#include <utsm/vmx.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
/* EPT 4 级页表:PML4[512] -> PDPT[512] -> PD[512] -> PT[512] -> 4KB 页
* 虚拟地址(GPA)拆分:
* bits [51:48] reserved
* bits [47:39] PML4 index
* bits [38:30] PDPT index
* bits [29:21] PD index
* bits [20:12] PT index
* bits [11:0] page offset
*
* EPT entry 格式(4KB 页):
* bits [2:0] R/W/X
* bits [5:3] memory type
* bit 6 ignore PAT
* bit 7 large page
* bits [11:8] ignored
* bits [51:12] physical address (page-aligned)
*/
static u64 g_ept_pml4_phys;
static u64 g_ept_pml4_virt;
static int g_ept_ready;
/* 物理地址 → 虚拟地址(HHDM 直映射) */
static void *phys_to_virt(u64 phys) {
if (!g_hhdm_request.response) return (void *)0;
u64 hhdm = g_hhdm_request.response->offset;
return (void *)(hhdm + phys);
}
/* 分配一页 4KB 对齐物理页,返回物理地址;置零。 */
static u64 ept_alloc_page(void) {
dkm_dma_buffer buf;
if (dma_alloc_pages(1, EPT_PAGE_SIZE, 0x100000000ULL, &buf) != 0) {
log_error("[EPT] alloc page failed");
return 0;
}
/* dma_alloc_pages 已清零 */
return buf.phys;
}
int ept_init(void) {
g_ept_pml4_phys = ept_alloc_page();
if (g_ept_pml4_phys == 0) {
log_error("[EPT] failed to alloc PML4");
return -1;
}
g_ept_pml4_virt = (u64)phys_to_virt(g_ept_pml4_phys);
g_ept_ready = 1;
log_hex64("[EPT] PML4 phys=", g_ept_pml4_phys);
log_info("[EPT] init ok");
return 0;
}
/* 读取 EPT 表项指针,必要时分配中间页表。
* level: 0=PT, 1=PD, 2=PDPT, 3=PML4 */
static u64 *ept_walk(u64 gpa, int alloc_missing) {
u64 *table = (u64 *)g_ept_pml4_virt;
u64 indices[4];
indices[3] = (gpa >> 39) & 0x1FF;
indices[2] = (gpa >> 30) & 0x1FF;
indices[1] = (gpa >> 21) & 0x1FF;
indices[0] = (gpa >> 12) & 0x1FF;
for (int level = 3; level > 0; level--) {
u64 entry = table[indices[level]];
if (!(entry & EPT_READ)) {
if (!alloc_missing) return (u64 *)0;
u64 new_phys = ept_alloc_page();
if (new_phys == 0) return (u64 *)0;
/* 中间页表项:RWX + WB 内存类型 */
table[indices[level]] = new_phys | EPT_RWX | (EPT_MEMORY_TYPE_WB << 3);
/* 切换到新分配的下一级页表 */
table = (u64 *)phys_to_virt(new_phys);
} else {
/* 清除标志位取出物理地址 */
u64 child_phys = entry & 0x000FFFFFFFFFF000ULL;
table = (u64 *)phys_to_virt(child_phys);
}
}
return &table[indices[0]];
}
int ept_map_range(u64 gpa, u64 hpa, u64 size, u64 flags) {
if (!g_ept_ready) return -1;
if ((gpa & (EPT_PAGE_SIZE - 1)) || (hpa & (EPT_PAGE_SIZE - 1)) ||
(size & (EPT_PAGE_SIZE - 1))) {
log_error("[EPT] unaligned map request");
return -2;
}
u64 pages = size / EPT_PAGE_SIZE;
for (u64 i = 0; i < pages; i++) {
u64 cur_gpa = gpa + i * EPT_PAGE_SIZE;
u64 cur_hpa = hpa + i * EPT_PAGE_SIZE;
u64 *entry = ept_walk(cur_gpa, 1);
if (!entry) {
log_error("[EPT] walk failed");
return -3;
}
*entry = cur_hpa | flags | (EPT_MEMORY_TYPE_WB << 3);
}
return 0;
}
int ept_identity_map(u64 gpa, u64 size, u64 flags) {
return ept_map_range(gpa, gpa, size, flags);
}
/* Walk the EPT to translate a GPA to its mapped HPA.
* Returns 0 if the GPA is not mapped (no present entry at any level).
* Handles 4KB pages and 2MB large pages (bit 7 = PS in PD entry). */
u64 ept_gpa_to_hpa(u64 gpa) {
if (!g_ept_ready) return 0;
u64 *table = (u64 *)g_ept_pml4_virt;
u64 pml4_idx = (gpa >> 39) & 0x1FF;
u64 pdpt_idx = (gpa >> 30) & 0x1FF;
u64 pd_idx = (gpa >> 21) & 0x1FF;
u64 pt_idx = (gpa >> 12) & 0x1FF;
u64 offset = gpa & 0xFFF;
/* Level 3: PML4 */
u64 entry = table[pml4_idx];
if (!(entry & EPT_READ)) return 0;
table = (u64 *)phys_to_virt(entry & 0x000FFFFFFFFFF000ULL);
/* Level 2: PDPT */
entry = table[pdpt_idx];
if (!(entry & EPT_READ)) return 0;
/* 1GB large page (bit 7 set in PDPT entry) */
if (entry & EPT_LARGE_PAGE) {
u64 hpa = entry & 0x000FFFFFC0000000ULL; /* 1GB page base */
return hpa + (gpa & 0x3FFFFFFF); /* offset within 1GB */
}
table = (u64 *)phys_to_virt(entry & 0x000FFFFFFFFFF000ULL);
/* Level 1: PD */
entry = table[pd_idx];
if (!(entry & EPT_READ)) return 0;
/* 2MB large page (bit 7 set in PD entry) */
if (entry & EPT_LARGE_PAGE) {
u64 hpa = entry & 0x000FFFFFFFE00000ULL; /* 2MB page base */
return hpa + (gpa & 0x1FFFFF); /* offset within 2MB */
}
table = (u64 *)phys_to_virt(entry & 0x000FFFFFFFFFF000ULL);
/* Level 0: PT */
entry = table[pt_idx];
if (!(entry & EPT_READ)) return 0;
u64 hpa = entry & 0x000FFFFFFFFFF000ULL;
return hpa + offset;
}
u64 ept_get_eptp(void) {
if (!g_ept_ready) return 0;
/* EPTP: bits [5:0] = memtype, bits [7:6] = walk length-1 (3 for 4-level),
* bits [51:12] = PML4 HPA */
return g_ept_pml4_phys | EPTP_WALK_LEN_4 | EPTP_MEMTYPE_WB;
}
+247
View File
@@ -0,0 +1,247 @@
/* hypercall.c — UTSM-side VMCALL hypercall handler.
*
* When the Linux guest executes a VMCALL instruction, it triggers a VM-Exit
* with exit reason EXIT_VCALL (18). The guest places:
* RAX = UTSM_HCALL_MAGIC | op (identifies this as a UTSM hypercall)
* RDI = arg0
* RSI = arg1
* RDX = arg2
*
* The VM-Exit assembly stub saves all guest GPRs into g_guest_regs before
* calling the C dispatcher. This handler reads arguments from g_guest_regs,
* dispatches to the operation handler, writes the return value into
* g_guest_regs.rax, and advances GUEST_RIP so the guest resumes after
* the VMCALL instruction.
*
* Protocol details in CODE/utsm-ipc/ipc_proto.h.
*/
#include <utsm/hypercall.h>
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/vmm.h>
#include <utsm/ipc_shm.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <ipc_proto.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
static u64 g_hypercall_count;
/* ===== Guest memory access helpers =====
*
* The Linux guest uses identity-mapped page tables (GVA == GPA), so
* pointers passed via hypercall arguments are GPAs. We translate GPA→HPA
* via EPT walk, then add HHDM offset to get the host virtual address. */
static void *gpa_to_hva(u64 gpa) {
u64 hpa = ept_gpa_to_hpa(gpa);
if (hpa == 0) return (void *)0;
if (!g_hhdm_request.response) return (void *)0;
u64 hhdm = g_hhdm_request.response->offset;
return (void *)(hhdm + hpa);
}
/* Write a u64 to guest memory at the given GPA.
* Returns 0 on success, -1 on failure. */
static int guest_write_u64(u64 gpa, u64 value) {
void *hva = gpa_to_hva(gpa);
if (!hva) return -1;
*(volatile u64 *)hva = value;
return 0;
}
/* Copy bytes from guest memory (GVA=GPA) to host buffer.
* Handles cross-page boundaries by translating each page separately.
* Returns 0 on success, -1 on failure. */
static int guest_read_buf(u64 gpa, void *host_dst, u64 len) {
u8 *dst = (u8 *)host_dst;
u64 copied = 0;
while (copied < len) {
void *hva = gpa_to_hva(gpa + copied);
if (!hva) return -1;
u64 page_end = (gpa + copied + 4096) & ~0xFFFULL;
u64 chunk = page_end - (gpa + copied);
if (chunk > len - copied) chunk = len - copied;
const u8 *src = (const u8 *)hva;
for (u64 i = 0; i < chunk; i++) {
dst[copied + i] = src[i];
}
copied += chunk;
}
return 0;
}
/* ===== Individual hypercall handlers ===== */
static long hcall_ping(u64 a0, u64 a1, u64 a2) {
(void)a0; (void)a1; (void)a2;
/* Return a recognizable PONG value */
return 0x504F4E47; /* "PONG" */
}
static long hcall_hello(u64 a0, u64 a1, u64 a2) {
(void)a0; (void)a1; (void)a2;
log_info("[HCALL] HELLO from Linux guest");
/* Linux is announcing itself; the IPC shared memory's linux_ready
* flag is set separately when Linux maps the shared memory. */
if (ipc_shm_is_ready()) {
/* Send a HELLO_ACK message via the shared memory ring */
const char *msg = "HELLO from UTSM";
ipc_shm_send(UTSM_MSG_HELLO_ACK, msg, 15);
}
return UTSM_HCALL_OK;
}
static long hcall_shm_info(u64 a0, u64 a1, u64 a2) {
/* a0 = GPA of gpa_out (u64), a1 = GPA of size_out (u64) */
(void)a2;
if (!ipc_shm_is_ready()) {
return UTSM_HCALL_NOMEM;
}
u64 shm_gpa = ipc_shm_get_gpa();
u64 shm_size = ipc_shm_get_size();
/* Write results to guest memory */
if (a0 != 0 && guest_write_u64(a0, shm_gpa) != 0) {
log_error("[HCALL] SHM_INFO: failed to write gpa to guest");
return UTSM_HCALL_INVAL;
}
if (a1 != 0 && guest_write_u64(a1, shm_size) != 0) {
log_error("[HCALL] SHM_INFO: failed to write size to guest");
return UTSM_HCALL_INVAL;
}
log_hex64("[HCALL] SHM_INFO: gpa=", shm_gpa);
log_hex64("[HCALL] SHM_INFO: size=", shm_size);
return UTSM_HCALL_OK;
}
static long hcall_cap_validate(u64 a0, u64 a1, u64 a2) {
/* Phase 1.3: stub — capability validation not yet implemented */
(void)a0; (void)a1; (void)a2;
log_info("[HCALL] CAP_VALIDATE (stub)");
return UTSM_HCALL_NOSYS;
}
static long hcall_utrw_read(u64 a0, u64 a1, u64 a2) {
/* Phase 1.3: stub — sealed memory read not yet implemented */
(void)a0; (void)a1; (void)a2;
log_info("[HCALL] UTRW_READ (stub)");
return UTSM_HCALL_NOSYS;
}
static long hcall_utrw_write(u64 a0, u64 a1, u64 a2) {
/* Phase 1.3: stub — sealed memory write not yet implemented */
(void)a0; (void)a1; (void)a2;
log_info("[HCALL] UTRW_WRITE (stub)");
return UTSM_HCALL_NOSYS;
}
static long hcall_drr_checkpoint(u64 a0, u64 a1, u64 a2) {
/* Phase 1.3: stub — DRR checkpoint trigger not yet implemented */
(void)a0; (void)a1; (void)a2;
log_info("[HCALL] DRR_CHECKPOINT (stub)");
return UTSM_HCALL_NOSYS;
}
static long hcall_console_write(u64 a0, u64 a1, u64 a2) {
/* a0 = GPA of string buffer, a1 = length */
(void)a2;
if (a0 == 0 || a1 == 0) {
return UTSM_HCALL_INVAL;
}
/* Cap length to prevent runaway reads */
if (a1 > 4096) a1 = 4096;
/* Read the string from guest memory */
char buf[4097];
if (guest_read_buf(a0, buf, a1) != 0) {
log_error("[HCALL] CONSOLE_WRITE: failed to read guest buffer");
return UTSM_HCALL_INVAL;
}
buf[a1] = '\0';
/* Output to UTSM serial console with a [LINUX] prefix */
serial_write("[LINUX] ");
serial_write(buf);
return (long)a1;
}
static long hcall_console_read(u64 a0, u64 a1, u64 a2) {
/* Phase 1.3: stub — console read not yet implemented */
(void)a0; (void)a1; (void)a2;
return UTSM_HCALL_NOSYS;
}
/* ===== Main hypercall dispatcher ===== */
int hypercall_handle(u64 guest_rax, u64 guest_rdi, u64 guest_rsi, u64 guest_rdx,
u64 guest_rip, u64 instr_len) {
g_hypercall_count++;
/* Validate magic: the high bits of RAX must be UTSM_HCALL_MAGIC */
if ((guest_rax & ~0xFFFFULL) != UTSM_HCALL_MAGIC) {
log_hex64("[HCALL] bad magic: ", guest_rax);
/* Return error in guest RAX */
g_guest_regs.rax = (u64)UTSM_HCALL_INVAL;
/* Still advance RIP to avoid infinite loop */
vmx_vmcs_write(VMCS_GUEST_RIP, guest_rip + instr_len);
return 1; /* resume guest */
}
/* Extract operation from low 16 bits */
u64 op = guest_rax & 0xFFFFULL;
long result = UTSM_HCALL_NOSYS;
switch (op) {
case UTSM_HCALL_PING:
result = hcall_ping(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_HELLO:
result = hcall_hello(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_SHM_INFO:
result = hcall_shm_info(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_CAP_VALIDATE:
result = hcall_cap_validate(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_UTRW_READ:
result = hcall_utrw_read(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_UTRW_WRITE:
result = hcall_utrw_write(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_DRR_CHECKPOINT:
result = hcall_drr_checkpoint(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_CONSOLE_WRITE:
result = hcall_console_write(guest_rdi, guest_rsi, guest_rdx);
break;
case UTSM_HCALL_CONSOLE_READ:
result = hcall_console_read(guest_rdi, guest_rsi, guest_rdx);
break;
default:
log_hex64("[HCALL] unknown op: ", op);
result = UTSM_HCALL_NOSYS;
break;
}
/* Write return value into guest RAX (restored by assembly before vmresume) */
g_guest_regs.rax = (u64)result;
/* Advance guest RIP past the VMCALL instruction */
vmx_vmcs_write(VMCS_GUEST_RIP, guest_rip + instr_len);
return 1; /* resume guest */
}
u64 hypercall_get_count(void) {
return g_hypercall_count;
}
+208
View File
@@ -0,0 +1,208 @@
/* ipc_shm.c — UTSM-side IPC shared memory manager.
*
* Allocates a 1MB contiguous physical region, initializes the shared
* utsm_ipc_shm structure (header + two ring buffers + payload pool),
* and EPT-maps it at LINUX_GUEST_IPC_SHM_GPA (0x04000000) so the Linux
* guest can access it after querying the GPA via UTSM_HCALL_SHM_INFO.
*
* Ring buffer discipline (single-producer, single-consumer):
* utsm_to_linux: UTSM produces via ipc_shm_send, Linux consumes
* linux_to_utsm: Linux produces, UTSM consumes via ipc_shm_recv
*
* Memory barriers ensure correct visibility across the two kernels
* (which share the same physical memory via EPT).
*/
#include <utsm/ipc_shm.h>
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include <utsm/linux_loader.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
/* ===== Internal state ===== */
static dkm_dma_buffer g_shm_buf; /* allocated DMA buffer for the SHM region */
static struct utsm_ipc_shm *g_shm; /* virtual address (HHDM) of the SHM region */
static int g_shm_ready;
static u32 g_send_seq; /* monotonic sequence for UTSM→Linux messages */
static u64 g_send_count;
static u64 g_recv_count;
/* ===== Initialization ===== */
int ipc_shm_init(void) {
if (g_shm_ready) {
log_warn("[IPC] shm already initialized");
return 0;
}
log_info("[IPC] shm init begin");
/* 1. Allocate 1MB contiguous physical memory for the shared region.
* Alignment: 4KB (page-aligned). Max phys: none (EPT maps any HPA). */
u64 page_count = UTSM_IPC_SHM_SIZE / 4096;
if (dma_alloc_pages(page_count, 4096, 0, &g_shm_buf) != 0) {
log_error("[IPC] failed to alloc shared memory (1MB)");
return -1;
}
log_hex64("[IPC] shm HPA=", g_shm_buf.phys);
log_hex64("[IPC] shm virt=", (u64)g_shm_buf.virt);
log_hex64("[IPC] shm size=", g_shm_buf.size);
/* 2. Zero the entire region */
u8 *p = (u8 *)g_shm_buf.virt;
for (u64 i = 0; i < g_shm_buf.size; i++) {
p[i] = 0;
}
/* 3. Initialize the shared memory structure */
g_shm = (struct utsm_ipc_shm *)g_shm_buf.virt;
/* Header */
g_shm->header.magic = UTSM_IPC_MAGIC;
g_shm->header.version = UTSM_IPC_VERSION;
g_shm->header.header_size = sizeof(struct utsm_ipc_shm_header);
g_shm->header.shm_phys = LINUX_GUEST_IPC_SHM_GPA; /* GPA as seen by Linux */
g_shm->header.shm_size = UTSM_IPC_SHM_SIZE;
g_shm->header.utsm_ready = 1; /* UTSM is ready now */
g_shm->header.linux_ready = 0; /* Linux will set this when it connects */
/* Ring buffers */
utsm_ipc_ring_init(&g_shm->utsm_to_linux);
utsm_ipc_ring_init(&g_shm->linux_to_utsm);
/* Memory barrier: ensure all writes are visible before EPT mapping */
utsm_ipc_mb();
/* 4. EPT-map the shared memory at the guest-visible GPA.
* Both kernels need read/write access. Execute is not needed. */
if (ept_map_range(LINUX_GUEST_IPC_SHM_GPA, g_shm_buf.phys,
UTSM_IPC_SHM_SIZE, EPT_READ | EPT_WRITE) != 0) {
log_error("[IPC] EPT map shared memory failed");
return -2;
}
log_hex64("[IPC] shm mapped GPA=", LINUX_GUEST_IPC_SHM_GPA);
g_shm_ready = 1;
g_send_seq = 0;
g_send_count = 0;
g_recv_count = 0;
log_info("[IPC] shm init ok");
return 0;
}
void ipc_shm_shutdown(void) {
if (!g_shm_ready) return;
g_shm->header.utsm_ready = 0;
utsm_ipc_mb();
g_shm_ready = 0;
log_info("[IPC] shm shutdown");
}
int ipc_shm_is_ready(void) {
return g_shm_ready;
}
u64 ipc_shm_get_hpa(void) {
return g_shm_ready ? g_shm_buf.phys : 0;
}
u64 ipc_shm_get_gpa(void) {
return LINUX_GUEST_IPC_SHM_GPA;
}
u64 ipc_shm_get_size(void) {
return UTSM_IPC_SHM_SIZE;
}
/* ===== Message API (UTSM-side) ===== */
int ipc_shm_send(u32 msg_type, const void *data, u32 data_len) {
if (!g_shm_ready || !g_shm) {
return -1;
}
/* Check if Linux is ready to receive */
if (!g_shm->header.linux_ready) {
log_warn("[IPC] send: Linux not ready, dropping message");
return -1;
}
/* Cap data length to inline buffer size */
if (data_len > UTSM_IPC_MSG_DATA_SIZE) {
data_len = UTSM_IPC_MSG_DATA_SIZE;
}
/* Build the message */
struct utsm_ipc_msg msg;
msg.type = msg_type;
msg.seq = g_send_seq++;
msg.data_len = data_len;
msg.flags = 0;
/* Copy data if present */
if (data && data_len > 0) {
const u8 *src = (const u8 *)data;
for (u32 i = 0; i < data_len; i++) {
msg.data[i] = src[i];
}
}
/* Enqueue into utsm_to_linux ring */
if (utsm_ipc_ring_push(&g_shm->utsm_to_linux, &msg) != 0) {
log_warn("[IPC] send: ring full, dropping message");
return -1;
}
g_send_count++;
return 0;
}
int ipc_shm_recv(struct utsm_ipc_msg *msg_out) {
if (!g_shm_ready || !g_shm || !msg_out) {
return -1;
}
if (utsm_ipc_ring_pop(&g_shm->linux_to_utsm, msg_out) != 0) {
return -1; /* empty */
}
g_recv_count++;
return 0;
}
int ipc_shm_has_pending(void) {
if (!g_shm_ready || !g_shm) return 0;
return !utsm_ipc_ring_empty(&g_shm->linux_to_utsm);
}
void ipc_shm_notify_linux(void) {
/* Phase 1.3: log only.
* Phase 1.4+: inject a guest IRQ (via VMCS VM-entry interruption info)
* to notify Linux that a message is waiting in the utsm_to_linux ring. */
log_info("[IPC] notify_linux (IPI injection not yet implemented)");
}
/* ===== Debug ===== */
void ipc_shm_dump_stats(void) {
if (!g_shm_ready) {
log_info("[IPC] not initialized");
return;
}
log_hex64("[IPC] utsm_ready=", g_shm->header.utsm_ready);
log_hex64("[IPC] linux_ready=", g_shm->header.linux_ready);
log_hex64("[IPC] utsm_to_linux head=", g_shm->utsm_to_linux.head);
log_hex64("[IPC] utsm_to_linux tail=", g_shm->utsm_to_linux.tail);
log_hex64("[IPC] linux_to_utsm head=", g_shm->linux_to_utsm.head);
log_hex64("[IPC] linux_to_utsm tail=", g_shm->linux_to_utsm.tail);
log_hex64("[IPC] send_count=", g_send_count);
log_hex64("[IPC] recv_count=", g_recv_count);
}
+358
View File
@@ -0,0 +1,358 @@
/* linux_boot.c — VMCS configuration and vmlaunch for Linux 6.6 guest.
*
* Configures the VMCS guest state for 64-bit Linux entry (startup_64):
* - RIP = kernel 64-bit entry point
* - RSI = boot_params GPA (Linux calling convention)
* - RSP = guest stack top
* - CR3 = guest page tables GPA (identity-mapped 4GB)
* - GDT = guest GDT GPA (64-bit code/data/TSS)
* - EFER.LME | EFER.LMA | EFER.NXE
*
* After vmlaunch, the VM-Exit handler (vmexit.c) processes exits and
* resumes the guest.
*/
#include <utsm/linux_loader.h>
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/vmm.h>
#include <utsm/virtio_mmio.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
/* VM-Exit assembly entry and C dispatcher (from vmm.c / vmexit_asm.S) */
extern void vmx_vm_exit_handler(void);
extern int vmexit_dispatch(void);
extern u64 vmexit_get_count(void);
/* 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 通过 <utsm/vmm.h> 声明(vmm.c 定义)。 */
extern u8 g_host_stack[8192];
/* CR helpers */
static inline u64 read_cr0_local(void) {
u64 v;
__asm__ volatile("mov %%cr0, %0" : "=r"(v));
return v;
}
static inline u64 read_cr3_local(void) {
u64 v;
__asm__ volatile("mov %%cr3, %0" : "=r"(v));
return v;
}
static inline u64 read_cr4_local(void) {
u64 v;
__asm__ volatile("mov %%cr4, %0" : "=r"(v));
return v;
}
static u64 vmx_adjust_control(u64 value, u32 msr) {
u32 allowed0 = (u32)vmx_read_msr(msr);
u32 allowed1 = (u32)(vmx_read_msr(msr) >> 32);
u32 adjusted = (u32)value | allowed0;
adjusted &= allowed1;
return adjusted;
}
/* MSR bitmap (4KB, all zeros = don't intercept any MSR) */
static u8 g_linux_msr_bitmap[4096] __attribute__((aligned(4096)));
/* ===== VMCS host state (same as vmm.c, but for Linux guest) ===== */
static void linux_vmcs_setup_host_state(void) {
vmx_vmcs_write(VMCS_HOST_CR0, read_cr0_local() | CR0_PE | CR0_NE | CR0_PG);
vmx_vmcs_write(VMCS_HOST_CR3, read_cr3_local());
vmx_vmcs_write(VMCS_HOST_CR4, read_cr4_local() | CR4_VMXE);
/* Host segment selectors: UTSM GDT 0x08 (code) / 0x10 (data) */
vmx_vmcs_write(VMCS_HOST_CS_SELECTOR, 0x08);
vmx_vmcs_write(VMCS_HOST_SS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_DS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_ES_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_FS_SELECTOR, 0);
vmx_vmcs_write(VMCS_HOST_GS_SELECTOR, 0);
vmx_vmcs_write(VMCS_HOST_FS_BASE, 0);
vmx_vmcs_write(VMCS_HOST_GS_BASE, 0);
/* Host GDT/IDT + TR */
{
struct __attribute__((packed)) { u16 limit; u64 base; } gdtr, idtr;
__asm__ volatile("sgdt %0" : "=m"(gdtr));
__asm__ volatile("sidt %0" : "=m"(idtr));
vmx_vmcs_write(VMCS_HOST_GDTR_BASE, gdtr.base);
vmx_vmcs_write(VMCS_HOST_IDTR_BASE, idtr.base);
u16 tr_sel;
u64 tr_base;
__asm__ volatile("str %0" : "=r"(tr_sel));
u64 gdt_base = gdtr.base;
u64 *tss_desc = (u64 *)(gdt_base + (tr_sel & 0xFFF8));
u64 low = tss_desc[0];
u64 high = tss_desc[1];
tr_base = (low >> 16) & 0xFFFFFF;
tr_base |= ((low >> 32) & 0xFF) << 24;
tr_base |= (high & 0xFFFFFFFF) << 32;
vmx_vmcs_write(VMCS_HOST_TR_SELECTOR, tr_sel);
vmx_vmcs_write(VMCS_HOST_TR_BASE, tr_base);
}
/* Host SYSENTER */
{
u64 sysenter_cs;
__asm__ volatile("rdmsr" : "=a"(sysenter_cs) : "c"(0x174));
vmx_vmcs_write(VMCS_HOST_SYSENTER_CS, sysenter_cs & 0xFFFFFFFF);
vmx_vmcs_write(VMCS_HOST_SYSENTER_ESP, 0);
vmx_vmcs_write(VMCS_HOST_SYSENTER_EIP, 0);
}
/* Host EFER */
{
u32 low, high;
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0xC0000080));
u64 host_efer = ((u64)high << 32) | low;
vmx_vmcs_write(VMCS_HOST_IA32_EFER, host_efer);
}
/* Host RSP/RIP: VM-Exit handler */
vmx_vmcs_write(VMCS_HOST_RSP, (u64)&g_host_stack[sizeof(g_host_stack) - 16]);
vmx_vmcs_write(VMCS_HOST_RIP, (u64)vmx_vm_exit_handler);
}
/* ===== VMCS guest state for Linux ===== */
static void linux_vmcs_setup_guest_state(const struct linux_guest_info *gi) {
/* CR0/CR3/CR4: long mode with paging */
vmx_vmcs_write(VMCS_GUEST_CR0, CR0_PE | CR0_NE | CR0_PG | CR0_WP);
/* CR3 points to guest PML4 (GPA) */
vmx_vmcs_write(VMCS_GUEST_CR3, gi->pgt_gpa);
vmx_vmcs_write(VMCS_GUEST_CR4, CR4_PAE | CR4_PGE | CR4_PSE);
/* Segment selectors from guest GDT */
vmx_vmcs_write(VMCS_GUEST_CS_SELECTOR, 0x08);
vmx_vmcs_write(VMCS_GUEST_SS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_DS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_ES_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_FS_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_GS_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_LDTR_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_TR_SELECTOR, 0x18);
/* Segment bases: flat model, TR points to TSS in GDT page */
vmx_vmcs_write(VMCS_GUEST_CS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_SS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_DS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_ES_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_FS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_GS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_LDTR_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_TR_BASE, gi->gdt_gpa + 0x100);
vmx_vmcs_write(VMCS_GUEST_GDTR_BASE, gi->gdt_gpa);
vmx_vmcs_write(VMCS_GUEST_IDTR_BASE, 0);
/* Segment limits */
vmx_vmcs_write(VMCS_GUEST_CS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_SS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_DS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_ES_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_FS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_GS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_LDTR_LIMIT, 0xFFFF);
vmx_vmcs_write(VMCS_GUEST_TR_LIMIT, 0x67);
vmx_vmcs_write(VMCS_GUEST_GDTR_LIMIT, 0x28);
vmx_vmcs_write(VMCS_GUEST_IDTR_LIMIT, 0xFFFF);
/* Access rights */
vmx_vmcs_write(VMCS_GUEST_CS_ACCESS, 0xA09B); /* 64-bit code, L=1 */
vmx_vmcs_write(VMCS_GUEST_SS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_DS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_ES_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_FS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_GS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_LDTR_ACCESS, 0x82); /* unusable */
vmx_vmcs_write(VMCS_GUEST_TR_ACCESS, 0x8B); /* busy 64-bit TSS */
/* Guest RIP = Linux 64-bit entry (startup_64) */
vmx_vmcs_write(VMCS_GUEST_RIP, gi->kernel_entry);
/* Guest RSP = top of stack page */
vmx_vmcs_write(VMCS_GUEST_RSP, LINUX_GUEST_STACK_GPA + 0x1000);
/* NOTE: RSI (boot_params address) is NOT a VMCS field.
* VMX does not save/restore GPRs (RAX-R15) in VMCS.
* RSI is set via the inline asm launch stub in linux_launch(). */
vmx_vmcs_write(VMCS_GUEST_RFLAGS, 0x2); /* interrupts disabled */
/* Guest EFER: long mode active */
vmx_vmcs_write(VMCS_GUEST_IA32_EFER, EFER_LME | EFER_LMA | EFER_NXE);
/* Guest SYSENTER (unused but required by VMX) */
vmx_vmcs_write(VMCS_GUEST_SYSENTER_CS, 0);
vmx_vmcs_write(VMCS_GUEST_SYSENTER_ESP, 0);
vmx_vmcs_write(VMCS_GUEST_SYSENTER_EIP, 0);
/* Guest DR7, activity state, interruptibility */
vmx_vmcs_write(VMCS_GUEST_DR7, 0x400);
vmx_vmcs_write(VMCS_GUEST_ACTIVITY_STATE, 0);
vmx_vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, 0);
}
/* ===== VMCS controls for Linux ===== */
static void linux_vmcs_setup_controls(u64 eptp) {
/* Pin-based:外部中断 exit + NMI exit + VMX preemption timer(若支持)。
* preemption timer 提供 1ms 周期 exit,用于轮询 host 串口 RX、
* 推进 guest PIT tick、注入 pending virtio/COM1 IRQ。 */
u64 pin_want = PIN_EXT_INTERRUPT_EXITING | PIN_NMI_EXITING;
if (vmx_preemption_timer_supported()) {
pin_want |= PIN_VMX_PREEMPTION_TIMER;
}
u64 pin = vmx_adjust_control(pin_want, IA32_VMX_TRUE_PINBASED_CTLS);
/* CPU-based: exit on HLT, INVLPG, MWAIT, IO, use MSR bitmap,
* activate secondary controls. Don't intercept RDTSC (Linux needs it). */
u64 cpu = CPU_BASED_HLT_EXITING
| CPU_BASED_ACTIVATE_SECONDARY
| CPU_BASED_USE_MSR_BITMAPS
| CPU_BASED_UNCOND_IO_EXITING
| CPU_BASED_INVLPG_EXITING;
cpu = vmx_adjust_control(cpu, IA32_VMX_TRUE_PROCBASED_CTLS);
/* Secondary: EPT + VPID */
u64 cpu2 = SEC_EXEC_ENABLE_EPT | SEC_EXEC_ENABLE_VPID;
{
u64 msr = vmx_read_msr(IA32_VMX_PROCBASED_CTLS2);
u32 allowed1 = (u32)(msr >> 32);
cpu2 &= allowed1;
cpu2 |= SEC_EXEC_ENABLE_EPT;
}
u64 exit_ctrl = VM_EXIT_SAVE_DEBUG_CONTROLS
| VM_EXIT_HOST_ADDR_SPACE_SIZE
| VM_EXIT_LOAD_HOST_EFER;
exit_ctrl = vmx_adjust_control(exit_ctrl, IA32_VMX_TRUE_EXIT_CTLS);
u64 entry_ctrl = VM_ENTRY_LOAD_DEBUG_CONTROLS
| VM_ENTRY_IA32E_MODE_GUEST
| VM_ENTRY_LOAD_GUEST_EFER;
entry_ctrl = vmx_adjust_control(entry_ctrl, IA32_VMX_TRUE_ENTRY_CTLS);
vmx_vmcs_write(VMCS_PIN_BASED_VM_EXEC_CONTROL, pin);
vmx_vmcs_write(VMCS_CPU_BASED_VM_EXEC_CONTROL, cpu);
vmx_vmcs_write(VMCS_SECONDARY_VM_EXEC_CONTROL, cpu2);
vmx_vmcs_write(VMCS_VM_EXIT_CONTROLS, exit_ctrl);
vmx_vmcs_write(VMCS_VM_ENTRY_CONTROLS, entry_ctrl);
/* MSR bitmap */
{
u64 hhdm = g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
u64 msr_bitmap_phys = (u64)g_linux_msr_bitmap - hhdm;
vmx_vmcs_write(VMCS_MSR_BITMAP, msr_bitmap_phys);
}
vmx_vmcs_write(VMCS_EPT_POINTER, eptp);
vmx_vmcs_write(VMCS_VPID, 2); /* VPID 1 = self-test, VPID 2 = Linux */
/* Exception bitmap: catch #GP, #PF, #UD, #DF (triple fault handled separately) */
vmx_vmcs_write(VMCS_EXCEPTION_BITMAP, (1ULL << 13) | (1ULL << 14) | (1ULL << 6) | (1ULL << 8));
}
/* ===== Global for passing RSI to launch asm ===== */
static volatile u64 g_linux_rsi;
/* ===== Linux launch =====
*
* This function is called after vmm_init() + linux_loader_init().
* It configures the VMCS for the Linux guest and performs vmlaunch.
*
* NOTE: VMX general registers (RAX-R15) are NOT saved in VMCS.
* We use inline assembly to set RSI = boot_params GPA before vmlaunch.
*
* Returns 0 on successful guest termination, negative on error.
*/
int linux_launch(void) {
const struct linux_guest_info *gi = linux_get_guest_info();
if (!gi || !gi->loaded) {
log_error("[LINUX] guest not loaded");
return -1;
}
if (!vmm_is_ready()) {
log_error("[LINUX] VMM not ready");
return -2;
}
log_info("[LINUX] launch begin");
/* 注册 virtio-mmio 后端(block + net),guest 经 cmdline
* virtio_mmio.device= 发现。幂等:重复调用直接返回。 */
virtio_mmio_init();
/* 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);
linux_vmcs_setup_controls(vmm_get_eptp());
linux_vmcs_setup_host_state();
/* 初始 arm preemption timer(1ms 后首次周期 exit) */
vmx_vmcs_write(VMCS_GUEST_PREEMPTION_TIMER, vmx_preemption_quantum_1ms());
/* Set RSI for Linux boot_params (passed via global, loaded in asm) */
g_linux_rsi = gi->bootparams_gpa;
log_hex64("[LINUX] RIP=", gi->kernel_entry);
log_hex64("[LINUX] RSP=", LINUX_GUEST_STACK_GPA + 0x1000);
log_hex64("[LINUX] RSI=", gi->bootparams_gpa);
log_hex64("[LINUX] CR3=", gi->pgt_gpa);
log_hex64("[LINUX] GDTR=", gi->gdt_gpa);
log_info("[LINUX] vmlaunch");
/* vmlaunch with RSI = boot_params.
* Same control flow pattern as vmm_self_test:
* - On failure: failed=1, falls through
* - On success: enters guest, VM-Exit → vmx_vm_exit_handler
* - On terminate: handler restores RSP, jmps to post_guest label */
int failed;
__asm__ volatile(
"movq %%rsp, g_saved_host_rsp(%%rip)\n\t"
"leaq 1f(%%rip), %%rax\n\t"
"movq %%rax, g_saved_return_rip(%%rip)\n\t"
"movq g_linux_rsi(%%rip), %%rsi\n\t" /* RSI = boot_params GPA */
"vmlaunch\n\t"
/* ---- failure path ---- */
"movl $1, %0\n\t"
"jmp 2f\n\t"
/* ---- post_guest: terminate path jmps here ---- */
"1:\n\t"
"movl $0, %0\n\t"
"2:\n\t"
: "=r"(failed)
:: "rax", "rcx", "rdx", "rsi", "rdi",
"r8", "r9", "r10", "r11", "memory"
);
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 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());
return 0;
}
+198
View File
@@ -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 <utsm/linux_compat.h>
#include <utsm/linux_resume.h>
#include <utsm/ipc_shm.h>
#include <utsm/vmm.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <ipc_proto.h>
/* ===== 内部辅助 ===== */
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;
}
+677
View File
@@ -0,0 +1,677 @@
/* linux_loader.c — Linux 6.6 bzImage loader for UTSM VMX non-root guest.
*
* Responsibilities:
* 1. Find the bzImage among Limine boot modules
* 2. Parse the bzImage setup_header (Linux boot protocol)
* 3. Allocate host physical memory for guest regions
* 4. Load the protected-mode kernel into guest memory
* 5. Set up boot_params (e820, cmdline, initrd pointers)
* 6. Build guest page tables (identity-mapped 4GB, 2MB pages)
* 7. Build guest GDT (64-bit code/data/TSS)
* 8. EPT-map all guest regions (GPA → HPA)
*
* The actual VMCS configuration and vmlaunch happen in linux_boot.c.
*/
#include <utsm/linux_loader.h>
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_module_request g_module_request;
extern volatile struct limine_hhdm_request g_hhdm_request;
extern volatile struct limine_memmap_request g_memmap_request;
/* ===== Runtime helpers ===== */
static void mem_copy(void *dst, const void *src, u64 n) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u64 i = 0; i < n; i++) d[i] = s[i];
}
static void mem_zero(void *dst, u64 n) {
u8 *d = (u8 *)dst;
for (u64 i = 0; i < n; i++) d[i] = 0;
}
static u64 str_len(const char *s) {
u64 n = 0;
while (s[n]) n++;
return n;
}
/* ===== Guest state ===== */
static struct linux_guest_info g_guest;
static const char *g_default_cmdline = LINUX_DEFAULT_CMDLINE;
/* ===== Guest page table helpers =====
*
* Build identity-mapped 4-level page tables for the guest.
* Maps the first 4GB of GPA space using 2MB large pages.
*
* Layout (all at known GPAs for guest CR3):
* PML4 at g_guest.pgt_gpa + 0x0000 (1 page)
* PDPT at g_guest.pgt_gpa + 0x1000 (1 page)
* PD[0] at g_guest.pgt_gpa + 0x2000 (1 page, covers 0-1GB)
* PD[1] at g_guest.pgt_gpa + 0x3000 (1 page, covers 1-2GB)
* PD[2] at g_guest.pgt_gpa + 0x4000 (1 page, covers 2-3GB)
* PD[3] at g_guest.pgt_gpa + 0x5000 (1 page, covers 3-4GB)
*/
#define PGT_PML4_OFF 0x0000
#define PGT_PDPT_OFF 0x1000
#define PGT_PD_BASE 0x2000
#define PGT_PD_COUNT 4 /* 4 PD pages = 4GB */
#define PGT_TOTAL_PAGES (2 + PGT_PD_COUNT)
#define PGT_TOTAL_SIZE (PGT_TOTAL_PAGES * 4096)
/* x86-64 page table entry bits */
#define PTE_P (1ULL << 0) /* Present */
#define PTE_RW (1ULL << 1) /* Read/Write */
#define PTE_US (1ULL << 2) /* User/Supervisor */
#define PTE_PS (1ULL << 7) /* Page Size (large page) */
#define PTE_G (1ULL << 8) /* Global */
#define PTE_NX (1ULL << 63) /* No Execute */
static int build_guest_page_tables(u64 pgt_hpa) {
u64 *pml4 = (u64 *)(pgt_hpa + (g_hhdm_request.response ? g_hhdm_request.response->offset : 0));
u64 *pdpt = (u64 *)((u8 *)pml4 + 4096);
/* PML4[0] → PDPT */
u64 pdpt_gpa = g_guest.pgt_gpa + PGT_PDPT_OFF;
pml4[0] = pdpt_gpa | PTE_P | PTE_RW;
/* PDPT[0..3] → PD pages (each covers 1GB) */
for (int i = 0; i < PGT_PD_COUNT; i++) {
u64 pd_gpa = g_guest.pgt_gpa + PGT_PD_BASE + (u64)i * 4096;
pdpt[i] = pd_gpa | PTE_P | PTE_RW;
}
/* Each PD has 512 entries, each mapping a 2MB page (PS=1).
* PD[i] entry j maps GPA = i*1GB + j*2MB. */
for (int pd_idx = 0; pd_idx < PGT_PD_COUNT; pd_idx++) {
u64 *pd = (u64 *)((u8 *)pml4 + PGT_PD_BASE + (u64)pd_idx * 4096);
for (int j = 0; j < 512; j++) {
u64 page_gpa = (u64)pd_idx * (1ULL << 30) + (u64)j * (2ULL << 20);
pd[j] = page_gpa | PTE_P | PTE_RW | PTE_PS;
}
}
return 0;
}
/* ===== Guest GDT builder =====
*
* GDT layout (at g_guest.gdt_gpa):
* [0x00] null
* [0x08] 64-bit code: base=0, limit=0xFFFFF, L=1, G=1, present, DPL0, type=0x9B
* [0x10] 64-bit data: base=0, limit=0xFFFFF, G=1, present, DPL0, type=0x93
* [0x18] 64-bit TSS descriptor (16 bytes)
*
* TSS itself is placed in the same 4KB page after the GDT.
*/
typedef struct __attribute__((packed)) {
u16 limit_low;
u16 base_low;
u8 base_mid;
u8 access;
u8 flags_limit_high;
u8 base_high;
} gdt_entry64;
typedef struct __attribute__((packed)) {
u32 limit_low_base_low;
u8 base_mid;
u8 access;
u8 flags_limit_high;
u8 base_high;
u32 base_upper;
u32 reserved;
} tss_desc64;
static int build_guest_gdt(u64 gdt_hpa) {
u8 *gdt_page = (u8 *)(gdt_hpa + (g_hhdm_request.response ? g_hhdm_request.response->offset : 0));
mem_zero(gdt_page, 4096);
gdt_entry64 *gdt = (gdt_entry64 *)gdt_page;
/* [0x08] 64-bit code segment */
gdt[1].limit_low = 0xFFFF;
gdt[1].base_low = 0;
gdt[1].base_mid = 0;
gdt[1].access = 0x9B; /* present, DPL0, code, exec/read */
gdt[1].flags_limit_high = 0xAF; /* G=1, L=1 (64-bit), limit[19:16]=0xF */
gdt[1].base_high = 0;
/* [0x10] 64-bit data segment */
gdt[2].limit_low = 0xFFFF;
gdt[2].base_low = 0;
gdt[2].base_mid = 0;
gdt[2].access = 0x93; /* present, DPL0, data, read/write */
gdt[2].flags_limit_high = 0xCF; /* G=1, DB=1, limit[19:16]=0xF */
gdt[2].base_high = 0;
/* [0x18] 64-bit TSS descriptor (16 bytes).
* TSS structure lives at offset 0x100 in the same page (GPA = gdt_gpa + 0x100). */
tss_desc64 *tss_d = (tss_desc64 *)&gdt_page[0x18];
u64 tss_base = g_guest.gdt_gpa + 0x100;
u32 tss_limit = 0x67; /* 104-byte minimum 64-bit TSS */
tss_d->limit_low_base_low = (tss_limit & 0xFFFF) | ((u32)(tss_base & 0xFFFFFF) << 16);
tss_d->base_mid = (u8)((tss_base >> 24) & 0xFF);
tss_d->access = 0x89; /* present, DPL0, 64-bit TSS (available) */
tss_d->flags_limit_high = (u8)((tss_limit >> 16) & 0x0F);
tss_d->base_high = 0;
tss_d->base_upper = 0;
tss_d->reserved = 0;
return 0;
}
/* ===== e820 setup ===== */
struct __attribute__((packed)) e820_entry {
u64 addr;
u64 size;
u32 type;
};
static int setup_e820(void *bootparams_virt) {
u8 *bp = (u8 *)bootparams_virt;
/* e820_entries count at offset 0x1e8 */
u8 *e820_count = &bp[E820_ENTRIES_OFFSET];
struct e820_entry *table = (struct e820_entry *)&bp[E820_TABLE_OFFSET];
int n = 0;
/* Entry 0: 0x00000000-0x0009FFFF: usable RAM (640KB) */
table[n].addr = 0x00000000;
table[n].size = 0x000A0000;
table[n].type = E820_TYPE_RAM;
n++;
/* Entry 1: 0x000A0000-0x000FFFFF: reserved (VGA + BIOS) */
table[n].addr = 0x000A0000;
table[n].size = 0x00060000;
table[n].type = E820_TYPE_RESERVED;
n++;
/* Entry 2: 0x00100000-0x003FFFFF: reserved (UTSM area) */
table[n].addr = 0x00100000;
table[n].size = 0x00300000;
table[n].type = E820_TYPE_RESERVED;
n++;
/* Entry 3: 0x00400000-0x03FFFFFF: usable RAM (kernel + initrd + low RAM) */
table[n].addr = 0x00400000;
table[n].size = 0x03C00000; /* 60MB */
table[n].type = E820_TYPE_RAM;
n++;
/* Entry 4: 0x04000000-0x040FFFFF: reserved (IPC shared memory, 1MB) */
table[n].addr = 0x04000000;
table[n].size = 0x00100000; /* 1MB */
table[n].type = E820_TYPE_RESERVED;
n++;
/* Entry 5: 0x04100000-0x0FFFFFFF: usable RAM (general RAM, ~188MB) */
table[n].addr = 0x04100000;
table[n].size = 0x0BF00000; /* 191MB */
table[n].type = E820_TYPE_RAM;
n++;
*e820_count = (u8)n;
return n;
}
/* ===== bzImage module finder ===== */
void *linux_find_bzimage_module(u64 *size_out) {
if (!g_module_request.response) {
log_error("[LINUX] no Limine module response");
return (void *)0;
}
struct limine_module_response *resp = g_module_request.response;
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file *file = resp->modules[i];
if (!file || !file->path) continue;
/* Look for paths containing "linux" or "bzImage" */
const char *p = file->path;
int match = 0;
/* Simple case-insensitive substring search for "linux" */
for (const char *s = p; *s; s++) {
if ((s[0] == 'l' || s[0] == 'L') &&
(s[1] == 'i' || s[1] == 'I') &&
(s[2] == 'n' || s[2] == 'N') &&
(s[3] == 'u' || s[3] == 'U') &&
(s[4] == 'x' || s[4] == 'X')) {
match = 1;
break;
}
}
if (!match) continue;
if (size_out) *size_out = file->size;
log_info("[LINUX] found bzImage module:");
log_info(file->path);
log_hex64("[LINUX] module size=", file->size);
return file->address;
}
log_warn("[LINUX] no bzImage module found");
return (void *)0;
}
/* ===== initrd module finder =====
*
* 在 Limine boot module 中查找 initramfs(路径含 "initrd" 或 "initramfs")。
* 返回模块数据指针,*size_out 为大小;未找到返回 NULL。 */
static void *linux_find_initrd_module(u64 *size_out) {
if (!g_module_request.response) return (void *)0;
struct limine_module_response *resp = g_module_request.response;
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file *file = resp->modules[i];
if (!file || !file->path) continue;
const char *p = file->path;
int match = 0;
for (const char *s = p; *s; s++) {
/* 匹配 "initrd"(如 linux-initrd.img) */
if ((s[0] == 'i' || s[0] == 'I') &&
(s[1] == 'n' || s[1] == 'N') &&
(s[2] == 'i' || s[2] == 'I') &&
(s[3] == 't' || s[3] == 'T') &&
(s[4] == 'r' || s[4] == 'R') &&
(s[5] == 'd' || s[5] == 'D')) {
match = 1;
break;
}
}
if (!match) continue;
if (size_out) *size_out = file->size;
log_info("[LINUX] found initrd module:");
log_info(file->path);
log_hex64("[LINUX] initrd size=", file->size);
return file->address;
}
log_warn("[LINUX] no initrd module found");
return (void *)0;
}
/* ===== bzImage parser ===== */
int linux_parse_bzimage(const void *bzimage, u64 size,
u64 *payload_offset_out, u64 *payload_length_out,
u64 *init_size_out, u64 *entry_offset_out) {
const u8 *img = (const u8 *)bzimage;
if (size < 0x300) {
log_error("[LINUX] bzImage too small");
return -1;
}
/* Check boot_flag at 0x1fe */
u16 boot_flag = *(u16 *)&img[BZ_BOOT_FLAG_OFF];
if (boot_flag != 0xAA55) {
log_hex64("[LINUX] bad boot_flag=", boot_flag);
return -2;
}
/* Check header magic "HdrS" at 0x202 */
u32 magic = *(u32 *)&img[BZ_HEADER_OFF];
if (magic != BZ_HEADER_MAGIC) {
log_hex64("[LINUX] bad header magic=", magic);
return -3;
}
/* Check protocol version */
u16 version = *(u16 *)&img[BZ_VERSION_OFF];
log_hex64("[LINUX] boot protocol version=", version);
if (version < 0x020c) {
log_error("[LINUX] boot protocol too old");
return -4;
}
/* Check xloadflags for 64-bit kernel */
u16 xloadflags = *(u16 *)&img[BZ_XLOADFLAGS_OFF];
log_hex64("[LINUX] xloadflags=", xloadflags);
if (!(xloadflags & XLF_KERNEL_64)) {
log_error("[LINUX] not a 64-bit kernel");
return -5;
}
/* Payload offset and length */
u32 payload_offset = *(u32 *)&img[BZ_PAYLOAD_OFFSET_OFF];
u32 payload_length = *(u32 *)&img[BZ_PAYLOAD_LENGTH_OFF];
log_hex64("[LINUX] payload_offset=", payload_offset);
log_hex64("[LINUX] payload_length=", payload_length);
if (payload_offset + payload_length > size) {
log_error("[LINUX] payload exceeds image size");
return -6;
}
/* Init size (total memory needed for kernel init) */
u32 init_size = *(u32 *)&img[BZ_INIT_SIZE_ACTUAL_OFF];
if (init_size == 0) init_size = payload_length;
log_hex64("[LINUX] init_size=", init_size);
/* The 64-bit entry point is at offset 0x200 from the payload start.
* This is the "startup_64" symbol. */
u64 entry_offset = payload_offset + 0x200;
log_hex64("[LINUX] entry_offset=", entry_offset);
/* Setup sectors (for real-mode header size) */
u8 setup_sects = img[0x1f1];
if (setup_sects == 0) setup_sects = 4;
u64 setup_size = (setup_sects + 1) * 512;
log_hex64("[LINUX] setup_sects=", setup_sects);
log_hex64("[LINUX] setup_size=", setup_size);
if (payload_offset_out) *payload_offset_out = payload_offset;
if (payload_length_out) *payload_length_out = payload_length;
if (init_size_out) *init_size_out = init_size;
if (entry_offset_out) *entry_offset_out = entry_offset;
return 0;
}
/* ===== Boot params setup ===== */
static int setup_boot_params(void *bp_virt, u64 bp_gpa,
u64 kernel_entry_gpa,
u64 cmdline_gpa, u64 cmdline_len,
u64 initrd_gpa, u64 initrd_size,
const void *bzimage) {
(void)bp_gpa; /* GPA is used by EPT mapping, not needed here */
const u8 *img = (const u8 *)bzimage;
u8 *bp = (u8 *)bp_virt;
/* Zero entire boot_params */
mem_zero(bp, BOOT_PARAMS_SIZE);
/* Copy setup_header from bzImage (offset 0x1f1, length 0x1ef = 0x200 - 0x11) */
/* The setup_header in boot_params starts at 0x1f1 and goes to 0x2xx.
* We copy from bzImage offset 0x1f1 to boot_params offset 0x1f1. */
u64 hdr_copy_len = 0x210; /* copy a generous chunk */
if (hdr_copy_len > BOOT_PARAMS_SIZE - SETUP_HEADER_OFFSET) {
hdr_copy_len = BOOT_PARAMS_SIZE - SETUP_HEADER_OFFSET;
}
mem_copy(&bp[SETUP_HEADER_OFFSET], &img[SETUP_HEADER_OFFSET], hdr_copy_len);
/* Override key fields */
bp[BZ_TYPE_OF_LOADER_OFF] = 0xFF; /* custom bootloader */
/* loadflags: LOADED_HIGH | CAN_USE_HEAP */
u8 loadflags = bp[BZ_LOADFLAGS_OFF];
loadflags |= LOADFLAG_LOADED_HIGH | LOADFLAG_CAN_USE_HEAP;
bp[BZ_LOADFLAGS_OFF] = loadflags;
/* Command line pointer and size */
*(u32 *)&bp[BZ_CMD_LINE_PTR_OFF] = (u32)cmdline_gpa;
*(u32 *)&bp[BZ_CMDLINE_SIZE_OFF] = (u32)cmdline_len;
/* Initrd */
if (initrd_size > 0) {
*(u32 *)&bp[BZ_RAMDISK_IMAGE_OFF] = (u32)initrd_gpa;
*(u32 *)&bp[BZ_RAMDISK_SIZE_OFF] = (u32)initrd_size;
}
/* code32_start: set to kernel entry (informational) */
*(u32 *)&bp[BZ_CODE32_START_OFF] = (u32)kernel_entry_gpa;
/* Heap end pointer (offset 0x224): end of real-mode heap.
* Not critical for 64-bit boot, but set a reasonable value. */
*(u16 *)&bp[0x224] = 0xFE00; /* heap ends at 0xFE00 */
/* Set up e820 memory map */
int e820_count = setup_e820(bp_virt);
log_hex64("[LINUX] e820 entries=", (u64)e820_count);
return 0;
}
/* ===== Main loader entry ===== */
int linux_loader_init(void) {
log_info("[LINUX] loader init begin");
/* 1. Find bzImage module */
u64 bzimage_size = 0;
void *bzimage = linux_find_bzimage_module(&bzimage_size);
if (!bzimage) {
log_warn("[LINUX] no bzImage found, skipping Linux guest");
return -1;
}
/* 2. Parse bzImage header */
u64 payload_offset, payload_length, init_size, entry_offset;
if (linux_parse_bzimage(bzimage, bzimage_size,
&payload_offset, &payload_length,
&init_size, &entry_offset) != 0) {
log_error("[LINUX] bzImage parse failed");
return -2;
}
/* 3. Allocate host physical memory for guest regions */
/* Kernel: round up init_size to page boundary, align to 2MB */
u64 kernel_alloc_size = ((init_size + 0xFFFFF) & ~0xFFFFFULL);
if (kernel_alloc_size < payload_length) kernel_alloc_size = payload_length;
dkm_dma_buffer kernel_buf;
if (dma_alloc_pages(kernel_alloc_size / 4096, 2 * 1024 * 1024, 0, &kernel_buf) != 0) {
log_error("[LINUX] failed to alloc kernel memory");
return -3;
}
log_hex64("[LINUX] kernel HPA=", kernel_buf.phys);
log_hex64("[LINUX] kernel alloc size=", kernel_alloc_size);
/* Boot params: 1 page */
dkm_dma_buffer bp_buf;
if (dma_alloc_pages(1, 4096, 0, &bp_buf) != 0) {
log_error("[LINUX] failed to alloc boot_params");
return -4;
}
/* Page tables: PGT_TOTAL_PAGES pages */
dkm_dma_buffer pgt_buf;
if (dma_alloc_pages(PGT_TOTAL_PAGES, 4096, 0, &pgt_buf) != 0) {
log_error("[LINUX] failed to alloc page tables");
return -5;
}
/* GDT: 1 page */
dkm_dma_buffer gdt_buf;
if (dma_alloc_pages(1, 4096, 0, &gdt_buf) != 0) {
log_error("[LINUX] failed to alloc GDT");
return -6;
}
/* Stack: 1 page (stack grows down from top) */
dkm_dma_buffer stack_buf;
if (dma_alloc_pages(1, 4096, 0, &stack_buf) != 0) {
log_error("[LINUX] failed to alloc stack");
return -7;
}
/* Command line: 1 page */
dkm_dma_buffer cmdline_buf;
if (dma_alloc_pages(1, 4096, 0, &cmdline_buf) != 0) {
log_error("[LINUX] failed to alloc cmdline");
return -8;
}
/* General RAM: 64MB for Linux, mapped at GPA 0x05000000 (after IPC region) */
u64 ram_size = 64 * 1024 * 1024;
dkm_dma_buffer ram_buf;
if (dma_alloc_pages(ram_size / 4096, 2 * 1024 * 1024, 0, &ram_buf) != 0) {
log_error("[LINUX] failed to alloc guest RAM (64MB)");
/* Try smaller: 32MB */
ram_size = 32 * 1024 * 1024;
if (dma_alloc_pages(ram_size / 4096, 2 * 1024 * 1024, 0, &ram_buf) != 0) {
log_error("[LINUX] failed to alloc guest RAM (32MB)");
return -9;
}
}
log_hex64("[LINUX] guest RAM HPA=", ram_buf.phys);
log_hex64("[LINUX] guest RAM size=", ram_size);
/* 4. Load kernel image into allocated memory */
const u8 *kernel_src = (const u8 *)bzimage + payload_offset;
mem_copy(kernel_buf.virt, kernel_src, payload_length);
log_hex64("[LINUX] kernel loaded, size=", payload_length);
/* 5. Set up guest page tables */
g_guest.pgt_gpa = LINUX_GUEST_PGT_GPA;
build_guest_page_tables(pgt_buf.phys);
log_hex64("[LINUX] page tables built at HPA=", pgt_buf.phys);
/* 6. Set up guest GDT */
g_guest.gdt_gpa = LINUX_GUEST_GDT_GPA;
build_guest_gdt(gdt_buf.phys);
log_hex64("[LINUX] GDT built at HPA=", gdt_buf.phys);
/* 7. Set up command line */
u64 cmdline_len = str_len(g_default_cmdline) + 1; /* include NUL */
mem_copy(cmdline_buf.virt, g_default_cmdline, cmdline_len);
log_info("[LINUX] cmdline:");
log_info(g_default_cmdline);
/* 8. Set up boot_params */
g_guest.bootparams_gpa = LINUX_GUEST_BOOTPARAMS_GPA;
g_guest.kernel_gpa = LINUX_GUEST_KERNEL_GPA;
g_guest.kernel_entry = LINUX_GUEST_KERNEL_GPA + entry_offset;
g_guest.cmdline_gpa = LINUX_GUEST_CMDLINE_GPA;
g_guest.cmdline_size = cmdline_len;
/* 8.5. Load initrd (busybox initramfs) if present.
* initrd 放在 GPA 0x03000000(initrd 区,上限 16MB 到 IPC 区 0x04000000)。 */
g_guest.initrd_gpa = 0;
g_guest.initrd_size = 0;
{
u64 initrd_size = 0;
void *initrd = linux_find_initrd_module(&initrd_size);
if (initrd && initrd_size > 0) {
u64 initrd_max = LINUX_GUEST_IPC_SHM_GPA - LINUX_GUEST_INITRD_GPA; /* 16MB */
if (initrd_size > initrd_max) {
log_error("[LINUX] initrd too large (>16MB), skipped");
} else {
u64 initrd_pages = (initrd_size + 4095) / 4096;
dkm_dma_buffer initrd_buf;
if (dma_alloc_pages(initrd_pages, 4096, 0, &initrd_buf) != 0) {
log_error("[LINUX] failed to alloc initrd memory");
return -17;
}
mem_copy(initrd_buf.virt, initrd, initrd_size);
if (ept_map_range(LINUX_GUEST_INITRD_GPA, initrd_buf.phys,
initrd_pages * 4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[LINUX] EPT map initrd failed");
return -18;
}
g_guest.initrd_gpa = LINUX_GUEST_INITRD_GPA;
g_guest.initrd_size = initrd_size;
log_hex64("[LINUX] initrd mapped GPA=", g_guest.initrd_gpa);
log_hex64("[LINUX] initrd size=", g_guest.initrd_size);
}
} else {
log_warn("[LINUX] booting without initrd (no userland!)");
}
}
setup_boot_params(bp_buf.virt, g_guest.bootparams_gpa,
g_guest.kernel_entry,
g_guest.cmdline_gpa, cmdline_len,
g_guest.initrd_gpa, g_guest.initrd_size,
bzimage);
log_hex64("[LINUX] boot_params at GPA=", g_guest.bootparams_gpa);
/* 9. EPT-map all guest regions (GPA → HPA) */
/* Kernel: GPA 0x01000000 → kernel HPA, RWX */
if (ept_map_range(g_guest.kernel_gpa, kernel_buf.phys,
kernel_alloc_size, EPT_RWX) != 0) {
log_error("[LINUX] EPT map kernel failed");
return -10;
}
/* Boot params: GPA 0x00400000 → bp HPA, RW */
if (ept_map_range(g_guest.bootparams_gpa, bp_buf.phys,
4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[LINUX] EPT map boot_params failed");
return -11;
}
/* Page tables: GPA 0x00700000 → pgt HPA, RW */
if (ept_map_range(g_guest.pgt_gpa, pgt_buf.phys,
PGT_TOTAL_SIZE, EPT_READ | EPT_WRITE) != 0) {
log_error("[LINUX] EPT map page tables failed");
return -12;
}
/* GDT: GPA 0x00600000 → gdt HPA, RW */
if (ept_map_range(g_guest.gdt_gpa, gdt_buf.phys,
4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[LINUX] EPT map GDT failed");
return -13;
}
/* Stack: GPA 0x00800000 → stack HPA, RW */
if (ept_map_range(LINUX_GUEST_STACK_GPA, stack_buf.phys,
4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[LINUX] EPT map stack failed");
return -14;
}
/* Command line: GPA 0x00500000 → cmdline HPA, R */
if (ept_map_range(g_guest.cmdline_gpa, cmdline_buf.phys,
4096, EPT_READ) != 0) {
log_error("[LINUX] EPT map cmdline failed");
return -15;
}
/* General RAM: map the allocated RAM at GPA 0x05000000 (after IPC region).
* GPA layout (matches e820):
* 0x00400000-0x009FFFFF: fixed regions (boot_params, GDT, pgt, stack, cmdline)
* 0x01000000-0x02FFFFFF: kernel
* 0x03000000-0x03FFFFFF: initrd (if any)
* 0x04000000-0x040FFFFF: IPC shared memory (mapped by ipc_shm_init)
* 0x05000000-0x08FFFFFF: general RAM (64MB)
* Linux sees this as usable RAM in e820. */
u64 ram_gpa = LINUX_GUEST_RAM_GPA;
if (ept_map_range(ram_gpa, ram_buf.phys, ram_size, EPT_RWX) != 0) {
log_error("[LINUX] EPT map guest RAM failed");
return -16;
}
log_hex64("[LINUX] guest RAM mapped GPA=", ram_gpa);
/* Identity-map the first 1MB (BIOS area, needed by some Linux code paths) */
if (ept_identity_map(0, 0x100000, EPT_RWX) != 0) {
log_warn("[LINUX] EPT identity map first 1MB failed (non-critical)");
}
/* 10. Record guest info */
g_guest.kernel_size = payload_length;
g_guest.loaded = 1;
log_hex64("[LINUX] kernel GPA=", g_guest.kernel_gpa);
log_hex64("[LINUX] kernel entry=", g_guest.kernel_entry);
log_hex64("[LINUX] kernel size=", g_guest.kernel_size);
log_info("[LINUX] loader init ok");
return 0;
}
const struct linux_guest_info *linux_get_guest_info(void) {
return g_guest.loaded ? &g_guest : (struct linux_guest_info *)0;
}
+68
View File
@@ -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 <utsm/linux_resume.h>
#include <utsm/vmx.h>
#include <utsm/vmm.h>
#include <utsm/log.h>
#include <utsm/types.h>
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;
}
+254
View File
@@ -0,0 +1,254 @@
/* virtio_blk.c — UTSM virtio-blk 设备模拟后端。
*
* 为 Linux guest 提供 virtio-blk 块设备。guest 的块读写请求经
* virtqueue 传入,本后端桥接到 UTSM 真实块设备(block API,AHCI/SATA)。
*
* Virtio Spec 1.1 §5.2 (Block Device)。
* 请求描述符链: [header(16B, R)] [data(N*512B, R/W)] [status(1B, W)]
* 支持的操作:VIRTIO_BLK_T_IN(读)、VIRTIO_BLK_T_OUT(写)、VIRTIO_BLK_T_GET_ID。
*
* 设计说明:
* - 桥接到 UTSM block API 的第 0 个设备(ahci0 SATA 盘)。
* - guest 把 Arch rootfs 镜像当作 /dev/vda 挂载;镜像需预先写入 SATA 盘
* 的某个分区/LBA 区间(由 mkfat32/build 流程或独立 raw 区提供)。
* - 当前为最小可用:扇区 512B,单队列,无 flush/discard。
*/
#include <utsm/virtio_mmio.h>
#include <utsm/linux_loader.h>
#include <utsm/block.h>
#include <utsm/log.h>
#include <utsm/types.h>
/* virtio-blk 请求头(Virtio Spec 1.1 §5.2.6.2) */
struct virtio_blk_outhdr {
u32 type; /* VIRTIO_BLK_T_* */
u32 reserved; /* (ioprio) */
u64 sector; /* 起始扇区(512B 单位) */
} __attribute__((packed));
#define VIRTIO_BLK_T_IN 0 /* 读 */
#define VIRTIO_BLK_T_OUT 1 /* 写 */
#define VIRTIO_BLK_T_FLUSH 4
#define VIRTIO_BLK_T_GET_ID 8
#define VIRTIO_BLK_S_OK 0
#define VIRTIO_BLK_S_IOERR 1
#define VIRTIO_BLK_S_UNSUPP 2
/* feature bits */
#define VIRTIO_BLK_F_SIZE_MAX 1
#define VIRTIO_BLK_F_SEG_MAX 2
#define VIRTIO_BLK_F_BLK_SIZE 6
#define VIRTIO_BLK_F_FLUSH 9
/* device config 空间(VIRTIO_MMIO_CONFIG 起) */
struct virtio_blk_config {
u64 capacity; /* 总扇区数(512B) */
u32 size_max;
u32 seg_max;
u16 geometry[4];
u32 blk_size;
/* ... 其余字段省略 */
} __attribute__((packed));
/* 后端状态 */
static u64 g_blk_capacity; /* 扇区数 */
static u32 g_blk_sector_size = 512;
static int g_blk_bound = -1; /* 绑定的 UTSM block 设备索引 */
/* 读取 device config 空间 */
static u32 blk_read_config(u32 offset, int width) {
(void)width;
struct virtio_blk_config cfg;
/* 填零后逐字段读取 */
u8 *c = (u8 *)&cfg;
for (u32 i = 0; i < sizeof(cfg); i++) c[i] = 0;
cfg.capacity = g_blk_capacity;
cfg.size_max = 128; /* 单请求最大 128 扇区 */
cfg.seg_max = 32;
cfg.blk_size = g_blk_sector_size;
/* 按 offset 返回 32 位窗口 */
if (offset + 4 <= sizeof(cfg)) {
u32 v;
u8 *src = (u8 *)&cfg + offset;
v = (u32)src[0] | ((u32)src[1] << 8) | ((u32)src[2] << 16) | ((u32)src[3] << 24);
return v;
}
return 0;
}
/* 处理一个块请求描述符链。返回写入 guest 的字节数(用于 used.len)。 */
static u32 blk_handle_chain(struct virtq_desc *desc, u16 head, u32 qnum) {
(void)qnum;
if (!desc) return 0;
/* 第一个描述符:请求头(driver→device,16 字节) */
struct virtio_blk_outhdr *hdr =
(struct virtio_blk_outhdr *)virtio_gpa_to_host(desc[head].addr);
if (!hdr) return 0;
u32 type = hdr->type;
u64 sector = hdr->sector;
/* 沿描述符链找到 data 与 status 描述符 */
u16 cur = head;
u16 data_idx = 0xFFFF, status_idx = 0xFFFF;
int hops = 0;
if (desc[cur].flags & VIRTQ_DESC_F_NEXT) {
data_idx = desc[cur].next;
cur = data_idx;
/* data 之后可能还有 status */
while ((desc[cur].flags & VIRTQ_DESC_F_NEXT) && hops < 16) {
cur = desc[cur].next;
hops++;
}
/* 最后一个是 status(device write,1 字节) */
status_idx = cur;
}
u8 *status = (u8 *)0;
if (status_idx != 0xFFFF && (desc[status_idx].flags & VIRTQ_DESC_F_WRITE)) {
status = (u8 *)virtio_gpa_to_host(desc[status_idx].addr);
}
u32 bytes_done = 0;
u8 result = VIRTIO_BLK_S_OK;
const dkm_block_api *blk = block_get_api();
switch (type) {
case VIRTIO_BLK_T_IN: { /* 读:块设备 → guest data buffer */
if (data_idx == 0xFFFF || g_blk_bound < 0 || !blk) {
result = VIRTIO_BLK_S_IOERR;
break;
}
u8 *dbuf = (u8 *)virtio_gpa_to_host(desc[data_idx].addr);
u32 dlen = desc[data_idx].len;
if (!dbuf) { result = VIRTIO_BLK_S_IOERR; break; }
u32 nsec = dlen / 512;
if (nsec == 0) nsec = 1;
if (blk->read((u32)g_blk_bound, sector, nsec, dbuf) != 0) {
result = VIRTIO_BLK_S_IOERR;
} else {
bytes_done = dlen;
}
break;
}
case VIRTIO_BLK_T_OUT: { /* 写:guest data buffer → 块设备 */
if (data_idx == 0xFFFF || g_blk_bound < 0 || !blk) {
result = VIRTIO_BLK_S_IOERR;
break;
}
const u8 *dbuf = (const u8 *)virtio_gpa_to_host(desc[data_idx].addr);
u32 dlen = desc[data_idx].len;
if (!dbuf) { result = VIRTIO_BLK_S_IOERR; break; }
u32 nsec = dlen / 512;
if (nsec == 0) nsec = 1;
if (blk->write((u32)g_blk_bound, sector, nsec, dbuf) != 0) {
result = VIRTIO_BLK_S_IOERR;
} else {
bytes_done = 0; /* 写操作 used.len=0 */
}
break;
}
case VIRTIO_BLK_T_GET_ID: {
if (data_idx != 0xFFFF) {
char *idbuf = (char *)virtio_gpa_to_host(desc[data_idx].addr);
if (idbuf) {
const char *id = "DESHAB-VIRTIO-BLK";
u32 i = 0;
for (; id[i] && i < desc[data_idx].len - 1; i++) idbuf[i] = id[i];
idbuf[i] = 0;
bytes_done = i + 1;
}
}
break;
}
case VIRTIO_BLK_T_FLUSH:
result = VIRTIO_BLK_S_OK; /* 无写缓存,直接成功 */
break;
default:
result = VIRTIO_BLK_S_UNSUPP;
break;
}
/* 写状态字节 */
if (status) *status = result;
return bytes_done;
}
/* queue_notify:guest 提交了块请求,处理队列。 */
static u16 g_blk_last_avail;
static void blk_queue_notify(u32 queue_idx) {
struct virtq_desc *desc;
struct virtq_avail *avail;
struct virtq_used *used;
u32 qnum;
if (virtio_queue_get_ptrs(VIRTIO_ID_BLOCK, queue_idx,
&desc, &avail, &used, &qnum) != 0) {
return;
}
/* 处理自上次以来的所有新 avail 请求 */
u16 cur = g_blk_last_avail;
u16 used_idx = used->idx;
while (cur != avail->idx) {
u16 head = avail->ring[cur % qnum];
u32 len = blk_handle_chain(desc, head, qnum);
/* 写 used 回执 */
used->ring[used_idx % qnum].id = head;
used->ring[used_idx % qnum].len = len;
used_idx++;
cur++;
}
virtio_queue_bump_used(used, used_idx);
g_blk_last_avail = cur;
}
static void blk_reset(void) {
g_blk_last_avail = 0;
}
/* 后端结构体 */
static struct virtio_backend g_blk_backend = {
.device_id = VIRTIO_ID_BLOCK,
.gpa_base = VIRTIO_MMIO_BLK_GPA,
.irq = 5, /* guest ISA IRQ5(cmdline :5,vector 0x35) */
.num_queues = 1,
.queue_size = 128,
.device_features = (1ULL << VIRTIO_F_VERSION_1) |
(1ULL << VIRTIO_BLK_F_SIZE_MAX) |
(1ULL << VIRTIO_BLK_F_SEG_MAX) |
(1ULL << VIRTIO_BLK_F_BLK_SIZE) |
(1ULL << VIRTIO_BLK_F_FLUSH),
.config_len = sizeof(struct virtio_blk_config),
.read_config = blk_read_config,
.queue_notify = blk_queue_notify,
.reset = blk_reset,
};
void virtio_blk_backend_init(void) {
/* 绑定 UTSM 第 0 个块设备(ahci0 SATA 盘)作为后端存储 */
const dkm_block_api *blk = block_get_api();
if (blk && blk->device_count() > 0) {
g_blk_bound = 0;
u64 ssize = blk->sector_size(0);
if (ssize > 0) g_blk_sector_size = (u32)ssize;
/* 从设备名推算容量不可得,用默认值(guest 端可再探测)。
* 实际 capacity 需读 ATA IDENTIFY;这里给 4GB 占位,
* guest mkfs/mount 会按此上限工作。 */
g_blk_capacity = 8ULL * 1024 * 1024 * 1024 / 512; /* 8GB in sectors */
log_info("[VBLK] bound to UTSM block device 0");
log_hex64("[VBLK] capacity(sectors)=", g_blk_capacity);
} else {
g_blk_bound = -1;
g_blk_capacity = 0;
log_warn("[VBLK] no UTSM block device, blk backend degraded");
}
virtio_mmio_register(&g_blk_backend);
}
+294
View File
@@ -0,0 +1,294 @@
/* virtio_mmio.c — UTSM virtio-mmio 设备模拟后端框架实现。
*
* 管理一组 virtio_backend 设备(block + net),处理 guest 对 MMIO 寄存器
* 的读/写。寄存器语义遵循 Virtio Spec 1.1 §4.2(modern,version=2)。
*
* guest 设备发现:cmdline virtio_mmio.device=SIZE@GPA:IRQ 参数(见
* linux_loader 的 cmdline 注入)。QUEUE_NOTIFY 处理完后经
* vmx_guest_queue_irq 注入虚拟中断(legacy PIC,vector=0x30+irq)。
*/
#include <utsm/virtio_mmio.h>
#include <utsm/linux_loader.h>
#include <utsm/ept.h>
#include <utsm/vmx.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
#define VIRTIO_MMIO_MAGIC 0x74726976 /* "virt" */
#define VIRTIO_MMIO_VERSION_MODERN 2
#define MAX_VIRTIO_DEVICES 4
/* 每个 virtio 设备的运行状态(框架层维护,后端无关) */
struct virtio_dev_state {
struct virtio_backend *be; /* 后端驱动(NULL = 未注册) */
u32 status; /* device status */
u32 drv_features_sel; /* DRIVER_FEATURES_SEL */
u32 dev_features_sel; /* DEVICE_FEATURES_SEL */
u64 driver_features; /* guest 协商的 features */
u32 queue_sel; /* 当前选中队列 */
u32 interrupt_status; /* INTERRUPT_STATUS */
u32 config_generation;
/* 每队列状态 */
struct {
u32 num; /* QUEUE_NUM(实际启用大小) */
u32 ready; /* QUEUE_READY */
u64 desc_gpa; /* 描述符表 GPA */
u64 driver_gpa; /* avail ring GPA */
u64 device_gpa; /* used ring GPA */
} q[4];
};
static struct virtio_dev_state g_vdevs[MAX_VIRTIO_DEVICES];
static int g_vdev_count;
static int g_vio_ready;
/* ===== guest 内存访问辅助 ===== */
void *virtio_gpa_to_host(u64 gpa) {
u64 hpa = ept_gpa_to_hpa(gpa);
if (hpa == 0) return (void *)0;
u64 hhdm = g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
return (void *)(hhdm + hpa);
}
/* ===== 设备注册 ===== */
int virtio_mmio_register(struct virtio_backend *dev) {
if (!dev || g_vdev_count >= MAX_VIRTIO_DEVICES) return -1;
struct virtio_dev_state *s = &g_vdevs[g_vdev_count];
s->be = dev;
s->status = 0;
s->drv_features_sel = 0;
s->dev_features_sel = 0;
s->driver_features = 0;
s->queue_sel = 0;
s->interrupt_status = 0;
s->config_generation = 0;
for (int i = 0; i < 4; i++) {
s->q[i].num = 0;
s->q[i].ready = 0;
s->q[i].desc_gpa = 0;
s->q[i].driver_gpa = 0;
s->q[i].device_gpa = 0;
}
g_vdev_count++;
log_hex64("[VIO] registered device id=", dev->device_id);
log_hex64("[VIO] gpa=", dev->gpa_base);
return 0;
}
/* 按 GPA 查找设备(含该设备的 4KB 寄存器区)。 */
static struct virtio_dev_state *find_dev(u64 gpa, u32 *offset_out) {
for (int i = 0; i < g_vdev_count; i++) {
u64 base = g_vdevs[i].be->gpa_base;
if (gpa >= base && gpa < base + 0x1000) {
if (offset_out) *offset_out = (u32)(gpa - base);
return &g_vdevs[i];
}
}
return (struct virtio_dev_state *)0;
}
/* ===== feature 读取(按 sel 返回 32 位窗口) ===== */
static u32 read_features(struct virtio_dev_state *s) {
if (s->dev_features_sel == 0) {
return (u32)(s->be->device_features & 0xFFFFFFFFULL);
} else if (s->dev_features_sel == 1) {
return (u32)(s->be->device_features >> 32);
}
return 0;
}
/* ===== 寄存器读 ===== */
u64 virtio_mmio_read(u64 gpa, int width) {
(void)width; /* virtio-mmio 寄存器固定 32 位访问 */
u32 off;
struct virtio_dev_state *s = find_dev(gpa, &off);
if (!s || !s->be) {
log_hex64("[VIO] read unmapped gpa=", gpa);
return 0;
}
switch (off) {
case VIRTIO_MMIO_MAGIC_VALUE: return VIRTIO_MMIO_MAGIC;
case VIRTIO_MMIO_VERSION: return VIRTIO_MMIO_VERSION_MODERN;
case VIRTIO_MMIO_DEVICE_ID: return s->be->device_id;
case VIRTIO_MMIO_VENDOR_ID: return 0x55444D55; /* "UMDU" 自定义 */
case VIRTIO_MMIO_DEVICE_FEATURES:return read_features(s);
case VIRTIO_MMIO_QUEUE_NUM_MAX: return s->be->queue_size;
case VIRTIO_MMIO_QUEUE_READY: return s->q[s->queue_sel].ready;
case VIRTIO_MMIO_INTERRUPT_STATUS:return s->interrupt_status;
case VIRTIO_MMIO_STATUS: return s->status;
case VIRTIO_MMIO_CONFIG_GENERATION:return s->config_generation;
default:
/* device config 空间(0x100 起) */
if (off >= VIRTIO_MMIO_CONFIG && off < VIRTIO_MMIO_CONFIG + s->be->config_len) {
if (s->be->read_config) {
return s->be->read_config(off - VIRTIO_MMIO_CONFIG, width);
}
}
return 0;
}
}
/* ===== 寄存器写 ===== */
void virtio_mmio_write(u64 gpa, u64 value, int width) {
(void)width;
u32 off;
struct virtio_dev_state *s = find_dev(gpa, &off);
if (!s || !s->be) {
log_hex64("[VIO] write unmapped gpa=", gpa);
return;
}
u32 v32 = (u32)value;
switch (off) {
case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
s->dev_features_sel = v32;
break;
case VIRTIO_MMIO_DRIVER_FEATURES:
if (s->drv_features_sel == 0) {
s->driver_features = (s->driver_features & 0xFFFFFFFF00000000ULL) | v32;
} else if (s->drv_features_sel == 1) {
s->driver_features = (s->driver_features & 0xFFFFFFFFULL) | ((u64)v32 << 32);
}
break;
case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
s->drv_features_sel = v32;
break;
case VIRTIO_MMIO_QUEUE_SEL:
if (v32 < 4) s->queue_sel = v32;
break;
case VIRTIO_MMIO_QUEUE_NUM: {
/* 限制在后端 queue_size 内 */
if (v32 == 0 || v32 > s->be->queue_size) {
s->q[s->queue_sel].num = s->be->queue_size;
} else {
s->q[s->queue_sel].num = v32;
}
break;
}
case VIRTIO_MMIO_QUEUE_READY:
s->q[s->queue_sel].ready = v32 ? 1 : 0;
break;
case VIRTIO_MMIO_QUEUE_DESC_LOW:
s->q[s->queue_sel].desc_gpa = (s->q[s->queue_sel].desc_gpa & 0xFFFFFFFF00000000ULL) | v32;
break;
case VIRTIO_MMIO_QUEUE_DESC_HIGH:
s->q[s->queue_sel].desc_gpa = (s->q[s->queue_sel].desc_gpa & 0xFFFFFFFFULL) | ((u64)v32 << 32);
break;
case VIRTIO_MMIO_QUEUE_DRIVER_LOW:
s->q[s->queue_sel].driver_gpa = (s->q[s->queue_sel].driver_gpa & 0xFFFFFFFF00000000ULL) | v32;
break;
case VIRTIO_MMIO_QUEUE_DRIVER_HIGH:
s->q[s->queue_sel].driver_gpa = (s->q[s->queue_sel].driver_gpa & 0xFFFFFFFFULL) | ((u64)v32 << 32);
break;
case VIRTIO_MMIO_QUEUE_DEVICE_LOW:
s->q[s->queue_sel].device_gpa = (s->q[s->queue_sel].device_gpa & 0xFFFFFFFF00000000ULL) | v32;
break;
case VIRTIO_MMIO_QUEUE_DEVICE_HIGH:
s->q[s->queue_sel].device_gpa = (s->q[s->queue_sel].device_gpa & 0xFFFFFFFFULL) | ((u64)v32 << 32);
break;
case VIRTIO_MMIO_QUEUE_NOTIFY:
/* guest 通知设备处理队列 v32。调用后端处理请求。 */
if (s->be->queue_notify) {
s->be->queue_notify(v32);
}
/* 设置中断状态位(used buffer notification)并注入虚拟 IRQ。
* legacy PIC 模式:vector = 0x30 + irq(master PIC 基址 0x30)。
* 是否真正投递由 vmexit 的 maybe_inject_irq 按 IMR/IF 决定。 */
s->interrupt_status |= 1;
if (s->be->irq != 0) {
vmx_guest_queue_irq(0x30 + s->be->irq);
}
break;
case VIRTIO_MMIO_INTERRUPT_ACK:
s->interrupt_status &= ~v32;
break;
case VIRTIO_MMIO_STATUS:
s->status = v32;
if (v32 == 0) {
/* 复位 */
if (s->be->reset) s->be->reset();
s->driver_features = 0;
s->interrupt_status = 0;
for (int i = 0; i < 4; i++) {
s->q[i].ready = 0;
s->q[i].num = 0;
}
}
break;
default:
/* device config 空间写(多数设备 config 只读,忽略) */
break;
}
}
/* ===== 队列访问 API 实现 =====
*
* 后端 queue_notify 调用 virtio_queue_get_ptrs 取队列三个表的 host 地址,
* 自行遍历 avail ring 处理请求,写 used ring 回执。 */
int virtio_queue_get_ptrs(u32 device_id, u32 queue_idx,
struct virtq_desc **desc_out,
struct virtq_avail **avail_out,
struct virtq_used **used_out,
u32 *queue_num_out) {
struct virtio_dev_state *s = (struct virtio_dev_state *)0;
for (int i = 0; i < g_vdev_count; i++) {
if (g_vdevs[i].be && g_vdevs[i].be->device_id == device_id) {
s = &g_vdevs[i];
break;
}
}
if (!s || queue_idx >= 4) return -1;
if (!s->q[queue_idx].ready) return -2;
struct virtq_desc *desc = (struct virtq_desc *)virtio_gpa_to_host(s->q[queue_idx].desc_gpa);
struct virtq_avail *avail = (struct virtq_avail *)virtio_gpa_to_host(s->q[queue_idx].driver_gpa);
struct virtq_used *used = (struct virtq_used *)virtio_gpa_to_host(s->q[queue_idx].device_gpa);
if (!desc || !avail || !used) return -3;
if (desc_out) *desc_out = desc;
if (avail_out) *avail_out = avail;
if (used_out) *used_out = used;
if (queue_num_out) *queue_num_out = s->q[queue_idx].num ? s->q[queue_idx].num : s->be->queue_size;
return 0;
}
void virtio_queue_bump_used(struct virtq_used *used, u16 new_idx) {
if (!used) return;
__asm__ volatile("mfence" ::: "memory");
used->idx = new_idx;
__asm__ volatile("mfence" ::: "memory");
}
/* ===== 框架初始化 =====
*
* 注册 block 与 net 后端。后端结构体由 virtio_blk.c / virtio_net.c 提供。 */
extern struct virtio_backend g_virtio_blk_backend;
extern struct virtio_backend g_virtio_net_backend;
/* 后端初始化符号(定义于 virtio_blk.c / virtio_net.c)。
* 这里声明以便链接;具体注册在各自文件完成。 */
void virtio_blk_backend_init(void);
void virtio_net_backend_init(void);
void virtio_mmio_init(void) {
if (g_vio_ready) return;
g_vdev_count = 0;
virtio_blk_backend_init();
virtio_net_backend_init();
g_vio_ready = 1;
log_info("[VIO] virtio-mmio backend init ok");
}
+218
View File
@@ -0,0 +1,218 @@
/* virtio_net.c — UTSM virtio-net 设备模拟后端。
*
* 为 Linux guest 提供 virtio-net 网卡。guest 的收发包请求经 virtqueue
* 传入,本后端桥接到 UTSM 真实网卡(net API,e1000/virtio_net DKM 驱动)。
*
* Virtio Spec 1.1 §5.1 (Network Device)。
* 队列:0=RX(device→driver 收包),1=TX(driver→device 发包)。
* 每帧前置 virtio_net_hdr(10/12 字节,无 GSO 时全零)。
*
* 最小可用实现:
* - 无 GSO/TSO/checksum offload(feature 不声明,guest 走软件路径)
* - RX 轮询:guest RX 队列提交空 buffer,queue_notify 时尝试 rx_poll
* - TX:queue_notify 时取帧直接 net->tx 发出
*/
#include <utsm/virtio_mmio.h>
#include <utsm/linux_loader.h>
#include <utsm/net.h>
#include <utsm/log.h>
#include <utsm/types.h>
/* virtio_net_hdr(VIRTIO_NET_F_CSUM/GSO 未协商时为 10 字节 legacy) */
struct virtio_net_hdr {
u8 flags;
u8 gso_type;
u16 hdr_len;
u16 gso_size;
u16 csum_start;
u16 csum_offset;
/* u16 num_buffers; // 仅 VIRTIO_NET_F_MRG_RXBUF 协商后存在 */
} __attribute__((packed));
#define VIRTIO_NET_HDR_LEN 10
/* feature bits(我们都不声明,保持最简) */
#define VIRTIO_NET_F_CSUM 0
#define VIRTIO_NET_F_GUEST_CSUM 1
#define VIRTIO_NET_F_MAC 5
#define VIRTIO_NET_F_STATUS 16
/* device config(VIRTIO_MMIO_CONFIG 起) */
struct virtio_net_config {
u8 mac[6];
u16 status; /* bit0 = link up */
u16 max_virtqueue_pairs;
} __attribute__((packed));
static u8 g_net_mac[6] = { 0x52, 0x54, 0x00, 0xDE, 0x5A, 0x01 };
static int g_net_bound = -1;
static u32 net_read_config(u32 offset, int width) {
(void)width;
struct virtio_net_config cfg;
u8 *c = (u8 *)&cfg;
for (u32 i = 0; i < sizeof(cfg); i++) c[i] = 0;
for (int i = 0; i < 6; i++) cfg.mac[i] = g_net_mac[i];
cfg.status = 1; /* link up */
cfg.max_virtqueue_pairs = 1;
if (offset + 4 <= sizeof(cfg)) {
u8 *src = (u8 *)&cfg + offset;
return (u32)src[0] | ((u32)src[1] << 8) | ((u32)src[2] << 16) | ((u32)src[3] << 24);
}
return 0;
}
/* 从描述符链拼接一个连续帧(virtio-net 帧通常 2 个描述符:hdr + data)。
* 返回帧长度,数据拷入 out_buf(out_cap 容量)。 */
static u32 net_gather_frame(struct virtq_desc *desc, u16 head,
u8 *out_buf, u32 out_cap, int skip_hdr) {
u32 total = 0;
u16 cur = head;
int hops = 0;
while (hops < 32) {
u8 *src = (u8 *)virtio_gpa_to_host(desc[cur].addr);
u32 len = desc[cur].len;
if (!src) break;
/* 跳过第一个描述符中的 virtio_net_hdr */
if (hops == 0 && skip_hdr && len > VIRTIO_NET_HDR_LEN) {
u32 dl = len - VIRTIO_NET_HDR_LEN;
if (total + dl <= out_cap) {
u8 *d = src + VIRTIO_NET_HDR_LEN;
for (u32 i = 0; i < dl; i++) out_buf[total + i] = d[i];
total += dl;
}
} else {
if (total + len <= out_cap) {
for (u32 i = 0; i < len; i++) out_buf[total + i] = src[i];
total += len;
}
}
if (!(desc[cur].flags & VIRTQ_DESC_F_NEXT)) break;
cur = desc[cur].next;
hops++;
}
return total;
}
/* TX queue(idx=1):guest 发包。 */
static u16 g_tx_last_avail;
static u8 g_tx_scratch[2048];
static void net_handle_tx(void) {
struct virtq_desc *desc;
struct virtq_avail *avail;
struct virtq_used *used;
u32 qnum;
if (virtio_queue_get_ptrs(VIRTIO_ID_NET, 1, &desc, &avail, &used, &qnum) != 0)
return;
const dkm_net_api *net = net_get_api();
u16 cur = g_tx_last_avail;
u16 used_idx = used->idx;
while (cur != avail->idx) {
u16 head = avail->ring[cur % qnum];
u32 flen = net_gather_frame(desc, head, g_tx_scratch, sizeof(g_tx_scratch), 1);
if (flen > 0 && net && g_net_bound >= 0) {
net->tx((u32)g_net_bound, g_tx_scratch, flen);
}
used->ring[used_idx % qnum].id = head;
used->ring[used_idx % qnum].len = 0;
used_idx++;
cur++;
}
virtio_queue_bump_used(used, used_idx);
g_tx_last_avail = cur;
}
/* RX queue(idx=0):guest 提交空 buffer,我们尝试 rx_poll 填充。
* guest 每提交一批 buffer 就 notify 一次;有包则填充 + used,无包则
* 保留 buffer 等下次 notify(简化:直接返回 0 长度 used,让 guest 重投)。 */
static u16 g_rx_last_avail;
static void net_handle_rx(void) {
struct virtq_desc *desc;
struct virtq_avail *avail;
struct virtq_used *used;
u32 qnum;
if (virtio_queue_get_ptrs(VIRTIO_ID_NET, 0, &desc, &avail, &used, &qnum) != 0)
return;
const dkm_net_api *net = net_get_api();
if (!net || g_net_bound < 0) return;
u16 cur = g_rx_last_avail;
u16 used_idx = used->idx;
while (cur != avail->idx) {
u16 head = avail->ring[cur % qnum];
/* RX 描述符链:hdr(W) + data(W)。尝试收一包。 */
u16 curd = head;
u8 *hdrp = (u8 *)virtio_gpa_to_host(desc[curd].addr);
u8 *datap = (u8 *)0;
u32 datalen = 0;
if (desc[curd].flags & VIRTQ_DESC_F_NEXT) {
u16 di = desc[curd].next;
datap = (u8 *)virtio_gpa_to_host(desc[di].addr);
datalen = desc[di].len;
}
if (!hdrp || !datap || datalen == 0) { cur++; continue; }
u32 got = 0;
if (net->rx_poll((u32)g_net_bound, datap, datalen, &got) == 0 && got > 0) {
/* 填 virtio_net_hdr 全零(无 GSO) */
for (int i = 0; i < VIRTIO_NET_HDR_LEN; i++) hdrp[i] = 0;
used->ring[used_idx % qnum].id = head;
used->ring[used_idx % qnum].len = VIRTIO_NET_HDR_LEN + got;
used_idx++;
} else {
/* 无包:不消耗该 buffer,留待下次(但为避免死循环,跳过) */
}
cur++;
}
virtio_queue_bump_used(used, used_idx);
g_rx_last_avail = cur;
}
static void net_queue_notify(u32 queue_idx) {
if (queue_idx == 1) net_handle_tx();
else if (queue_idx == 0) net_handle_rx();
}
static void net_reset(void) {
g_tx_last_avail = 0;
g_rx_last_avail = 0;
}
static struct virtio_backend g_net_backend = {
.device_id = VIRTIO_ID_NET,
.gpa_base = VIRTIO_MMIO_NET_GPA,
.irq = 6, /* guest ISA IRQ6(cmdline :6,vector 0x36) */
.num_queues = 2,
.queue_size = 128,
.device_features = (1ULL << VIRTIO_F_VERSION_1) |
(1ULL << VIRTIO_NET_F_MAC) | (1ULL << VIRTIO_NET_F_STATUS),
.config_len = sizeof(struct virtio_net_config),
.read_config = net_read_config,
.queue_notify = net_queue_notify,
.reset = net_reset,
};
void virtio_net_backend_init(void) {
/* 绑定 UTSM 第 0 个网卡(e1000 或 virtio_net DKM 驱动) */
const dkm_net_api *net = net_get_api();
if (net && net->device_count() > 0) {
g_net_bound = 0;
dkm_net_device_info info;
if (net->device_info(0, &info) == 0) {
for (int i = 0; i < 6; i++) g_net_mac[i] = info.mac[i];
}
log_info("[VNET] bound to UTSM net device 0");
} else {
g_net_bound = -1;
log_warn("[VNET] no UTSM net device, net backend degraded");
}
virtio_mmio_register(&g_net_backend);
}
+758
View File
@@ -0,0 +1,758 @@
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/vmm.h>
#include <utsm/hypercall.h>
#include <utsm/ipc_shm.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include <utsm/linux_loader.h>
#include <utsm/virtio_mmio.h>
#include "../arch/x86_64/limine.h"
/* VM-Exit 处理器:从 VMCS 读取 exit reason 与 guest 状态,分发处理。
*
* Phase 1.1:处理 HLT(终止 guest)、EPT violation(动态映射)、异常。
* Phase 1.3:增加 VMCALL hypercall 处理(读 guest GPR、分发、写返回值)。
* Phase 1.4:EPT violation 区分 RAM(按需分配 HPA)与 virtio-mmio
* (路由到设备模拟寄存器读写,不映射真实内存)。
* Phase 1.5:guest 平台设备模拟(legacy 模式,配合 noapic/nolapic):
* - COM1 8250 UART(console=ttyS0 输出 + host 串口输入桥)
* - 8259 PIC(IMR 跟踪,EOI/ICW 吞掉)
* - 8254 PIT ch0(divisor 跟踪,按 guest 编程频率注入 IRQ0 tick)
* - CPUID passthrough(屏蔽 VMX/hypervisor 位)
* - 虚拟中断注入(pending queue + interrupt-window exiting)
* - VMX preemption timer 周期 exit(轮询 host 串口 RX + tick)
*/
extern volatile struct limine_hhdm_request g_hhdm_request;
extern void vmx_vmresume_wrapper(void);
/* serial.c 导出的 host 串口与 TSC 频率接口 */
extern int serial_try_read(void);
extern u64 serial_tsc_per_ms(void);
extern void serial_putc(char c);
static u64 g_vmexit_count;
static void log_exit_diagnostics(u64 reason, u64 qualification, u64 rip, u64 len) {
log_hex64("[VMEXIT] reason=", reason);
log_hex64("[VMEXIT] qual=", qualification);
log_hex64("[VMEXIT] rip=", rip);
log_hex64("[VMEXIT] ilen=", len);
}
/* 处理 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; /* 两种情况都 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;
}
/* ===== EPT violation: guest RAM 按需分配 =====
*
* guest 首次访问某 GPA 页时触发 EPT violation。我们按需分配一个
* 4KB HPA 页并建立 GPA→HPA 映射(不再是错误的 1:1 恒等映射——
* 那会把 guest GPA 直接当 host 物理地址,可能与 UTSM 自身内存冲突)。
*
* 已映射区域的访问权限不足(如先 R 映射后写)也走这里:升级为 RWX。
*/
/* 按需为普通 guest RAM GPA 分配一个 HPA 页并映射。返回 0 成功。 */
static int ept_map_guest_ram_page(u64 gpa_page) {
dkm_dma_buffer buf;
if (dma_alloc_pages(1, EPT_PAGE_SIZE, 0, &buf) != 0) {
log_error("[VMEXIT] OOM for guest RAM page");
return -1;
}
if (ept_map_range(gpa_page, buf.phys, EPT_PAGE_SIZE, EPT_RWX) != 0) {
log_error("[VMEXIT] EPT map guest RAM page failed");
return -1;
}
return 0;
}
/* ===== 虚拟中断注入(legacy PIC 模式,vector = 0x30 + irq) =====
*
* Linux 以 noapic/nolapic 启动时走 XT-PIC:master PIC 向量基址 0x30。
* 我们向 guest 注入的外部中断只能是这些 ISA IRQ 向量:
* IRQ0 (0x30) PIT tick、IRQ4 (0x34) COM1、IRQ5 (0x35) virtio-blk、
* IRQ6 (0x36) virtio-net。
*
* 注入条件:guest RFLAGS.IF=1 且 interruptibility=0 且 IMR 未 mask。
* 不满足时 arm CPU_BASED_INTR_WINDOW_EXITING,guest 一旦开中断
* 立即产生 interrupt-window exit,届时再注入。
*/
#define IRQ_PENDING_MAX 16
static u8 g_pending_vectors[IRQ_PENDING_MAX];
static int g_pending_head, g_pending_tail;
/* guest 8259 PIC 状态(master) */
static u8 g_pic_imr = 0xFF; /* 初始全 mask */
static int g_pic_icw_state = 0; /* 0=就绪(OCW), 1=等ICW2, 2=等ICW3, 3=等ICW4 */
static int g_pic_icw_sngl = 0;
static int g_pic_icw_need4 = 0;
void vmx_guest_queue_irq(u32 vector) {
/* 合并连续相同向量(PIT tick 合并 / COM1 RX 去重) */
if (g_pending_tail != g_pending_head) {
int last = (g_pending_tail + IRQ_PENDING_MAX - 1) % IRQ_PENDING_MAX;
if (g_pending_vectors[last] == (u8)vector) return;
}
int next = (g_pending_tail + 1) % IRQ_PENDING_MAX;
if (next == g_pending_head) return; /* 队列满:丢弃 */
g_pending_vectors[g_pending_tail] = (u8)vector;
g_pending_tail = next;
}
static void maybe_inject_irq(void) {
if (g_pending_head == g_pending_tail) return;
u8 vec = g_pending_vectors[g_pending_head];
/* IMR 检查:master PIC vector 0x30+n → IMR bit n */
if (vec >= 0x30 && vec < 0x38) {
if (g_pic_imr & (1u << (vec - 0x30))) {
/* 被 guest mask:丢弃(级别触发设备下次 notify 会重发) */
g_pending_head = (g_pending_head + 1) % IRQ_PENDING_MAX;
return;
}
}
u64 rflags = vmx_vmcs_read(VMCS_GUEST_RFLAGS);
u64 intr_st = vmx_vmcs_read(VMCS_GUEST_INTERRUPTIBILITY);
u64 cpu = vmx_vmcs_read(VMCS_CPU_BASED_VM_EXEC_CONTROL);
if ((rflags & 0x200) && intr_st == 0) {
/* 可注入:写 VM-entry interruption-information field */
vmx_vmcs_write(VMCS_VM_ENTRY_INTERRUPT_INFO,
VM_ENTRY_INTR_INFO_VALID |
(VM_ENTRY_INTR_TYPE_HW_IRQ << 8) | vec);
g_pending_head = (g_pending_head + 1) % IRQ_PENDING_MAX;
if (cpu & CPU_BASED_INTR_WINDOW_EXITING) {
vmx_vmcs_write(VMCS_CPU_BASED_VM_EXEC_CONTROL,
cpu & ~CPU_BASED_INTR_WINDOW_EXITING);
}
} else {
/* 暂不可注入:arm interrupt-window exiting */
if (!(cpu & CPU_BASED_INTR_WINDOW_EXITING)) {
vmx_vmcs_write(VMCS_CPU_BASED_VM_EXEC_CONTROL,
cpu | CPU_BASED_INTR_WINDOW_EXITING);
}
}
}
/* ===== guest COM1 8250 UART 模拟 =====
*
* console=ttyS0 的 I/O 端口 0x3F8-0x3FF。
* TX:THR 写 → host 串口输出(与 UTSM log 共用 QEMU stdio)。
* RX:host 串口输入 → g_guest_rx 环形缓冲 → guest 读 RBR;
* 有数据时注入 IRQ4(vector 0x34)。
* 寄存器:DLL/DLM/IER/FCR/LCR/MCR/SCR 存储,LSR/IIR/MSR 按状态合成。
*/
#define GUEST_RX_SIZE 256
static u8 g_guest_rx[GUEST_RX_SIZE];
static int g_rx_head, g_rx_tail;
static u8 g_com1_dll, g_com1_dlm, g_com1_ier, g_com1_fcr;
static u8 g_com1_lcr, g_com1_mcr, g_com1_scr;
static u64 com1_io(u32 port, int is_in, u64 value) {
u32 reg = port - 0x3F8;
int dlab = (g_com1_lcr & 0x80) != 0;
if (is_in) {
switch (reg) {
case 0: /* RBR / DLL */
if (dlab) return g_com1_dll;
if (g_rx_head != g_rx_tail) {
u8 b = g_guest_rx[g_rx_head];
g_rx_head = (g_rx_head + 1) % GUEST_RX_SIZE;
return b;
}
return 0;
case 1: return dlab ? g_com1_dlm : g_com1_ier;
case 2: /* IIR:bit0=0 表示有中断;bits7:6=1 表示 FIFO 启用 */
if (g_rx_head != g_rx_tail) return 0xC4; /* RX data available */
return 0xC1; /* no interrupt pending */
case 3: return g_com1_lcr;
case 4: return g_com1_mcr;
case 5: /* LSR:bit0 DR,bit5 THRE,bit6 TEMT */
return ((g_rx_head != g_rx_tail) ? 0x01 : 0x00) | 0x20 | 0x40;
case 6: return 0xB0; /* MSR:CTS|DSR|DCD */
case 7: return g_com1_scr;
}
return 0xFF;
}
switch (reg) {
case 0:
if (dlab) g_com1_dll = (u8)value;
else serial_putc((char)(value & 0xFF)); /* guest → host 串口 */
break;
case 1: if (dlab) g_com1_dlm = (u8)value; else g_com1_ier = (u8)value; break;
case 2: g_com1_fcr = (u8)value; break;
case 3: g_com1_lcr = (u8)value; break;
case 4: g_com1_mcr = (u8)value; break;
case 7: g_com1_scr = (u8)value; break;
}
return 0;
}
/* ===== guest 8254 PIT ch0 模拟(仅跟踪 divisor,tick 由 preemption exit 驱动) ===== */
static u16 g_pit_divisor = 0; /* 0 = 65536(默认 18.2Hz) */
static int g_pit_load_state = 0; /* 0=等 lo 字节, 1=等 hi 字节 */
static u8 g_pit_lo = 0;
static u64 g_pit_period_tsc = 0; /* 每 tick 的 TSC 周期 */
static u64 g_pit_accum = 0;
static u64 g_last_tick_tsc = 0;
static u64 rdtsc_local(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
static void pit_recompute_period(void) {
u64 tpm = serial_tsc_per_ms();
if (tpm == 0) tpm = 2000000; /* 未校准时按 ~2GHz 兜底 */
u64 div = g_pit_divisor ? g_pit_divisor : 65536;
/* period_tsc = div / 1193182 Hz,用 ms 刻度换算避免溢出 */
g_pit_period_tsc = (div * tpm) / 1193; /* ≈ div * tpm * 1000 / 1193182 */
if (g_pit_period_tsc == 0) g_pit_period_tsc = 1;
}
static u64 pit_io(u32 port, int is_in, u64 value) {
if (is_in) return 0; /* 计数器读回:恒 0(Linux 用 TSC clocksource,不依赖) */
if (port == 0x43) {
/* mode/command:仅关心 ch0 + lo/hi 访问方式 */
if ((value & 0xC0) == 0x00 && (value & 0x30) == 0x30) {
g_pit_load_state = 0;
}
return 0;
}
if (port == 0x40) {
if (g_pit_load_state == 0) {
g_pit_lo = (u8)value;
g_pit_load_state = 1;
} else {
g_pit_divisor = (u16)(g_pit_lo | ((u16)(u8)value << 8));
g_pit_load_state = 0;
pit_recompute_period();
}
return 0;
}
return 0; /* 0x41/0x42 吞掉 */
}
/* ===== 其余 legacy 端口(吞掉/返回安全值) ===== */
static u64 misc_io(u32 port, int is_in, u64 value, int *out_terminate) {
/* 8259 PIC master */
if (port == 0x20) {
if (!is_in) {
if (value & 0x10) { /* ICW1 */
g_pic_icw_sngl = (value & 0x02) != 0;
g_pic_icw_need4 = (value & 0x01) != 0;
g_pic_icw_state = 1;
g_pic_imr = 0xFF;
}
/* 其余 OCW2/OCW3(含 EOI)吞掉 */
}
return 0;
}
if (port == 0x21) {
if (is_in) return g_pic_imr;
switch (g_pic_icw_state) {
case 1: /* ICW2(向量基址,忽略,固定 0x30) */
g_pic_icw_state = g_pic_icw_sngl ? (g_pic_icw_need4 ? 3 : 0) : 2;
break;
case 2: /* ICW3 */
g_pic_icw_state = g_pic_icw_need4 ? 3 : 0;
break;
case 3: /* ICW4 */
g_pic_icw_state = 0;
break;
default: /* OCW1 = IMR */
g_pic_imr = (u8)value;
break;
}
return 0;
}
/* 8259 PIC slave:吞掉(不用) */
if (port == 0xA0 || port == 0xA1) return is_in ? 0xFF : 0;
/* 8254 PIT */
if (port >= 0x40 && port <= 0x43) return pit_io(port, is_in, value);
/* PS/2 键盘/鼠标(i8042):status 读 0(空),data 读 0,写吞掉。
* i8042 自检将失败 → guest 无 PS/2 键鼠(console 输入走串口)。 */
if (port == 0x60 || port == 0x64) return is_in ? 0 : 0;
/* CMOS/RTC */
if (port == 0x70 || port == 0x71) return is_in ? 0 : 0;
/* PCI config mechanism #1:对 guest 隐藏整个 PCI 总线。
* guest reconfig BAR 会破坏 UTSM 的 AHCI/e1000,故全部返回"无设备"。 */
if (port >= 0xCF8 && port <= 0xCFF) return is_in ? 0xFFFFFFFFULL : 0;
/* 0xCF9 reset control:guest 请求重启 → 终止 guest 回 host */
if (port == 0xCF9) {
if (!is_in) {
log_warn("[VMEXIT] guest requested reboot via 0xCF9");
*out_terminate = 1;
}
return 0;
}
/* COM2-4:无设备 */
if ((port >= 0x2F8 && port <= 0x2FF) ||
(port >= 0x3E8 && port <= 0x3EF) ||
(port >= 0x2E8 && port <= 0x2EF)) {
return is_in ? 0xFF : 0;
}
/* 其余端口(0x80 delay、0x61 NMI、0x92 A20、0xB2 APM、0xF0-0xFF FPU
* error、0xED delay、ACPI 等):读 0,写吞掉 */
return is_in ? 0 : 0;
}
/* ===== EXIT_IO_INSTRUCTION 处理 =====
*
* exit qualification(Intel SDM 27.2.4.2? 实为 Vol 3D 表 27-6):
* bits 2:0 = 访问大小-1(0=1B, 1=2B, 3=4B)
* bit 3 = 方向(0=OUT, 1=IN)
* bit 4 = string 指令
* bit 5 = REP 前缀
* bits31:16 = 端口号
* OUT 数据在 guest RAX;IN 结果写回 guest RAX。
*/
static int handle_io(u64 qualification, u64 rip, u64 instr_len, int *out_resume) {
u32 port = (u32)(qualification >> 16);
u32 size = (u32)(qualification & 7) + 1;
int is_in = (qualification >> 3) & 1;
u64 value = 0;
int terminate = 0;
if (!is_in) {
value = g_guest_regs.rax;
if (size == 1) value &= 0xFF;
else if (size == 2) value &= 0xFFFF;
else value &= 0xFFFFFFFFULL;
}
u64 result;
if (port >= 0x3F8 && port <= 0x3FF) {
result = com1_io(port, is_in, value);
} else {
result = misc_io(port, is_in, value, &terminate);
}
if (is_in) {
if (size == 1) {
g_guest_regs.rax = (g_guest_regs.rax & ~0xFFULL) | (result & 0xFF);
} else if (size == 2) {
g_guest_regs.rax = (g_guest_regs.rax & ~0xFFFFULL) | (result & 0xFFFF);
} else {
g_guest_regs.rax = result & 0xFFFFFFFFULL; /* 32 位写清零高 32 位 */
}
}
if (terminate) {
*out_resume = 0;
return 0;
}
vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len);
*out_resume = 1;
return 0;
}
/* ===== EXIT_CPUID 处理:host passthrough + 屏蔽 VMX/hypervisor =====
*
* CPUID 在 VMX non-root 无条件 exit。必须真实执行并把结果写回 guest
* rax/rbx/rcx/rdx,否则 guest 特性探测全错(原"skip"实现会崩 Linux)。
* leaf 1 ecx:清 bit5(VMX) 与 bit31(hypervisor),让 guest 认为自己在裸机。
*/
static void cpuid_host(u32 a, u32 c, u32 *oa, u32 *ob, u32 *oc, u32 *od) {
__asm__ volatile("cpuid"
: "=a"(*oa), "=b"(*ob), "=c"(*oc), "=d"(*od)
: "a"(a), "c"(c));
}
static int handle_cpuid(u64 rip, u64 instr_len, int *out_resume) {
u32 a = (u32)g_guest_regs.rax;
u32 c = (u32)g_guest_regs.rcx;
u32 oa, ob, oc, od;
cpuid_host(a, c, &oa, &ob, &oc, &od);
if (a == 1) {
oc &= ~(1u << 5); /* VMX */
oc &= ~(1u << 31); /* hypervisor present */
}
/* leaf 0x15/0x16 passthrough:QEMU 提供 TSC/总线频率,
* guest 据此校准 TSC(绕过 PIT ch2 校准路径)。 */
g_guest_regs.rax = oa;
g_guest_regs.rbx = ob;
g_guest_regs.rcx = oc;
g_guest_regs.rdx = od;
vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len);
*out_resume = 1;
return 0;
}
/* ===== resume 前例行工作:RX 轮询 + PIT tick + 注入 pending IRQ ===== */
static void poll_host_serial_to_guest(void) {
for (int i = 0; i < 32; i++) {
int b = serial_try_read();
if (b < 0) break;
int next = (g_rx_tail + 1) % GUEST_RX_SIZE;
if (next == g_rx_head) break; /* ring 满 */
g_guest_rx[g_rx_tail] = (u8)b;
g_rx_tail = next;
}
if (g_rx_head != g_rx_tail) {
vmx_guest_queue_irq(0x34); /* COM1 = IRQ4 → vector 0x34 */
}
}
static void pit_tick_update(void) {
u64 now = rdtsc_local();
if (g_last_tick_tsc == 0) {
g_last_tick_tsc = now;
if (g_pit_period_tsc == 0) pit_recompute_period();
return;
}
u64 delta = now - g_last_tick_tsc;
g_last_tick_tsc = now;
if (g_pit_period_tsc == 0) return;
g_pit_accum += delta;
int n = 0;
while (g_pit_accum >= g_pit_period_tsc && n < 8) {
g_pit_accum -= g_pit_period_tsc;
vmx_guest_queue_irq(0x30); /* PIT = IRQ0 → vector 0x30(自动合并) */
n++;
}
if (g_pit_accum >= g_pit_period_tsc) g_pit_accum = 0; /* 防积压 */
}
static u64 g_preemption_quantum = 0;
u64 vmx_preemption_quantum_1ms(void) {
if (g_preemption_quantum) return g_preemption_quantum;
u64 basic = vmx_read_msr(IA32_VMX_BASIC);
u64 scale = (basic >> 48) & 0xFF; /* preemption timer scale */
if (scale == 0) scale = 1;
u64 tpm = serial_tsc_per_ms();
if (tpm == 0) tpm = 2000000;
g_preemption_quantum = tpm / scale;
if (g_preemption_quantum == 0) g_preemption_quantum = 1;
return g_preemption_quantum;
}
int vmx_preemption_timer_supported(void) {
u64 msr = vmx_read_msr(IA32_VMX_TRUE_PINBASED_CTLS);
return (msr & (1ULL << (6 + 32))) != 0; /* bit6 允许置 1 */
}
/* 每次 vmresume 前调用(vmexit_dispatch resume=1 路径末尾)。
* 重新 arm preemption timer(1ms 周期 exit),轮询 host 串口 RX,
* 推进 PIT tick,注入 pending IRQ。 */
static void vmexit_before_resume(void) {
if (!g_linux_guest_active) return;
vmx_vmcs_write(VMCS_GUEST_PREEMPTION_TIMER, vmx_preemption_quantum_1ms());
poll_host_serial_to_guest();
pit_tick_update();
maybe_inject_irq();
}
/* ===== EPT violation: 指令解码(用于 virtio-mmio MMIO 数据) =====
*
* virtio-mmio 寄存器访问是普通 MOV 指令,如:
* mov eax, [gpa] 读 (8A /r 或 A1)
* mov [gpa], eax 写 (89 /r 或 A3)
* movzx/movsx 等
*
* 我们不需要完整解码——只需确定:
* 1. 访问方向(读 or 写)
* 2. 访问宽度(1/2/4/8 字节)
* 3. 写访问时的写入数据;读访问时数据写到哪个 guest GPR
*
* 这通过解码指令字节 + 读 guest RIP 处的机器码完成。
* 用 GVA=EPT GPA 简化:guest 采用恒等页表,GUEST_LINEAR_ADDR 即 GPA。
*/
/* 从 guest 内存读指令字节(GPA=GVA 恒等映射,经 EPT 翻译到 HPA)。 */
static int guest_fetch(u64 gva, u8 *buf, u64 len) {
u64 hhdm = g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
for (u64 i = 0; i < len; i++) {
u64 hpa = ept_gpa_to_hpa(gva + i);
if (hpa == 0) return -1;
buf[i] = *(volatile u8 *)(hhdm + hpa);
}
return 0;
}
/* 解码 MMIO 访问指令。
* rip_gpa : guest RIP(GPA,恒等映射)
* is_write_out : 输出 1=写 MMIO,0=读 MMIO
* width_out : 输出访问宽度(字节)
* reg_field_out: 输出目标/源 GPR 在 g_guest_regs 中的索引(0-15)
* 返回指令总长度(用于回退时推进 RIP),失败返回 0。 */
static u64 decode_mmio_insn(u64 rip_gpa, int *is_write_out, int *width_out,
int *reg_field_out) {
u8 insn[16];
if (guest_fetch(rip_gpa, insn, sizeof(insn)) != 0) return 0;
u64 off = 0;
int rex_w = 0, rex_r = 0;
/* 跳过前缀 + REX */
for (;;) {
u8 b = insn[off];
if (b == 0x66 || b == 0x67 || b == 0xF0 || b == 0xF2 || b == 0xF3 ||
(b >= 0x2E && b <= 0x3E && (b==0x2E||b==0x36||b==0x3E||b==0x26||b==0x64||b==0x65))) {
off++;
continue;
}
if (b >= 0x40 && b <= 0x4F) { /* REX */
rex_w = (b >> 3) & 1;
rex_r = (b >> 2) & 1;
off++;
}
break;
}
u8 op = insn[off];
u8 modrm = insn[off + 1];
int reg = ((modrm >> 3) & 7) | (rex_r << 3);
/* mov r, m(读 MMIO): 8A(8位) / 8B(全宽) */
if (op == 0x8A || op == 0x8B) {
*is_write_out = 0;
*width_out = (op == 0x8A) ? 1 : (rex_w ? 8 : 4);
*reg_field_out = reg;
return off + 2; /* 简化:假设 ModRM mod=00,rm=101(无 SIB/disp) */
}
/* mov m, r(写 MMIO): 88(8位) / 89(全宽) */
if (op == 0x88 || op == 0x89) {
*is_write_out = 1;
*width_out = (op == 0x88) ? 1 : (rex_w ? 8 : 4);
*reg_field_out = reg;
return off + 2;
}
/* movzx: 0F B6(8→宽) / 0F B7(16→宽) 读 */
if (op == 0x0F && (insn[off+1] == 0xB6 || insn[off+1] == 0xB7)) {
*is_write_out = 0;
*width_out = (insn[off+1] == 0xB6) ? 1 : 2;
u8 m2 = insn[off + 2];
*reg_field_out = ((m2 >> 3) & 7) | (rex_r << 3);
return off + 3;
}
/* 无法解码(如带 SIB/displacement 的复杂寻址)。
* virtio-mmio 驱动用简单寄存器访问,暂不支持则返回 0。 */
return 0;
}
/* 按索引读写 guest GPR(用于 MMIO 数据的源/目的)。 */
static u64 *gpr_ptr(int idx) {
switch (idx & 15) {
case 0: return (u64 *)&g_guest_regs.rax;
case 1: return (u64 *)&g_guest_regs.rcx;
case 2: return (u64 *)&g_guest_regs.rdx;
case 3: return (u64 *)&g_guest_regs.rbx;
case 6: return (u64 *)&g_guest_regs.rsi;
case 7: return (u64 *)&g_guest_regs.rdi;
case 5: return (u64 *)&g_guest_regs.rbp;
case 4: return (u64 *)&g_guest_regs.rax; /* rsp 不用作 MMIO 数据,占位 */
case 8: return (u64 *)&g_guest_regs.r8;
case 9: return (u64 *)&g_guest_regs.r9;
case 10: return (u64 *)&g_guest_regs.r10;
case 11: return (u64 *)&g_guest_regs.r11;
case 12: return (u64 *)&g_guest_regs.r12;
case 13: return (u64 *)&g_guest_regs.r13;
case 14: return (u64 *)&g_guest_regs.r14;
case 15: return (u64 *)&g_guest_regs.r15;
}
return (u64 *)&g_guest_regs.rax;
}
/* 处理 virtio-mmio 设备 GPA 的访问:路由到设备模拟。返回 1=已处理(resume)。 */
static int handle_virtio_mmio_access(u64 gpa, u64 rip, int *out_resume) {
int is_write, width, reg;
u64 insn_len = decode_mmio_insn(rip, &is_write, &width, &reg);
if (insn_len == 0) {
log_error("[VMEXIT] virtio-mmio: cannot decode access insn");
log_hex64("[VMEXIT] rip=", rip);
*out_resume = 0;
return -1;
}
u64 value = 0;
if (is_write) {
/* 从 guest GPR 取写入数据(按宽度截断) */
u64 raw = *gpr_ptr(reg);
if (width == 1) value = raw & 0xFF;
else if (width == 2) value = raw & 0xFFFF;
else if (width == 4) value = raw & 0xFFFFFFFFULL;
else value = raw;
virtio_mmio_write(gpa, value, width);
} else {
/* 从设备读,写入 guest GPR(按宽度) */
value = virtio_mmio_read(gpa, width);
u64 *dst = gpr_ptr(reg);
if (width == 1) *dst = (*dst & ~0xFFULL) | (value & 0xFF);
else if (width == 2) *dst = (*dst & ~0xFFFFULL) | (value & 0xFFFF);
else if (width == 4) *dst = value & 0xFFFFFFFFULL; /* 32位写清零高32 */
else *dst = value;
}
/* 推进 RIP 越过 MMIO 指令,resume guest */
vmx_vmcs_write(VMCS_GUEST_RIP, rip + insn_len);
*out_resume = 1;
return 0;
}
/* 处理 EPT violation:区分 guest RAM(按需分配)与 virtio-mmio(设备模拟)。
*
* exit qualification bits:
* bit0 = read access, bit1 = write access, bit2 = instruction fetch
* bit3 = EPT entry readable, bit4 = writable, bit5 = executable
* virtio-mmio GPA 故意不映射 → bit3/4/5 全 0 → "访问不存在页"。 */
static int handle_ept_violation(u64 qualification, u64 rip, int *out_resume) {
(void)qualification; /* virtio-mmio 判定只看 GPA,不看访问类型 */
u64 gpa = vmx_vmcs_read(VMCS_GUEST_PHYSICAL_ADDR);
/* virtio-mmio 设备区:路由到设备模拟,绝不映射真实内存 */
if (IS_VIRTIO_MMIO_GPA(gpa)) {
return handle_virtio_mmio_access(gpa, rip, out_resume);
}
/* 普通 guest RAM:按需分配 HPA 页并映射 */
u64 page = gpa & ~(EPT_PAGE_SIZE - 1);
if (ept_map_guest_ram_page(page) != 0) {
log_error("[VMEXIT] EPT violation: guest RAM map failed");
log_hex64("[VMEXIT] gpa=", gpa);
*out_resume = 0;
return -1;
}
*out_resume = 1;
return 0;
}
/* 处理通用异常:打印诊断信息,终止 guest。 */
static int handle_exception(u64 qualification, u64 rip, int *out_resume) {
u64 intr_info = vmx_vmcs_read(0x4404); /* VM_EXIT_INTR_INFO */
log_hex64("[VMEXIT] exception intr_info=", intr_info);
log_hex64("[VMEXIT] exception qual=", qualification);
log_hex64("[VMEXIT] exception rip=", rip);
*out_resume = 0;
return 0;
}
/* VM-Exit 主分发器。从 vmx_vm_exit_handler(汇编入口)调用。
* 返回 1 表示 vmresume;0 表示终止 guest。 */
int vmexit_dispatch(void) {
u64 reason = vmx_vmcs_read(VMCS_EXIT_REASON) & 0xFFFF;
u64 qualification = vmx_vmcs_read(VMCS_EXIT_QUALIFICATION);
u64 rip = vmx_vmcs_read(VMCS_GUEST_RIP);
u64 instr_len = vmx_vmcs_read(VMCS_INSTRUCTION_LENGTH);
int resume = 0;
g_vmexit_count++;
switch (reason) {
case EXIT_HLT:
handle_hlt(rip, instr_len, &resume);
break;
case EXIT_EPT_VIOLATION:
handle_ept_violation(qualification, rip, &resume);
break;
case EXIT_EPT_MISCONFIG:
/* MMIO 区域权限配置错误或硬件 EPT 表损坏。诊断后终止。 */
log_error("[VMEXIT] EPT misconfig");
log_hex64("[VMEXIT] gpa=", vmx_vmcs_read(VMCS_GUEST_PHYSICAL_ADDR));
resume = 0;
break;
case EXIT_EXCEPTION_NMI:
handle_exception(qualification, rip, &resume);
break;
case EXIT_TRIPLE_FAULT:
log_error("[VMEXIT] triple fault");
resume = 0;
break;
case EXIT_CPUID:
/* host passthrough + 屏蔽 VMX/hypervisor 位(handle_cpuid 内推进 RIP) */
handle_cpuid(rip, instr_len, &resume);
break;
case EXIT_IO_INSTRUCTION:
/* legacy 设备模拟:COM1 UART / PIC / PIT / 其余端口吞掉 */
handle_io(qualification, rip, instr_len, &resume);
break;
case EXIT_INTERRUPT_WINDOW:
/* guest 刚开中断(RFLAGS.IF=1):尝试注入 pending IRQ */
{
u64 cpu = vmx_vmcs_read(VMCS_CPU_BASED_VM_EXEC_CONTROL);
vmx_vmcs_write(VMCS_CPU_BASED_VM_EXEC_CONTROL,
cpu & ~CPU_BASED_INTR_WINDOW_EXITING);
maybe_inject_irq();
resume = 1;
}
break;
case EXIT_VMX_PREEMPTION_TIMER:
/* 1ms 周期 exit:resume 前由 vmexit_before_resume 统一
* 轮询 host 串口 RX / 推进 PIT tick / 注入 pending IRQ */
resume = 1;
break;
case EXIT_RDMSR:
case EXIT_WRMSR:
vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len);
resume = 1;
break;
case EXIT_VMCALL:
/* VMCALL hypercall: read guest GPRs from g_guest_regs (saved by
* vmexit_asm.S), dispatch to the hypercall handler, and write
* the return value into g_guest_regs.rax. The handler also
* advances GUEST_RIP past the VMCALL instruction. */
{
int hcall_resume = hypercall_handle(
g_guest_regs.rax, g_guest_regs.rdi,
g_guest_regs.rsi, g_guest_regs.rdx,
rip, instr_len);
resume = hcall_resume;
}
break;
default:
log_exit_diagnostics(reason, qualification, rip, instr_len);
resume = 0;
break;
}
(void)qualification;
/* resume 前例行工作:arm preemption timer + host 串口 RX 轮询 +
* PIT tick 推进 + pending IRQ 注入(仅 Linux guest active 时) */
if (resume) {
vmexit_before_resume();
}
return resume;
}
u64 vmexit_get_count(void) { return g_vmexit_count; }
+90
View File
@@ -0,0 +1,90 @@
/* VM-Exit assembly entry.
*
* On VM-Exit, CPU loads host state from VMCS:
* - host CR0/CR3/CR4, segment registers
* - host RSP = VMCS_HOST_RSP (top of g_host_stack)
* - host RIP = VMCS_HOST_RIP (this function)
*
* VMCS does NOT save guest GPRs (RAX-R15). They remain in the CPU registers
* and must be saved explicitly if the C dispatcher wants to read or modify
* them (e.g. for VMCALL hypercall arguments and return values).
*
* This function:
* 1. Saves all 16 guest GPRs into g_guest_regs
* 2. Calls vmm_vmexit_entry() (C dispatcher, returns void)
* - Internally calls vmexit_dispatch(); sets g_guest_terminated if !resume
* 3. Restores all 16 guest GPRs from g_guest_regs (C may have modified rax)
* 4. Checks g_guest_terminated:
* - 0: executes vmresume (GPRs = guest values, possibly modified by C)
* If vmresume fails, falls through to terminate
* - 1: restores saved host RSP, jmps to saved return RIP
*
* The saved RSP/RIP point into the vmlaunch inline asm block (in vmm_self_test
* or linux_launch). After the jmp, C flow continues with failed=0.
*/
.section .text
.global vmx_vm_exit_handler
vmx_vm_exit_handler:
/* Step 1: Save all 16 guest GPRs to g_guest_regs.
* GPRs at this point hold guest values (VMCS doesn't save them). */
movq %rax, g_guest_regs + 0(%rip)
movq %rbx, g_guest_regs + 8(%rip)
movq %rcx, g_guest_regs + 16(%rip)
movq %rdx, g_guest_regs + 24(%rip)
movq %rsi, g_guest_regs + 32(%rip)
movq %rdi, g_guest_regs + 40(%rip)
movq %rbp, g_guest_regs + 48(%rip)
movq %r8, g_guest_regs + 56(%rip)
movq %r9, g_guest_regs + 64(%rip)
movq %r10, g_guest_regs + 72(%rip)
movq %r11, g_guest_regs + 80(%rip)
movq %r12, g_guest_regs + 88(%rip)
movq %r13, g_guest_regs + 96(%rip)
movq %r14, g_guest_regs + 104(%rip)
movq %r15, g_guest_regs + 112(%rip)
/* Step 2: Align stack to 16 bytes and call C dispatcher.
* The host RSP (from VMCS_HOST_RSP) is 16-byte aligned minus 8
* (for the implicit return address), so we sub 8 for alignment. */
subq $8, %rsp
call vmm_vmexit_entry
addq $8, %rsp
/* Step 3: Restore all 16 guest GPRs from g_guest_regs.
* C handlers may have modified fields (e.g. rax = hypercall return value). */
movq g_guest_regs + 0(%rip), %rax
movq g_guest_regs + 8(%rip), %rbx
movq g_guest_regs + 16(%rip), %rcx
movq g_guest_regs + 24(%rip), %rdx
movq g_guest_regs + 32(%rip), %rsi
movq g_guest_regs + 40(%rip), %rdi
movq g_guest_regs + 48(%rip), %rbp
movq g_guest_regs + 56(%rip), %r8
movq g_guest_regs + 64(%rip), %r9
movq g_guest_regs + 72(%rip), %r10
movq g_guest_regs + 80(%rip), %r11
movq g_guest_regs + 88(%rip), %r12
movq g_guest_regs + 96(%rip), %r13
movq g_guest_regs + 104(%rip), %r14
movq g_guest_regs + 112(%rip), %r15
/* Step 4: Check terminate flag */
movl g_guest_terminated(%rip), %eax
testl %eax, %eax
jnz .Lterminate
/* Resume guest: GPRs are now restored (with any C modifications).
* vmresume does not return on success — CPU enters guest. */
vmresume
/* If we reach here, vmresume failed (CF=1 or ZF=1). */
movl $1, g_guest_terminated(%rip)
/* Fall through to terminate */
.Lterminate:
/* Terminate: restore saved RSP and jmp to saved return RIP.
* The saved RSP is the caller's stack pointer before vmlaunch.
* The saved RIP is the "1:" label in the vmlaunch inline asm,
* which sets failed=0 and continues C flow. */
movq g_saved_host_rsp(%rip), %rsp
jmpq *g_saved_return_rip(%rip)
+529
View File
@@ -0,0 +1,529 @@
#include <utsm/vmm.h>
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
/* ===== VMX 指令错误码 ===== */
static const char *vmx_error_str(u64 err) {
switch (err) {
case 1: return "VMCALL/VMCLEAR/VMLAUNCH/VMRESUME with non-launched VMCS";
case 2: return "VMRESUME after VMXOFF";
case 3: return "VMRESUME with corrupted VMCS";
case 4: return "vmlaunch with non-clear VMCS";
case 5: return "VM operation invalid";
case 6: return "VMCS revision mismatch";
case 7: return "VMCS shadowing mismatch";
case 8: return "invalid guest state";
case 9: return "host state invalid";
case 10: return "VM-execution control invalid";
case 11: return "VM-exit control invalid";
case 12: return "VM-entry control invalid";
case 13: return "VM-exit control fields invalid";
case 15: return "address out of width";
case 16: return "MSR bitmap addr invalid";
case 17: return "VMREAD/VMWRITE from unsupported field";
case 18: return "VMCS addr invalid";
default: return "unknown";
}
}
/* ===== VMM 全局状态 ===== */
static int g_vmm_ready;
static u64 g_vmcs_phys;
/* VM-Exit 后回到 host 的栈与 RIP 恢复点。
* vmm_self_test 在 vmlaunch 前保存当前 RSP 与"launch 后"的 RIP,
* VM-Exit 处理器在决定终止 guest 时恢复 RSP 并 jmp 到该 RIP。
* 注意:这些变量必须是非 static 的全局符号,因为 vmexit_asm.S 通过
* RIP 相对寻址引用它们。 */
volatile u64 g_saved_host_rsp;
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)。 */
volatile struct vmexit_guest_regs g_guest_regs;
/* host 栈:VM-Exit 时使用(HOST_RSP)。
* 非static:linux_boot.c 共用同一个 host 栈。 */
u8 g_host_stack[8192] __attribute__((aligned(16)));
/* VM-Exit 汇编入口(vmexit_asm.S) */
extern void vmx_vm_exit_handler(void);
/* C 分发器(vmexit.c) */
extern int vmexit_dispatch(void);
extern u64 vmexit_get_count(void);
/* ===== Phase 1.1 自检 guest 代码 =====
* guest 在 GPA 0x1000 运行,执行 nop;nop;nop;hlt。
* HLT 触发 VMEXIT,dispatch 返回 0(终止)。 */
static u8 g_guest_code[4096] __attribute__((aligned(4096))) = {
0x90, 0x90, 0x90, 0xF4, /* nop; nop; nop; hlt */
};
/* guest 栈页 */
static u8 g_guest_stack[4096] __attribute__((aligned(4096)));
/* MSR bitmap(4KB 全 0 = 不拦截 MSR) */
static u8 g_msr_bitmap[4096] __attribute__((aligned(4096)));
/* guest GDT(GPA 0x3000)与 TSS(GPA 0x4000)。
* VMX 要求 guest TR 非空且引用 GDT 中的 TSS 描述符。 */
static u8 g_guest_gdt[4096] __attribute__((aligned(4096)));
static u8 g_guest_tss[4096] __attribute__((aligned(4096)));
/* 64-bit GDT entry */
typedef struct __attribute__((packed)) {
u16 limit_low;
u16 base_low;
u8 base_mid;
u8 access;
u8 flags_limit_high;
u8 base_high;
} gdt_entry64;
/* 64-bit TSS descriptor (16 bytes) */
typedef struct __attribute__((packed)) {
u32 limit_low_base_low;
u8 base_mid;
u8 access;
u8 flags_limit_high;
u8 base_high;
u32 base_upper;
u32 reserved;
} tss_desc64;
static void build_guest_gdt(void) {
/* 清零 */
for (int i = 0; i < 4096; i++) g_guest_gdt[i] = 0;
for (int i = 0; i < 4096; i++) g_guest_tss[i] = 0;
gdt_entry64 *gdt = (gdt_entry64 *)g_guest_gdt;
/* [0x00] null */
/* [0x08] code: base=0, limit=0xFFFFF, L=1, G=1, present, DPL0, type=0x9B */
gdt[1].limit_low = 0xFFFF;
gdt[1].base_low = 0;
gdt[1].base_mid = 0;
gdt[1].access = 0x9B; /* present, DPL0, code, executed/read */
gdt[1].flags_limit_high = 0xAF; /* G=1, L=1 (64-bit), limit[19:16]=0xF */
gdt[1].base_high = 0;
/* [0x10] data: base=0, limit=0xFFFFF, G=1, present, DPL0, type=0x93 */
gdt[2].limit_low = 0xFFFF;
gdt[2].base_low = 0;
gdt[2].base_mid = 0;
gdt[2].access = 0x93; /* present, DPL0, data, read/write */
gdt[2].flags_limit_high = 0xCF; /* G=1, DB=1, limit[19:16]=0xF */
gdt[2].base_high = 0;
/* [0x18] TSS descriptor (16 bytes, 64-bit TSS) */
tss_desc64 *tss_d = (tss_desc64 *)&g_guest_gdt[0x18];
u64 tss_base = 0x4000; /* TSS at GPA 0x4000 */
u32 tss_limit = 0x67; /* 104-byte minimum TSS */
tss_d->limit_low_base_low = (tss_limit & 0xFFFF) | ((u32)(tss_base & 0xFFFFFF) << 16);
tss_d->base_mid = (u8)((tss_base >> 24) & 0xFF);
tss_d->access = 0x89; /* present, DPL0, 64-bit TSS */
tss_d->flags_limit_high = (u8)((tss_limit >> 16) & 0x0F) | 0x00;
tss_d->base_high = 0;
tss_d->base_upper = 0;
tss_d->reserved = 0;
}
/* ===== CR 读取辅助 ===== */
static inline u64 read_cr0_local(void) {
u64 v;
__asm__ volatile("mov %%cr0, %0" : "=r"(v));
return v;
}
static inline u64 read_cr4_local(void) {
u64 v;
__asm__ volatile("mov %%cr4, %0" : "=r"(v));
return v;
}
static inline u64 read_cr3_local(void) {
u64 v;
__asm__ volatile("mov %%cr3, %0" : "=r"(v));
return v;
}
/* ===== VMCS 配置 ===== */
static u64 vmx_adjust_control(u64 value, u32 msr) {
u32 allowed0 = (u32)vmx_read_msr(msr);
u32 allowed1 = (u32)(vmx_read_msr(msr) >> 32);
u32 adjusted = (u32)value | allowed0;
adjusted &= allowed1;
return adjusted;
}
static void vmcs_setup_host_state(void) {
vmx_vmcs_write(VMCS_HOST_CR0, read_cr0_local() | CR0_PE | CR0_NE | CR0_PG);
vmx_vmcs_write(VMCS_HOST_CR3, read_cr3_local());
vmx_vmcs_write(VMCS_HOST_CR4, read_cr4_local() | CR4_VMXE);
/* 段选择子:UTSM GDT 0x08 (code) / 0x10 (data) */
vmx_vmcs_write(VMCS_HOST_CS_SELECTOR, 0x08);
vmx_vmcs_write(VMCS_HOST_SS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_DS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_ES_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_HOST_FS_SELECTOR, 0);
vmx_vmcs_write(VMCS_HOST_GS_SELECTOR, 0);
vmx_vmcs_write(VMCS_HOST_FS_BASE, 0);
vmx_vmcs_write(VMCS_HOST_GS_BASE, 0);
/* Host GDT/IDT base + TR(VM-Exit 后 CPU 加载 host TR,必须有效) */
{
struct __attribute__((packed)) { u16 limit; u64 base; } gdtr, idtr;
__asm__ volatile("sgdt %0" : "=m"(gdtr));
__asm__ volatile("sidt %0" : "=m"(idtr));
vmx_vmcs_write(VMCS_HOST_GDTR_BASE, gdtr.base);
vmx_vmcs_write(VMCS_HOST_IDTR_BASE, idtr.base);
/* 读取当前 TR 寄存器(UTSM 已加载 TSS) */
u16 tr_sel;
u64 tr_base;
__asm__ volatile("str %0" : "=r"(tr_sel));
/* 从 GDT 中读取 TR base:TR 选择子指向的描述符 */
u64 gdt_base = gdtr.base;
u64 *tss_desc = (u64 *)(gdt_base + (tr_sel & 0xFFF8));
/* 64-bit TSS 描述符 16 字节:base 由 bits 拼接 */
u64 low = tss_desc[0];
u64 high = tss_desc[1];
tr_base = (low >> 16) & 0xFFFFFF; /* base[15:0] + base[23:16] */
tr_base |= ((low >> 32) & 0xFF) << 24; /* base[31:24] */
tr_base |= (high & 0xFFFFFFFF) << 32; /* base[63:32] */
vmx_vmcs_write(VMCS_HOST_TR_SELECTOR, tr_sel);
vmx_vmcs_write(VMCS_HOST_TR_BASE, tr_base);
}
/* Host SYSENTER(VMX 要求字段有效) */
{
u64 sysenter_cs;
__asm__ volatile("rdmsr" : "=a"(sysenter_cs) : "c"(0x174));
vmx_vmcs_write(VMCS_HOST_SYSENTER_CS, sysenter_cs & 0xFFFFFFFF);
vmx_vmcs_write(VMCS_HOST_SYSENTER_ESP, 0);
vmx_vmcs_write(VMCS_HOST_SYSENTER_EIP, 0);
}
/* Host EFER:VM_EXIT_LOAD_HOST_EFER 控制位要求 VMCS 中有 host EFER 值,
* VM-Exit 时加载。读取当前 host EFER MSR 填入。 */
{
u32 low, high;
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0xC0000080));
u64 host_efer = ((u64)high << 32) | low;
vmx_vmcs_write(VMCS_HOST_IA32_EFER, host_efer);
}
/* Host RSP/RIP:VM-Exit 后跳到 vmx_vm_exit_handler */
vmx_vmcs_write(VMCS_HOST_RSP, (u64)&g_host_stack[sizeof(g_host_stack) - 16]);
vmx_vmcs_write(VMCS_HOST_RIP, (u64)vmx_vm_exit_handler);
}
static void vmcs_setup_guest_state_phase1(void) {
/* Guest CR0/CR3/CR4:启用 PE/NE/PG/PAE,复用 host 页表 */
vmx_vmcs_write(VMCS_GUEST_CR0, CR0_PE | CR0_NE | CR0_PG | CR0_WP);
vmx_vmcs_write(VMCS_GUEST_CR3, read_cr3_local());
vmx_vmcs_write(VMCS_GUEST_CR4, CR4_PAE | CR4_PGE | CR4_PSE);
/* 段选择子:flat 模型。
* CS=0x08 (GDT[1] code), DS/SS/ES=0x10 (GDT[2] data),
* TR=0x18 (GDT[3] 64-bit TSS)。 */
vmx_vmcs_write(VMCS_GUEST_CS_SELECTOR, 0x08);
vmx_vmcs_write(VMCS_GUEST_SS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_DS_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_ES_SELECTOR, 0x10);
vmx_vmcs_write(VMCS_GUEST_FS_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_GS_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_LDTR_SELECTOR, 0);
vmx_vmcs_write(VMCS_GUEST_TR_SELECTOR, 0x18);
/* 段 base:flat 模型全 0,TR base 指向 GPA 0x4000 的 TSS */
vmx_vmcs_write(VMCS_GUEST_CS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_SS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_DS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_ES_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_FS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_GS_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_LDTR_BASE, 0);
vmx_vmcs_write(VMCS_GUEST_TR_BASE, 0x4000);
vmx_vmcs_write(VMCS_GUEST_GDTR_BASE, 0x3000);
vmx_vmcs_write(VMCS_GUEST_IDTR_BASE, 0);
/* 段 limit:代码/数据段 4GB,GDTR 覆盖 0x28 字节(3 个描述符 + TSS),
* TR limit = 0x67(104 字节最小 TSS) */
vmx_vmcs_write(VMCS_GUEST_CS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_SS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_DS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_ES_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_FS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_GS_LIMIT, 0xFFFFFFFF);
vmx_vmcs_write(VMCS_GUEST_LDTR_LIMIT, 0xFFFF);
vmx_vmcs_write(VMCS_GUEST_TR_LIMIT, 0x67);
vmx_vmcs_write(VMCS_GUEST_GDTR_LIMIT, 0x28);
vmx_vmcs_write(VMCS_GUEST_IDTR_LIMIT, 0xFFFF);
/* 段 access rights:
* CS: 0xA09B = present, DPL0, code, L=1 (64-bit), executed/read
* DS/SS/ES/FS/GS: 0xC093 = present, DPL0, data, writable
* LDTR: 0x82 = unusable (L=0 in high nibble)
* TR: 0x8B = present, DPL0, 64-bit busy TSS */
vmx_vmcs_write(VMCS_GUEST_CS_ACCESS, 0xA09B);
vmx_vmcs_write(VMCS_GUEST_SS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_DS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_ES_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_FS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_GS_ACCESS, 0xC093);
vmx_vmcs_write(VMCS_GUEST_LDTR_ACCESS, 0x82);
vmx_vmcs_write(VMCS_GUEST_TR_ACCESS, 0x8B);
/* Guest RIP/RSP/RFLAGS */
vmx_vmcs_write(VMCS_GUEST_RIP, 0x1000);
vmx_vmcs_write(VMCS_GUEST_RSP, 0x8000);
vmx_vmcs_write(VMCS_GUEST_RFLAGS, 0x2);
/* Guest SYSENTER(VMX 要求字段有效,即使 guest 不使用) */
vmx_vmcs_write(VMCS_GUEST_SYSENTER_CS, 0);
vmx_vmcs_write(VMCS_GUEST_SYSENTER_ESP, 0);
vmx_vmcs_write(VMCS_GUEST_SYSENTER_EIP, 0);
/* Guest DR7 = 默认值 */
vmx_vmcs_write(VMCS_GUEST_DR7, 0x400);
/* Guest EFER:64-bit guest (CS.L=1) 要求 EFER.LME=1 且 EFER.LMA=1,
* 否则 VMX guest-state 检查失败(error 8: invalid guest state)。
* NXE 允许 guest 使用 NX 位。 */
vmx_vmcs_write(VMCS_GUEST_IA32_EFER, EFER_LME | EFER_LMA | EFER_NXE);
/* Guest activity state = 0 (active) */
vmx_vmcs_write(VMCS_GUEST_ACTIVITY_STATE, 0);
/* Guest interruptibility = 0(无阻塞) */
vmx_vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, 0);
}
static void vmcs_setup_controls(u64 eptp) {
u64 pin = vmx_adjust_control(PIN_EXT_INTERRUPT_EXITING | PIN_NMI_EXITING,
IA32_VMX_TRUE_PINBASED_CTLS);
u64 cpu = CPU_BASED_HLT_EXITING
| CPU_BASED_ACTIVATE_SECONDARY
| CPU_BASED_USE_MSR_BITMAPS
| CPU_BASED_UNCOND_IO_EXITING
| CPU_BASED_INVLPG_EXITING;
cpu = vmx_adjust_control(cpu, IA32_VMX_TRUE_PROCBASED_CTLS);
/* Secondary controls:启用 EPT + VPID */
u64 cpu2 = SEC_EXEC_ENABLE_EPT | SEC_EXEC_ENABLE_VPID;
{
u64 msr = vmx_read_msr(IA32_VMX_PROCBASED_CTLS2);
u32 allowed1 = (u32)(msr >> 32);
cpu2 &= allowed1;
cpu2 |= SEC_EXEC_ENABLE_EPT;
}
u64 exit_ctrl = VM_EXIT_SAVE_DEBUG_CONTROLS
| VM_EXIT_HOST_ADDR_SPACE_SIZE
| VM_EXIT_LOAD_HOST_EFER;
exit_ctrl = vmx_adjust_control(exit_ctrl, IA32_VMX_TRUE_EXIT_CTLS);
u64 entry_ctrl = VM_ENTRY_LOAD_DEBUG_CONTROLS
| VM_ENTRY_IA32E_MODE_GUEST
| VM_ENTRY_LOAD_GUEST_EFER;
entry_ctrl = vmx_adjust_control(entry_ctrl, IA32_VMX_TRUE_ENTRY_CTLS);
vmx_vmcs_write(VMCS_PIN_BASED_VM_EXEC_CONTROL, pin);
vmx_vmcs_write(VMCS_CPU_BASED_VM_EXEC_CONTROL, cpu);
vmx_vmcs_write(VMCS_SECONDARY_VM_EXEC_CONTROL, cpu2);
vmx_vmcs_write(VMCS_VM_EXIT_CONTROLS, exit_ctrl);
vmx_vmcs_write(VMCS_VM_ENTRY_CONTROLS, entry_ctrl);
/* MSR bitmap 物理地址 */
{
u64 hhdm = g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
u64 msr_bitmap_phys = (u64)g_msr_bitmap - hhdm;
vmx_vmcs_write(VMCS_MSR_BITMAP, msr_bitmap_phys);
}
vmx_vmcs_write(VMCS_EPT_POINTER, eptp);
vmx_vmcs_write(VMCS_VPID, 1);
/* Exception bitmap:捕获 #GP(13)/#PF(14)/#UD(6) */
vmx_vmcs_write(VMCS_EXCEPTION_BITMAP, (1ULL << 13) | (1ULL << 14) | (1ULL << 6));
}
/* ===== VMM API ===== */
int vmm_init(void) {
log_info("[VMM] init begin");
if (vmx_enable() != 0) {
log_error("[VMM] VMX enable failed");
return -1;
}
if (ept_init() != 0) {
log_error("[VMM] EPT init failed");
return -2;
}
if (vmx_vmcs_alloc(&g_vmcs_phys) != 0) {
log_error("[VMM] VMCS alloc failed");
return -3;
}
log_hex64("[VMM] VMCS phys=", g_vmcs_phys);
if (vmx_vmcs_load(g_vmcs_phys) != 0) {
log_error("[VMM] VMCS load failed");
return -4;
}
/* VMCS 字段读写自检 */
vmx_vmcs_write(VMCS_VPID, 0x1234);
u64 vpid_readback = vmx_vmcs_read(VMCS_VPID);
if (vpid_readback != 0x1234) {
log_error("[VMM] VMCS read/write verify failed");
log_hex64("[VMM] wrote=0x1234 read=", vpid_readback);
return -5;
}
vmx_vmcs_write(VMCS_VPID, 1);
g_vmm_ready = 1;
log_info("[VMM] init ok");
return 0;
}
void vmm_shutdown(void) {
if (!g_vmm_ready) return;
vmx_disable();
g_vmm_ready = 0;
}
int vmm_is_ready(void) { return g_vmm_ready; }
u64 vmm_get_eptp(void) { return ept_get_eptp(); }
int vmm_self_test(void) {
if (!g_vmm_ready) {
log_error("[VMM] not ready");
return -1;
}
log_info("[VMM] self-test begin");
/* 重置状态 */
g_guest_terminated = 0;
g_last_exit_reason = 0xFFFFFFFFULL;
/* 构建 guest GDT + TSS(满足 VMX 对 guest TR 非空的要求) */
build_guest_gdt();
/* EPT 映射 guest 代码页(GPA 0x1000)、栈页(GPA 0x8000)、
* GDT 页(GPA 0x3000)、TSS 页(GPA 0x4000)。
* g_guest_* 是 UTSM 虚拟地址,物理地址 = virt - HHDM */
u64 hhdm = g_hhdm_request.response ? g_hhdm_request.response->offset : 0;
u64 guest_code_phys = (u64)g_guest_code - hhdm;
u64 guest_stack_phys = (u64)g_guest_stack - hhdm;
u64 guest_gdt_phys = (u64)g_guest_gdt - hhdm;
u64 guest_tss_phys = (u64)g_guest_tss - hhdm;
if (ept_map_range(0x1000, guest_code_phys, 4096, EPT_RWX) != 0) {
log_error("[VMM] map guest code failed");
return -3;
}
if (ept_map_range(0x8000, guest_stack_phys, 4096, EPT_RWX) != 0) {
log_error("[VMM] map guest stack failed");
return -4;
}
if (ept_map_range(0x3000, guest_gdt_phys, 4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[VMM] map guest GDT failed");
return -5;
}
if (ept_map_range(0x4000, guest_tss_phys, 4096, EPT_READ | EPT_WRITE) != 0) {
log_error("[VMM] map guest TSS failed");
return -6;
}
/* 配置 VMCS */
vmcs_setup_guest_state_phase1();
vmcs_setup_controls(ept_get_eptp());
vmcs_setup_host_state();
log_info("[VMM] launching guest");
/* vmlaunch 控制流:
* - 失败:执行下一条指令,failed=1
* - 成功:进入 guest,不返回。VM-Exit 后跳到 vmx_vm_exit_handler。
* - guest 终止时,handler 恢复 saved_rsp 并 jmp 到 post_guest 标签。
*
* 我们在 vmlaunch 前保存 RSP 与"vmlaunch 后"的 RIP(= post_guest 标签地址)。 */
int failed;
__asm__ volatile(
"movq %%rsp, g_saved_host_rsp(%%rip)\n\t" /* 保存当前 RSP */
"leaq 1f(%%rip), %%rax\n\t" /* 取 post_guest 标签地址 */
"movq %%rax, g_saved_return_rip(%%rip)\n\t" /* 保存返回 RIP */
"vmlaunch\n\t" /* 启动 guest */
/* ---- 失败路径 ---- */
"movl $1, %0\n\t"
"jmp 2f\n\t"
/* ---- post_guest 标签:终止路径 jmp 到这里 ---- */
"1:\n\t"
"movl $0, %0\n\t"
"2:\n\t"
: "=r"(failed)
:: "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory"
);
if (failed) {
u64 error = vmx_vmcs_read(VMCS_VMX_INSTRUCTION_ERROR);
log_hex64("[VMM] vmlaunch failed, error=", error);
serial_write("[VMM] ");
serial_write(vmx_error_str(error));
serial_putc('\n');
return -7;
}
/* 到达这里:guest 已终止(HLT 被处理) */
log_info("[VMM] guest terminated successfully");
log_hex64("[VMM] last exit reason=", g_last_exit_reason);
log_hex64("[VMM] vmexit count=", vmexit_get_count());
if (g_last_exit_reason != EXIT_HLT) {
log_warn("[VMM] expected HLT exit");
return -8;
}
log_info("[VMM] self-test PASS");
return 0;
}
/* 由 vmexit_asm.S 调用:dispatch 返回 1 = vmresume,返回 0 = 终止。
* 终止时,asm 侧恢复 saved_rsp 并 jmp saved_return_rip。
*
* 注意:vmresume 现在在 vmexit_asm.S 中执行(在恢复 guest GPR 之后),
* 不再在 C 中调用。这样 C 处理器可以修改 g_guest_regs.rax 等,
* 汇编恢复后 vmresume 时 CPU GPR = 修改后的 guest 值。 */
void vmm_vmexit_entry(void) {
int resume = vmexit_dispatch();
g_last_exit_reason = vmx_vmcs_read(VMCS_EXIT_REASON) & 0xFFFF;
if (resume) {
/* 汇编侧会恢复 GPR 并执行 vmresume,无需在此调用。
* 如果 vmresume 失败,汇编会设置 g_guest_terminated=1。 */
} else {
g_guest_terminated = 1;
}
}
/* 供 vmexit_asm.S 读取的全局变量符号已在文件顶部声明为非 static 全局变量。 */
+257
View File
@@ -0,0 +1,257 @@
#include <utsm/vmx.h>
#include <utsm/ept.h>
#include <utsm/log.h>
#include <utsm/types.h>
#include <utsm/dma.h>
#include "../arch/x86_64/limine.h"
extern volatile struct limine_hhdm_request g_hhdm_request;
/* ===== MSR / CR helpers ===== */
u64 vmx_read_msr(u32 msr) {
u32 lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((u64)hi << 32) | lo;
}
static void vmx_write_msr(u32 msr, u64 value) {
u32 lo = (u32)(value & 0xFFFFFFFFULL);
u32 hi = (u32)(value >> 32);
__asm__ volatile("wrmsr" :: "a"(lo), "d"(hi), "c"(msr));
}
static inline u64 read_cr0(void) {
u64 v;
__asm__ volatile("mov %%cr0, %0" : "=r"(v));
return v;
}
static inline u64 read_cr4(void) {
u64 v;
__asm__ volatile("mov %%cr4, %0" : "=r"(v));
return v;
}
static inline void write_cr4(u64 v) {
__asm__ volatile("mov %0, %%cr4" :: "r"(v));
}
static inline u64 read_cr3(void) {
u64 v;
__asm__ volatile("mov %%cr3, %0" : "=r"(v));
return v;
}
static inline u64 read_rflags(void) {
u64 v;
__asm__ volatile("pushfq; popq %0" : "=r"(v));
return v;
}
/* ===== CPUID ===== */
static void cpuid(u32 leaf, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) {
__asm__ volatile("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(leaf));
}
int vmx_supported(void) {
u32 eax, ebx, ecx, edx;
cpuid(1, &eax, &ebx, &ecx, &edx);
return (ecx & (1U << 5)) != 0; /* CPUID.1:ECX[5] = VMX */
}
/* ===== VMXON region ===== */
static u64 g_vmxon_phys;
static u64 g_vmcs_revision;
static u64 g_vmx_basic;
static int g_vmx_enabled;
/* VMX 要求 CR0 固定位必须为 1(VMX operation 不允许清除)。
* 这些位由 IA32_VMX_BASIC MSR 的高 32 位(CR0 fixed-0)和
* IA32_VMX_CR0_FIXED1 MSR 决定。简化:直接 OR 上 PE/NE/PG/WP。 */
static u64 vmx_required_cr0(void) {
return read_cr0() | CR0_PE | CR0_NE | CR0_WP | CR0_PG;
}
static u64 vmx_required_cr4(void) {
return read_cr4() | CR4_VMXE | CR4_PAE | CR4_PGE;
}
int vmx_enable(void) {
if (g_vmx_enabled) return 0;
if (!vmx_supported()) {
log_error("[VMX] CPU does not support VMX");
return -1;
}
/* 读 IA32_VMX_BASIC,获取 VMCS revision ID 与 region 大小 */
g_vmx_basic = vmx_read_msr(IA32_VMX_BASIC);
g_vmcs_revision = g_vmx_basic & 0x7FFFFFFFULL;
u32 vmcs_size = (u32)((g_vmx_basic >> 32) & 0x1FFF);
if (vmcs_size == 0) vmcs_size = 4096;
log_hex64("[VMX] VMX_BASIC=", g_vmx_basic);
log_hex64("[VMX] VMCS revision=", g_vmcs_revision);
/* 分配 VMXON region(4KB 对齐,物理页) */
dkm_dma_buffer vmxon_buf;
if (dma_alloc_pages(1, EPT_PAGE_SIZE, 0x100000000ULL, &vmxon_buf) != 0) {
log_error("[VMX] failed to alloc VMXON region");
return -2;
}
g_vmxon_phys = vmxon_buf.phys;
/* 在 VMXON region 偏移 0 写入 VMCS revision ID */
u32 *vmxon_ptr = (u32 *)vmxon_buf.virt;
vmxon_ptr[0] = (u32)g_vmcs_revision;
log_hex64("[VMX] VMXON phys=", g_vmxon_phys);
/* 设置 CR4.VMXE */
u64 cr4 = vmx_required_cr4();
write_cr4(cr4);
log_hex64("[VMX] CR4=", cr4);
/* 确保 CR0 满足 VMX 要求(这里只读不写,UEFI 已配置好) */
(void)vmx_required_cr0();
/* 执行 vmxon */
int err;
__asm__ volatile(
"vmxon %1\n"
"jnc 1f\n"
"jmp 2f\n"
"1:\n"
"mov $0, %0\n"
"jmp 3f\n"
"2:\n"
"mov $1, %0\n"
"3:\n"
: "=r"(err)
: "m"(g_vmxon_phys)
: "memory"
);
if (err) {
/* 第二次尝试:读 vmx instruction error 通过 VMCS 不适用 vmxon;
* 简化:返回失败。 */
log_error("[VMX] vmxon failed");
return -3;
}
g_vmx_enabled = 1;
log_info("[VMX] VMX root mode enabled");
return 0;
}
int vmx_disable(void) {
if (!g_vmx_enabled) return 0;
__asm__ volatile("vmxoff" ::: "memory");
g_vmx_enabled = 0;
/* 清 CR4.VMXE */
u64 cr4 = read_cr4();
cr4 &= ~CR4_VMXE;
write_cr4(cr4);
log_info("[VMX] VMX root mode disabled");
return 0;
}
int vmx_vmcs_alloc(u64 *phys_out) {
if (!phys_out) return -1;
dkm_dma_buffer buf;
if (dma_alloc_pages(1, EPT_PAGE_SIZE, 0x100000000ULL, &buf) != 0) {
log_error("[VMX] failed to alloc VMCS region");
return -2;
}
/* 偏移 0 写入 VMCS revision ID */
u32 *p = (u32 *)buf.virt;
p[0] = (u32)g_vmcs_revision;
*phys_out = buf.phys;
return 0;
}
int vmx_vmcs_load(u64 phys) {
int err;
__asm__ volatile(
"vmptrld %1\n"
"jnc 1f\n"
"jmp 2f\n"
"1:\n"
"mov $0, %0\n"
"jmp 3f\n"
"2:\n"
"mov $1, %0\n"
"3:\n"
: "=r"(err)
: "m"(phys)
: "memory"
);
if (err) {
log_error("[VMX] vmptrld failed");
return -1;
}
return 0;
}
u64 vmx_vmcs_read(u64 field) {
u64 value;
__asm__ volatile(
"vmread %1, %0\n"
: "=r"(value)
: "r"(field)
: "memory"
);
return value;
}
void vmx_vmcs_write(u64 field, u64 value) {
__asm__ volatile(
"vmwrite %1, %0\n"
:: "r"(field), "r"(value)
: "memory"
);
}
int vmx_vmlaunch(void) {
int err;
__asm__ volatile(
"vmlaunch\n"
"jnc 1f\n"
"jmp 2f\n"
"1:\n"
"mov $0, %0\n"
"jmp 3f\n"
"2:\n"
"mov $1, %0\n"
"3:\n"
: "=r"(err)
:: "memory"
);
return err;
}
int vmx_vmresume(void) {
int err;
__asm__ volatile(
"vmresume\n"
"jnc 1f\n"
"jmp 2f\n"
"1:\n"
"mov $0, %0\n"
"jmp 3f\n"
"2:\n"
"mov $1, %0\n"
"3:\n"
: "=r"(err)
:: "memory"
);
return err;
}
/* 暴露给其它模块用的辅助 */
u64 vmx_get_host_cr3(void) { return read_cr3(); }
u64 vmx_get_host_rflags(void) { return read_rflags(); }
+25
View File
@@ -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 }"
+24
View File
@@ -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
}
+1396
View File
@@ -0,0 +1,1396 @@
/* 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"
#include "../UTSM/pe/pe_loader.h" /* PE 头结构体(image_dos_header 等),供 peinfo 解析 */
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]);
}
}
/* 64 位十六进制输出(前导零抑制),用于 PE ImageBase 等宽字段 */
static void term_putx64(u64 v) {
static const char hex[] = "0123456789ABCDEF";
term_putc('0'); term_putc('x');
int started = 0;
for (int i = 60; i >= 0; i -= 4) {
u64 nib = (v >> i) & 0xF;
if (nib) started = 1;
if (started || i == 0) 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 <file> 显示文件内容\r\n");
term_puts(" copy <a> <b> 复制文件\r\n");
term_puts(" del <file> 删除文件\r\n");
term_puts(" ren <a> <b> 重命名文件\r\n");
term_puts(" echo <text> 显示文本 (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(" <name>.exe 运行 PE32+/PE32 程序 (如 hello.exe)\r\n");
term_puts(" <name> 自动查找 name.exe 并运行\r\n");
term_puts(" pe <name> 显式运行 PE 程序 (如 pe hello64)\r\n");
term_puts(" peinfo <nm> 显示 PE 头信息 (不执行)\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(" <DIR> ", 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 <file> — 显示文件内容 */
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 <text> — 显示文本或重定向到文件 */
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 <src> <dst> — 复制文件 */
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 <file> — 删除文件 */
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 <old> <new> — 重命名文件 */
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 int cmd_read_pe_from_bin(const char *name11, u8 **data, u32 *size); /* 前向声明 */
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;
}
/* PATH 查找:先根目录,再 /bin/ */
u8 *pe_data = 0; u32 pe_size = 0;
int rc = f32_read_root_file(name11, &pe_data, &pe_size);
if (rc != 0) {
rc = cmd_read_pe_from_bin(name11, &pe_data, &pe_size);
}
if (rc != 0) {
term_puts_color("'", CMD_ERROR);
term_puts_color(prog_name, CMD_ERROR);
term_puts_color("' 不是内部或外部命令,也不是可运行的程序。\r\n", CMD_ERROR);
term_puts_color(" (已搜索 C:\\ 及 C:\\bin\\)\r\n", CMD_DIM);
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;
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");
}
/* ============================================================
* /bin 子目录支持 — 用于查找 PE 可执行程序
* ============================================================ */
/* 在根目录簇链中查找名为 name11 的子目录(attr & 0x10),返回首簇号。
* 使用 f32_disk 缓存(来自 fat32_io.h)。返回 0 成功,-1 未找到。 */
static int cmd_find_subdir(u32 dir_clus, const char *name11, u32 *out_clus) {
if (!f32_disk_loaded && f32_disk_load() != 0) return -2;
const f32_bpb *bpb = (const f32_bpb *)f32_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 <= sizeof(f32_disk))
cb = f32_disk + (u64)lba * 512;
else {
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
cb = f32_cluster;
}
const f32_dirent *dir = (const f32_dirent *)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 (f32_neq11(dir[e].name, name11)) {
*out_clus = f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
return 0;
}
}
u32 fo = fat_byte_off + clus * 4;
if (fo + 4 > sizeof(f32_disk)) break;
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
done_dir:
return -1;
}
/* 从指定目录首簇读取文件到 f32_data。
* dir_clus: 目录起始簇号。name11: 8.3 名。
* 返回 0 成功,*out_data 指向 f32_data,*out_size 为字节数;非 0 失败。 */
static int cmd_read_file_in_dir(u32 dir_clus, const char *name11, u8 **out_data, u32 *out_size) {
if (!f32_disk_loaded && f32_disk_load() != 0) return -1;
const f32_bpb *bpb = (const f32_bpb *)f32_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;
u32 found_clus = 0, found_size = 0;
int found = 0;
while (clus >= 2 && clus < 0x0FFFFFF8 && !found) {
u32 lba = data_lba + (clus - 2) * spc;
const u8 *cb;
if ((u64)lba * 512 + (u64)spc * 512 <= sizeof(f32_disk))
cb = f32_disk + (u64)lba * 512;
else {
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
cb = f32_cluster;
}
const f32_dirent *dir = (const f32_dirent *)cb;
for (u32 e = 0; e * 32 < spc * 512; e++) {
if (dir[e].name[0] == 0) goto done_find;
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 (f32_neq11(dir[e].name, name11)) {
found_clus = f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
found_size = dir[e].fsize;
found = 1;
break;
}
}
u32 fo = fat_byte_off + clus * 4;
if (fo + 4 > sizeof(f32_disk)) break;
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
done_find:
if (!found) return -5;
if (found_size > sizeof(f32_data)) return -6;
/* 读取文件数据 */
u8 *dst = f32_data; u32 remaining = found_size; u32 fc = found_clus;
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
u32 fc_lba = data_lba + (fc - 2) * spc;
u32 fc_bytes = spc * 512;
if (fc_bytes > remaining) fc_bytes = remaining;
const u8 *fb;
if ((u64)fc_lba * 512 + (u64)spc * 512 <= sizeof(f32_disk))
fb = f32_disk + (u64)fc_lba * 512;
else {
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
fb = f32_cluster;
}
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
dst += fc_bytes; remaining -= fc_bytes;
u32 fo = fat_byte_off + fc * 4;
if (fo + 4 > sizeof(f32_disk)) break;
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
*out_data = f32_data; *out_size = found_size;
return 0;
}
static u32 g_cmd_bin_clus = 0;
static int g_cmd_bin_checked = 0;
/* 获取 /bin 目录首簇号。返回 0 成功,-1 未找到。 */
static int cmd_get_bin_cluster(u32 *out_clus) {
if (g_cmd_bin_checked) {
if (g_cmd_bin_clus == 0) return -1;
*out_clus = g_cmd_bin_clus;
return 0;
}
g_cmd_bin_checked = 1;
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
char bin83[11] = {'B','I','N',' ',' ',' ',' ',' ',' ',' ',' '};
if (cmd_find_subdir(bpb->root_clus, bin83, &g_cmd_bin_clus) != 0) {
g_cmd_bin_clus = 0;
return -1;
}
*out_clus = g_cmd_bin_clus;
return 0;
}
/* 从 /bin 子目录读取 PE 文件。返回 0 成功。 */
static int cmd_read_pe_from_bin(const char *name11, u8 **data, u32 *size) {
u32 bin_clus;
if (cmd_get_bin_cluster(&bin_clus) != 0) return -1;
return cmd_read_file_in_dir(bin_clus, name11, data, size);
}
/* ============================================================
* peinfo — 解析并显示 PE 头信息(不执行)
* ============================================================ */
/* 把程序名转为 8.3 名(自动补 .exe),返回 0 成功,-1 失败。
* 内联自 exec_pe 的同名逻辑,供 cmd_peinfo 复用。 */
static int make_pe_name83(const char *prog, char name11[11]) {
char base[64];
str_copy(base, prog, sizeof(base));
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;
}
return f32_name_to_83(base, name11);
}
static void cmd_peinfo(const char *args) {
if (!*args) {
term_puts_color("用法: peinfo <程序名> (如 peinfo hello64)\r\n", CMD_INFO);
return;
}
if (!f32_blk_read) {
term_puts_color("peinfo: 无块设备\r\n", CMD_ERROR);
return;
}
char name11[11];
if (make_pe_name83(args, name11) != 0) {
term_puts_color("peinfo: 无效文件名\r\n", CMD_ERROR);
return;
}
/* PATH 查找:先根目录,再 /bin/ */
u8 *pe = 0; u32 pe_size = 0;
int rc = f32_read_root_file(name11, &pe, &pe_size);
if (rc != 0) {
rc = cmd_read_pe_from_bin(name11, &pe, &pe_size);
}
if (rc != 0) {
term_puts_color("peinfo: 文件不存在: ", CMD_ERROR);
term_puts_color(args, CMD_ERROR);
term_puts_color(" (已搜索 C:\\ 及 C:\\bin\\)\r\n", CMD_ERROR);
return;
}
/* ---- DOS 头 ---- */
if (pe_size < sizeof(image_dos_header)) {
term_puts_color("peinfo: 文件过小,无 DOS 头\r\n", CMD_ERROR);
return;
}
const image_dos_header *dos = (const image_dos_header *)pe;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
term_puts_color("peinfo: 缺少 MZ 签名\r\n", CMD_ERROR);
return;
}
u32 lfanew = dos->e_lfanew;
if ((u64)lfanew + 4 + sizeof(image_file_header) > pe_size) {
term_puts_color("peinfo: e_lfanew 越界\r\n", CMD_ERROR);
return;
}
term_puts_color("=== PE 头信息 ===\r\n", CMD_INFO);
term_puts(" DOS: MZ OK, e_lfanew=");
term_putx(lfanew);
term_puts("\r\n");
/* ---- NT 签名 + File Header ---- */
const u8 *nt = pe + lfanew;
if (*(const u32 *)nt != IMAGE_NT_SIGNATURE) {
term_puts_color("peinfo: 缺少 PE\\0\\0 签名\r\n", CMD_ERROR);
return;
}
const image_file_header *fh = (const image_file_header *)(nt + 4);
u16 machine = fh->machine;
u16 nsec = fh->number_of_sections;
u16 opt_size = fh->size_of_optional_header;
term_puts(" NT: PE\\0\\0 OK\r\n");
term_puts(" Machine: ");
term_putx(machine);
if (machine == 0x8664) term_puts_color(" (AMD64)\r\n", CMD_OK);
else if (machine == 0x014c) term_puts_color(" (i386)\r\n", CMD_OK);
else term_puts(" (未知)\r\n");
term_puts(" 节区数: ");
term_putu(nsec);
term_puts(" OptHdr 大小: ");
term_putu(opt_size);
term_puts("\r\n");
/* ---- Optional Header ---- */
if (opt_size < 2) {
term_puts_color("peinfo: Optional Header 过小\r\n", CMD_WARN);
return;
}
if ((u64)lfanew + 4 + sizeof(image_file_header) + opt_size > pe_size) {
term_puts_color("peinfo: Optional Header 越界\r\n", CMD_ERROR);
return;
}
const void *opt = (const void *)(nt + 4 + sizeof(image_file_header));
u16 magic = *(const u16 *)opt;
int is_pe32_plus = (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC);
u64 img_base = 0;
u32 entry_rva = 0;
u32 img_size = 0;
u32 subsys = 0;
const image_data_directory *import_dir = 0;
if (is_pe32_plus) {
if (opt_size >= sizeof(image_optional_header64)) {
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
img_base = oh->image_base;
entry_rva = oh->address_of_entry_point;
img_size = oh->size_of_image;
subsys = oh->subsystem;
import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT];
}
term_puts_color(" 格式: PE32+ (64位 原生)\r\n", CMD_OK);
} else if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
if (opt_size >= sizeof(image_optional_header32)) {
const image_optional_header32 *oh = (const image_optional_header32 *)opt;
img_base = oh->image_base;
entry_rva = oh->address_of_entry_point;
img_size = oh->size_of_image;
subsys = oh->subsystem;
import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT];
}
term_puts_color(" 格式: PE32 (32位 解释器)\r\n", CMD_OK);
} else {
term_puts_color("peinfo: 未知 Optional Header magic=", CMD_ERROR);
term_putx(magic);
term_puts("\r\n");
return;
}
term_puts(" ImageBase: ");
term_putx64(img_base);
term_puts("\r\n");
term_puts(" EntryPoint: ");
term_putx(entry_rva);
term_puts(" (RVA)\r\n");
term_puts(" SizeOfImage: ");
term_putx(img_size);
term_puts(" (");
term_putu(img_size);
term_puts(" bytes)\r\n");
term_puts(" Subsystem: ");
term_putu(subsys);
term_puts(subsys == 3 ? " (CUI 控制台)\r\n" : "\r\n");
/* ---- 节区表 ---- */
const image_section_header *secs = (const image_section_header *)
((const u8 *)opt + opt_size);
if ((u64)((const u8 *)secs - pe) + (u64)nsec * sizeof(image_section_header) > pe_size) {
term_puts_color("peinfo: 节区表越界,跳过\r\n", CMD_WARN);
nsec = 0;
}
if (nsec > 0) {
term_puts_color(" 节区表:\r\n", CMD_INFO);
term_puts(" Name VAddr VSize RawSize Flg\r\n");
for (int i = 0; i < nsec && i < 96; i++) {
char nm[9];
for (int j = 0; j < 8; j++) nm[j] = (char)secs[i].name[j];
nm[8] = 0;
term_puts(" ");
term_puts(nm);
int pad = 8 - str_len(nm);
for (int k = 0; k < pad; k++) term_putc(' ');
term_puts(" ");
term_putx(secs[i].virtual_address);
term_puts(" ");
term_putx(secs[i].virtual_size);
term_puts(" ");
term_putx(secs[i].size_of_raw_data);
term_puts(" ");
char flags[4];
flags[0] = (secs[i].characteristics & IMAGE_SCN_MEM_EXECUTE) ? 'X' : '-';
flags[1] = (secs[i].characteristics & IMAGE_SCN_MEM_READ) ? 'R' : '-';
flags[2] = (secs[i].characteristics & IMAGE_SCN_MEM_WRITE) ? 'W' : '-';
flags[3] = 0;
term_puts_color(flags, CMD_WARN);
term_puts("\r\n");
}
}
/* ---- 导入表 ---- */
if (import_dir && import_dir->virtual_address != 0 && import_dir->size != 0) {
u32 imp_rva = import_dir->virtual_address;
if (imp_rva >= pe_size) {
term_puts_color("peinfo: 导入表 RVA 越界\r\n", CMD_WARN);
} else {
term_puts_color(" 导入表:\r\n", CMD_INFO);
int dll_count = 0;
int func_total = 0;
int truncated = 0;
for (int di = 0; !truncated; di++) {
u32 desc_off = imp_rva + (u32)di * sizeof(image_import_descriptor);
if ((u64)desc_off + sizeof(image_import_descriptor) > pe_size) break;
const image_import_descriptor *desc =
(const image_import_descriptor *)(pe + desc_off);
if (desc->name == 0 && desc->first_thunk == 0) break;
const char *dll_name = 0;
if (desc->name < pe_size) dll_name = (const char *)(pe + desc->name);
term_puts(" DLL: ");
term_puts_color(dll_name ? dll_name : "(?)", CMD_WARN);
term_puts("\r\n");
u32 int_rva = desc->original_first_thunk;
u32 iat_rva = desc->first_thunk;
if (int_rva == 0) int_rva = iat_rva;
if (int_rva == 0) { dll_count++; continue; }
for (int fi = 0; ; fi++) {
u32 thunk_off = int_rva + (u32)fi * 4;
if ((u64)thunk_off + 4 > pe_size) break;
u32 thunk = *(const u32 *)(pe + thunk_off);
if (thunk == 0) break;
term_puts(" ");
if (thunk & 0x80000000u) {
term_puts("#ordinal ");
term_putu(thunk & 0xFFFF);
} else if (thunk + 2 < pe_size) {
const char *fn = (const char *)(pe + thunk + 2);
term_puts(fn);
} else {
term_puts("(?)");
}
term_puts("\r\n");
func_total++;
if (func_total > 64) {
term_puts_color(" ... (导入过多,截断)\r\n", CMD_DIM);
truncated = 1;
break;
}
}
dll_count++;
if (dll_count > 32) {
term_puts_color(" ... (DLL 过多,截断)\r\n", CMD_DIM);
break;
}
}
term_puts(" 共 ");
term_putu(dll_count);
term_puts(" 个 DLL, ");
term_putu(func_total);
term_puts(" 个导入函数\r\n");
}
} else {
term_puts_color(" 导入表: 无\r\n", CMD_DIM);
}
term_puts_color("=== PE 头信息结束 ===\r\n", CMD_INFO);
}
/* ============================================================
* pe — 显式运行 PE 程序(等价于直接输入名字,但可在 help 中列出)
* ============================================================ */
static void cmd_pe(const char *args) {
if (!*args) {
term_puts_color("用法: pe <程序名> (如 pe hello64)\r\n", CMD_INFO);
return;
}
exec_pe(args, args);
}
/* ============================================================
* 命令解析与分发
* ============================================================ */
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, "pe")) { cmd_pe(args); return; }
if (str_ieq(name, "peinfo")) { cmd_peinfo(args); 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");
}
+3 -1
View File
@@ -17,8 +17,10 @@ SECTIONS
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
.bss : ALIGN(4K) {
*(.bss*)
} :data
}
+2346 -622
View File
@@ -1,20 +1,37 @@
/* Deshab Desktop Manager — 图形桌面环境
/* Deshab Desktop Manager — Winux-Kate 结构完整复刻
*
* 功能:窗口管理、任务栏、桌面图标、事件路由、应用加载
* 风格:Sealed Arc 视觉设计语言
* 输入:PS/2 键盘 + PS/2 鼠标
* 布局结构与 D:\Code\Winux-Kate 完全一致:
* app-shell
* ├── topbar (34px):品牌 + 工作区切换 + 页面切换 + PG 时钟
* page-host
* │ ├── 1·DASHBOARD:TERM-01 / TERM-02 / EDITOR / FILES 四面板 + StatusBar
* │ ├── 2·IDE:标签栏 + 编辑宿主
* │ ├── 3·DESKTOP:图标网格 + 窗口 + 任务栏
* │ └── 4+/CUSTOM:外部 .elf 全屏宿主(双击启动)
* └── task-view overlay(工作区卡片)
*
* 新 UI 特性(Kate 之外):
* - RTC 实时时钟(秒级)+ 开机至今 UP 计时
* - VOL / BRIGHT 可拖滑块(BRIGHT 真实调暗全屏)
* - 开机打字机 Boot 屏
* - FILES 面板双击文件载入 EDITOR(FAT32 真实读写 + SAVE 回写)
* - 窗口单实例聚焦策略
*/
#include "../UTSM/include/utsm/dsk.h"
/* block_read 函数类型(从 kernel_api + 0xA8 获取) */
typedef int (*desktop_block_read_fn)(void *ctx, unsigned long long lba, unsigned int count, void *buffer);
/* block 设备函数类型(从 kernel_api + 0xA8 获取 block_api,read @ +0x10, write @ +0x18) */
typedef int (*desktop_block_read_fn)(u32 index, u64 lba, u32 count, void *buffer);
typedef int (*desktop_block_write_fn)(u32 index, u64 lba, u32 count, const void *buffer);
/* 先包含 ascii_bitmaps.c(定义 g_ascii),再包含 deshab_ui.h(引用 g_ascii) */
#include "../firstInit/ascii_bitmaps.c"
#include "../UTSM/include/utsm/deshab_ui.h"
/* 用户态 FAT32 读写 */
#include "../tools/fat32_io.h"
typedef unsigned char u8;
typedef signed char i8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
@@ -32,24 +49,57 @@ static void sputc(char c) {
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
static void slog(const char *s) { swrite("[desktop] "); swrite(s); swrite("\n"); }
static int kstrlen(const char *s) { int n = 0; while (s[n]) n++; return n; }
static void kstrcpy(char *d, const char *s, int cap) {
int i = 0;
for (; i < cap - 1 && s[i]; i++) d[i] = s[i];
d[i] = 0;
}
static void kstrcat(char *d, const char *s, int cap) {
int n = kstrlen(d);
if (n < cap) kstrcpy(d + n, s, cap - n);
}
static u64 rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
/* 前向声明 */
static void redraw_all(void);
static int ps2_mouse_poll(void);
static void draw_statusbar_full(void);
static void files_refresh(void);
/* ============================================================
* 常量
* 常量(Winux-Kate 布局)
* ============================================================ */
#define MAX_WINDOWS 8
#define TASKBAR_H 40
#define TITLEBAR_H 28
#define BORDER_W 2
#define DESKTOP_ICON_W 80
#define DESKTOP_ICON_H 72
#define ICON_GUTTER_X 16
#define ICON_GUTTER_Y 16
#define BORDER_W 1
#define CURSOR_SIZE 24
#define ICON_W KATE_ICON_W /* 104 */
#define ICON_H KATE_ICON_H /* 90 */
#define ICON_GAP KATE_ICON_GAP /* 18 */
#define ICON_PAD KATE_ICON_PAD /* 24 */
#define CLOSE_BTN_SIZE 16
#define CLOSE_BTN_OFFSET 22
#define MAX_WS 6 /* 工作区上限 */
#define MAX_CPAGES 4 /* 自定义页上限 */
#define MAX_FILES 24 /* FILES 面板条目上限 */
/* 应用固定状态地址(单实例策略 → 每类应用唯一实例) */
#define ADDR_TERM1 0x4000000ULL
#define ADDR_TERM2 0x4100000ULL
#define ADDR_TERMWIN 0x4200000ULL
#define ADDR_EDDASH 0x5000000ULL
#define ADDR_EDWIN 0x5100000ULL
#define ADDR_CALC 0x6000000ULL
/* ============================================================
* 事件系统
* ============================================================ */
@@ -64,7 +114,7 @@ typedef enum {
typedef struct {
ev_type type;
int mx, my; /* 鼠标全局坐标 */
int mx, my;
int button; /* 0=左, 1=右 */
u8 scancode;
int shift, ctrl, alt;
@@ -81,18 +131,13 @@ typedef struct desktop_window {
int focused;
int minimized;
char title[48];
int app_id; /* 关联应用 ID */
void *app_state; /* 应用私有状态 */
int closing; /* 关闭中标记 */
int dirty; /* 需要重绘 */
int app_id;
void *app_state;
int closing;
int dirty;
int ws; /* 所属工作区 */
} desktop_window;
/* ============================================================
* 应用描述符
* ============================================================ */
/* 前向声明 */
struct desktop_window;
typedef struct desktop_window desktop_win;
typedef struct app_ctx {
@@ -101,6 +146,7 @@ typedef struct app_ctx {
int client_x, client_y, client_w, client_h;
int should_exit;
desktop_block_read_fn block_read;
desktop_block_write_fn block_write;
const void *kernel_api;
} app_ctx;
@@ -117,8 +163,39 @@ typedef struct {
app_event_fn on_event;
app_draw_fn on_draw;
app_destroy_fn on_destroy;
const char *elf_name;
} app_descriptor;
/* 桌面图标 */
typedef struct {
int app_id;
int x, y;
const char *label;
int selected;
} desktop_icon;
#define MAX_ICONS 8
/* 工作区 */
typedef struct {
char name[16];
int page; /* 该工作区离开时的页面 */
} workspace;
/* 自定义页 */
typedef struct {
char name[12];
char elf11[11];
int id; /* 页面号 5..8 */
} cpage;
/* FILES 面板条目 */
typedef struct {
char name11[11];
char disp[13];
u32 size;
} fentry;
/* ============================================================
* 全局状态
* ============================================================ */
@@ -126,11 +203,18 @@ 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;
/* 鼠标状态 */
static int g_mouse_x = 400, g_mouse_y = 300;
static int g_mouse_btn = 0; /* bit0=左键 */
static int g_mouse_btn = 0; /* bit0=左 bit1=右 */
static int g_left_pressed = 0, g_left_released = 0, g_right_pressed = 0;
static u8 g_mouse_buf[3];
static int g_mouse_idx = 0;
static int g_mouse_has_pkt = 0;
@@ -160,17 +244,137 @@ static int g_drag_off_x, g_drag_off_y;
static app_descriptor g_apps[MAX_APPS];
static int g_app_count = 0;
/* 桌面图标 */
typedef struct {
int app_id;
int x, y; /* 图标位置 */
const char *label;
} desktop_icon;
#define MAX_ICONS 8
static desktop_icon g_icons[MAX_ICONS];
static int g_icon_count = 0;
/* ---- Kate 外壳状态 ---- */
static int g_booted = 0;
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 */
static workspace g_ws[MAX_WS];
static int g_ws_count = 1;
static int g_ws_cur = 0;
static cpage g_cpages[MAX_CPAGES];
static int g_cpage_count = 0;
/* 右键菜单(移到工作区) */
static int g_ctx_open = 0;
static int g_ctx_x = 0, g_ctx_y = 0;
static int g_ctx_win = -1;
/* 状态栏滑块 */
static int g_vol = 65;
static int g_bright = 100;
static int g_bright_applied = 100; /* 上次应用的亮度值(避免重复处理) */
static int g_drag_slider = 0; /* 1=vol 2=bright */
static int g_vol_rect[4]; /* x,y,w,h 滑轨 */
static int g_bri_rect[4];
static int g_quit = 0; /* EXIT 按钮 → 返回 DSK */
/* 时钟 */
static int g_rtc_boot_sec = 0; /* 开机时刻(秒级当天秒) */
static int g_last_sec = -1;
static u64 g_tsc_per_sec = 0;
/* FILES 面板 */
static fentry g_files[MAX_FILES];
static int g_file_count = 0;
static int g_file_sel = -1;
static int g_files_loaded = 0;
/* 双击检测 */
static u64 g_last_click_tsc = 0;
static int g_last_click_x = -100, g_last_click_y = -100;
/* 面板/控件命中矩形(绘制时更新,点击时使用) */
static int g_dash_rect[4][4]; /* 4 面板 x,y,w,h */
static int g_save_rect[4];
static int g_pg_rect[8][4]; /* 页面切换按钮 */
static int g_pg_rm_rect[8][4];
static int g_pgadd_rect[4];
static int g_tvbtn_rect[4]; /* 任务视图按钮 */
static int g_ws_rect[MAX_WS][4];
static int g_wsadd_rect[4];
static int g_tb_rect[MAX_WINDOWS][4]; /* 任务栏项 */
static int g_tb_close_rect[MAX_WINDOWS][4];
static int g_tb_quit_rect[4];
static int g_tb_adopt_rect[4]; /* 收纳窗口按钮 */
static int g_tv_card[MAX_WS + 1][4]; /* 任务视图卡片(+add) */
static int g_tv_close[MAX_WS][4];
static int g_ctx_rect[4];
static int g_ctx_item[MAX_WS + 1][4];
static int g_ctx_item_ws[MAX_WS + 1]; /* 菜单行 → 真实工作区索引,-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_cp_host[4]; /* 自定义页宿主 */
/* ============================================================
* 脏矩形渲染(性能优化:只更新变化区域)
* ============================================================ */
#define MAX_DIRTY_RECTS 16
typedef struct {
int x, y, w, h;
int active;
} dirty_rect;
static dirty_rect g_dirty_rects[MAX_DIRTY_RECTS];
static int g_dirty_count = 0;
static int g_full_redraw = 1; /* 初始全屏重绘 */
/* 标记脏区域 */
__attribute__((unused))
static void mark_dirty(int x, int y, int w, int h) {
if (g_dirty_count >= MAX_DIRTY_RECTS) {
/* 溢出时合并为全屏重绘 */
g_full_redraw = 1;
return;
}
g_dirty_rects[g_dirty_count].x = x;
g_dirty_rects[g_dirty_count].y = y;
g_dirty_rects[g_dirty_count].w = w;
g_dirty_rects[g_dirty_count].h = h;
g_dirty_rects[g_dirty_count].active = 1;
g_dirty_count++;
}
/* 合并所有脏矩形为一个包围盒 */
static void merge_dirty_rects(int *out_x, int *out_y, int *out_w, int *out_h) {
if (g_dirty_count == 0) {
*out_x = *out_y = *out_w = *out_h = 0;
return;
}
int min_x = (int)g_fb_w, min_y = (int)g_fb_h;
int max_x = 0, max_y = 0;
for (int i = 0; i < g_dirty_count; i++) {
if (!g_dirty_rects[i].active) continue;
int x1 = g_dirty_rects[i].x;
int y1 = g_dirty_rects[i].y;
int x2 = x1 + g_dirty_rects[i].w;
int y2 = y1 + g_dirty_rects[i].h;
if (x1 < min_x) min_x = x1;
if (y1 < min_y) min_y = y1;
if (x2 > max_x) max_x = x2;
if (y2 > max_y) max_y = y2;
}
*out_x = min_x;
*out_y = min_y;
*out_w = max_x - min_x;
*out_h = max_y - min_y;
}
/* 清除所有脏矩形 */
static void clear_dirty_rects(void) {
g_dirty_count = 0;
g_full_redraw = 0;
}
/* ============================================================
* 箭头光标形状(24×24,1=前景 0=透明)
* ============================================================ */
@@ -203,70 +407,130 @@ static const u8 cursor_shape[24][24] = {
};
/* ============================================================
* 应用图标形状(32×32 像素数据,用程序化方式生成)
* RTC 时钟
* ============================================================ */
/* 绘制终端图标(32×32)到指定位置 */
static int bcd2bin(u8 v) { return (v >> 4) * 10 + (v & 0xF); }
static void rtc_read(int *yy, int *mo, int *dd, int *hh, int *mi, int *ss) {
outb(0x70, 0x00); *ss = bcd2bin(inb(0x71));
outb(0x70, 0x02); *mi = bcd2bin(inb(0x71));
outb(0x70, 0x04); *hh = bcd2bin(inb(0x71));
outb(0x70, 0x07); *dd = bcd2bin(inb(0x71));
outb(0x70, 0x08); *mo = bcd2bin(inb(0x71));
outb(0x70, 0x09); *yy = bcd2bin(inb(0x71));
}
static int rtc_day_sec(void) {
int y, mo, d, h, mi, s;
rtc_read(&y, &mo, &d, &h, &mi, &s);
return h * 3600 + mi * 60 + s;
}
/* 格式化 HH:MM:SS */
static void fmt_hms(char *out, int sec) {
int h = sec / 3600, m = (sec / 60) % 60, s = sec % 60;
out[0] = '0' + h / 10; out[1] = '0' + h % 10; out[2] = ':';
out[3] = '0' + m / 10; out[4] = '0' + m % 10; out[5] = ':';
out[6] = '0' + s / 10; out[7] = '0' + s % 10; out[8] = 0;
}
/* 校准 TSC(约 1 秒):用于双击间隔判定 */
static void tsc_calibrate(void) {
int s0 = rtc_day_sec();
while (rtc_day_sec() == s0) __asm__("pause");
u64 t0 = rdtsc();
int s1 = rtc_day_sec();
while (rtc_day_sec() == s1) __asm__("pause");
g_tsc_per_sec = rdtsc() - t0;
}
/* ============================================================
* Winux-Kate 应用图标(40×40,青色几何图形)
* ============================================================ */
static void draw_circle_outline(du_context *ctx, int cx, int cy, int r, u32 color) {
for (int dy = -r; dy <= r; dy++) {
for (int dx = -r; dx <= r; dx++) {
int d2 = dx * dx + dy * dy;
if (d2 <= r * r && d2 > (r - 1) * (r - 1)) {
du_pixel(ctx, cx + dx, cy + dy, color);
}
}
}
}
static void draw_icon_terminal(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_300;
u32 border = DP_SEAL_500;
/* 圆角矩形背景 */
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* >_ 符号 */
du_fill_rect(ctx, cx+5, cy+10, 8, 2, fg);
du_fill_rect(ctx, cx+5, cy+10, 2, 6, fg);
du_fill_rect(ctx, cx+13, cy+12, 2, 2, fg);
du_fill_rect(ctx, cx+17, cy+16, 6, 2, fg);
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
du_fill_rect(ctx, cx + 8, cy + 12, 2, 2, KS_ACCENT2);
du_fill_rect(ctx, cx + 10, cy + 14, 2, 2, KS_ACCENT2);
du_fill_rect(ctx, cx + 12, cy + 16, 2, 2, KS_ACCENT2);
du_fill_rect(ctx, cx + 10, cy + 18, 2, 2, KS_ACCENT2);
du_fill_rect(ctx, cx + 8, cy + 20, 2, 2, KS_ACCENT2);
du_fill_rect(ctx, cx + 16, cy + 22, 8, 2, KS_ACCENT);
}
/* 绘制编辑器图标(32×32) */
static void draw_icon_editor(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_SEAL_500;
u32 border = DP_SEAL_300;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文本行 */
for (int i = 0; i < 5; i++) {
int lw = (i == 0) ? 20 : (i == 3 ? 12 : 16);
du_fill_rect(ctx, cx+6, cy+6+i*5, lw, 2, fg);
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
int widths[] = {24, 20, 22, 14, 20, 18};
for (int i = 0; i < 6; i++) {
du_fill_rect(ctx, cx + 8, cy + 8 + i * 4, widths[i], 2,
(i == 0) ? KS_ACCENT2 : KS_ACCENT);
}
}
/* 绘制文件夹图标(32×32) */
static void draw_icon_folder(du_context *ctx, int cx, int cy) {
u32 bg = DP_ARC_500;
u32 fg = DP_ARC_300;
u32 border = DP_ARC_700;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 文件夹标签 */
du_fill_rect(ctx, cx+4, cy+6, 12, 3, fg);
/* 文件夹主体 */
du_fill_rect(ctx, cx+4, cy+12, 24, 2, border);
du_fill_rect(ctx, cx+4, cy+14, 24, 12, fg);
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
du_rect_outline(ctx, cx, cy, 40, 40, KS_BORDER, 2);
du_fill_rect(ctx, cx + 8, cy + 10, 12, 4, KS_ACCENT_DIM);
du_rect_outline(ctx, cx + 8, cy + 10, 12, 4, KS_ACCENT, 1);
du_fill_rect(ctx, cx + 6, cy + 14, 28, 16, KS_BG_SECONDARY);
du_rect_outline(ctx, cx + 6, cy + 14, 28, 16, KS_ACCENT, 1);
du_fill_rect(ctx, cx + 6, cy + 14, 28, 1, KS_ACCENT);
du_fill_rect(ctx, cx + 10, cy + 19, 12, 1, KS_ACCENT2);
du_fill_rect(ctx, cx + 10, cy + 23, 16, 1, KS_TEXT_DIM);
du_fill_rect(ctx, cx + 10, cy + 26, 10, 1, KS_TEXT_DIM);
}
/* 绘制计算器图标(32×32) */
static void draw_icon_calc(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700;
u32 fg = DP_WARNING;
u32 border = DP_WARNING;
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6);
/* 屏幕 */
du_fill_rect(ctx, cx+4, cy+4, 24, 6, DP_ABYSS_900);
du_fill_rect(ctx, cx+22, cy+5, 4, 4, fg);
/* 按键网格 */
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
du_fill_rect(ctx, cx + 6, cy + 6, 28, 8, KS_BG_PRIMARY);
du_rect_outline(ctx, cx + 6, cy + 6, 28, 8, KS_ACCENT2, 1);
du_fill_rect(ctx, cx + 28, cy + 8, 4, 4, KS_ACCENT2);
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
du_fill_rect(ctx, cx+4+c*8, cy+14+r*6, 6, 4, DP_NEUTRAL_400);
int bx = cx + 6 + c * 9;
int by = cy + 18 + r * 7;
du_fill_rect(ctx, bx, by, 7, 5, KS_ACCENT_DIM);
du_rect_outline(ctx, bx, by, 7, 5, KS_ACCENT, 1);
}
}
}
static void draw_icon_browser(du_context *ctx, int cx, int cy) {
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
int ccx = cx + 20, ccy = cy + 20, r = 11;
draw_circle_outline(ctx, ccx, ccy, r, KS_ACCENT);
du_fill_rect(ctx, ccx - r, ccy, 2 * r + 1, 1, KS_ACCENT);
du_fill_rect(ctx, ccx, ccy - r, 1, 2 * r + 1, KS_ACCENT);
du_pixel(ctx, ccx, ccy, KS_ACCENT2);
}
static void draw_app_icon(du_context *ctx, int app_id, int cx, int cy) {
switch (app_id) {
case 0: draw_icon_terminal(ctx, cx, cy); break;
case 1: draw_icon_editor(ctx, cx, cy); break;
case 2: draw_icon_folder(ctx, cx, cy); break;
case 3: draw_icon_calc(ctx, cx, cy); break;
case 4: draw_icon_browser(ctx, cx, cy); break;
default: draw_icon_terminal(ctx, cx, cy); break;
}
}
/* ============================================================
* 光标管理
* ============================================================ */
@@ -306,24 +570,53 @@ static void cursor_draw(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
if (cursor_shape[r][c]) {
du_pixel(&g_fb, mx + c, my + r, DS_DARK_TEXT_PRIMARY);
du_pixel(&g_fb, mx + c, my + r, KS_ACCENT);
}
}
}
}
/* 非阻塞延时:动画期间持续轮询鼠标,保持光标响应。
* 将 ms 个 50000-iter pause 块拆开,每块结束后轮询一次鼠标。
* 若鼠标有新数据包,立即擦除旧光标、在新位置保存背景并重绘光标。 */
/* ---- 双缓冲 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);
}
}
}
@@ -340,10 +633,10 @@ static desktop_window *win_find(int id) {
}
static desktop_window *win_find_at(int mx, int my) {
/* 从后往前搜索(后面的窗口在上面) */
for (int i = g_win_count - 1; i >= 0; i--) {
desktop_window *w = &g_windows[i];
if (!w->visible || w->minimized) continue;
if (w->ws != g_ws_cur) continue;
if (mx >= w->x && mx < w->x + w->w && my >= w->y && my < w->y + w->h) {
return w;
}
@@ -364,6 +657,15 @@ static void win_bring_to_front(desktop_window *w) {
g_windows[g_win_count - 1] = tmp;
}
static void win_focus(desktop_window *w) {
int id = w->id; /* bring_to_front 会重排数组,先保存 id 避免悬空指针 */
win_bring_to_front(w);
for (int i = 0; i < g_win_count; i++)
g_windows[i].focused = (g_windows[i].id == id);
g_focused_win = id;
if (g_win_count > 0) g_windows[g_win_count - 1].minimized = 0;
}
static desktop_window *win_create(int app_id, const char *title, int x, int y, int w, int h) {
if (g_win_count >= MAX_WINDOWS) return 0;
desktop_window *win = &g_windows[g_win_count++];
@@ -377,10 +679,9 @@ static desktop_window *win_create(int app_id, const char *title, int x, int y, i
win->app_state = 0;
win->closing = 0;
win->dirty = 1;
/* 拷贝标题 */
win->ws = g_ws_cur;
for (int i = 0; i < 47 && title[i]; i++) win->title[i] = title[i];
win->title[47] = 0;
/* 取消其他窗口焦点 */
for (int i = 0; i < g_win_count - 1; i++) g_windows[i].focused = 0;
g_focused_win = id;
return win;
@@ -393,200 +694,83 @@ static void win_destroy(desktop_window *w) {
}
if (idx < 0) return;
/* Window close animation - shrinking rectangle */
{
int wx = w->x, wy = w->y, ww = w->w, wh = w->h;
/* 直接隐藏窗口(去掉关闭动画以避免卡顿和闪屏) */
w->visible = 0;
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();
/* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */
cursor_restore_bg();
du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_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);
}
}
/* 调用应用的 on_destroy */
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);
}
for (int i = idx; i < g_win_count - 1; i++) g_windows[i] = g_windows[i+1];
g_win_count--;
if (g_focused_win == w->id) {
g_focused_win = g_win_count > 0 ? g_windows[g_win_count-1].id : -1;
if (g_focused_win >= 0) win_find(g_focused_win)->focused = 1;
g_focused_win = -1;
for (int i = g_win_count - 1; i >= 0; i--) {
if (g_windows[i].ws == g_ws_cur && g_windows[i].visible && !g_windows[i].minimized) {
g_focused_win = g_windows[i].id;
g_windows[i].focused = 1;
break;
}
}
}
}
static int win_hit_titlebar(desktop_window *w, int mx, int my) {
return mx >= w->x && mx < w->x + w->w &&
my >= w->y && my < w->y + TITLEBAR_H;
my >= w->y && my < w->y + KATE_TITLEBAR_H;
}
static int win_hit_close_btn(desktop_window *w, int mx, int my) {
int bx = w->x + w->w - 28;
int by = w->y + 4;
return mx >= bx && mx < bx + 24 && my >= by && my < by + 20;
int bx = w->x + w->w - CLOSE_BTN_OFFSET;
int by = w->y + (KATE_TITLEBAR_H - CLOSE_BTN_SIZE) / 2;
return mx >= bx && mx < bx + CLOSE_BTN_SIZE &&
my >= by && my < by + CLOSE_BTN_SIZE;
}
static int ws_win_count(int ws) {
int n = 0;
for (int i = 0; i < g_win_count; i++)
if (g_windows[i].ws == ws && g_windows[i].visible) n++;
return n;
}
/* ============================================================
* 窗口装饰绘制
* 窗口装饰绘制(Winux-Kate 风格)
* ============================================================ */
static void draw_window_frame(desktop_window *w) {
if (!w->visible || w->minimized) return;
u32 titlebar_bg = w->focused ? DP_ABYSS_600 : DP_ABYSS_800;
u32 border_col = w->focused ? DS_DARK_BORDER_FOCUS : DS_DARK_BORDER;
u32 border = w->focused ? KS_BORDER_FOCUS : KS_BORDER;
/* 发光效果(焦点窗口) */
if (w->focused) {
du_rect_glow(&g_fb, w->x-2, w->y-2, w->w+4, w->h+4,
DS_DARK_BORDER_FOCUS, 10, 3);
du_kate_glow_border(&g_fb, w->x, w->y, w->w, w->h, KS_ACCENT);
}
/* 窗口主体背景 */
du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h,
DS_DARK_BG_SECONDARY, DU_RADIUS_LG);
du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h, KS_BG_SECONDARY, 2);
/* 标题栏 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + BORDER_W,
w->w - 2*BORDER_W, TITLEBAR_H - BORDER_W, titlebar_bg);
du_fill_rect_gradient(&g_fb, w->x + 1, w->y + 1, w->w - 2, KATE_TITLEBAR_H - 1,
0xFF0A3050u, KS_BG_SECONDARY);
du_divider_h(&g_fb, w->x, w->y + KATE_TITLEBAR_H, w->w, KS_BORDER);
du_fill_rect(&g_fb, w->x + 6, w->y + KATE_TITLEBAR_H / 2 - 2, 4, 4, KS_ACCENT2);
/* 标题文字 */
du_draw_string(&g_fb, w->title,
w->x + (i64)DU_SPACE_SM,
w->y + (TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_PRIMARY, titlebar_bg, DU_ASCII_STEP);
w->x + 14,
w->y + (KATE_TITLEBAR_H - (i64)DU_ASCII_LINE_H) / 2 + 1,
KS_ACCENT, 0xFF0A3050u, DU_ASCII_STEP);
/* 关闭按钮 */
int bx = w->x + w->w - 28;
int by = w->y + 4;
du_fill_rounded_rect(&g_fb, bx, by, 24, 20, DP_ERROR, DU_RADIUS_SM);
du_draw_string(&g_fb, "X", bx + 8, by + 1, DP_NEUTRAL_900, DP_ERROR, DU_ASCII_STEP);
int bx = w->x + w->w - CLOSE_BTN_OFFSET;
int by = w->y + (KATE_TITLEBAR_H - CLOSE_BTN_SIZE) / 2;
du_rect_outline(&g_fb, bx, by, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE, KS_DANGER, 1);
du_draw_string(&g_fb, "X", bx + 3, by - 1,
KS_DANGER, KS_BG_SECONDARY, DU_ASCII_STEP);
/* 边框 */
du_rect_outline(&g_fb, w->x, w->y, w->w, w->h, border_col, DU_RADIUS_LG);
/* 标题栏底线 */
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + TITLEBAR_H,
w->w - 2*BORDER_W, 1, border_col);
}
/* ============================================================
* 任务栏绘制
* ============================================================ */
static void draw_taskbar(void) {
i64 tb_y = (i64)g_fb_h - TASKBAR_H;
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, TASKBAR_H, DP_ABYSS_800);
/* 顶部强调线 */
du_fill_rect(&g_fb, 0, tb_y, (i64)g_fb_w, 2, DS_DARK_ACCENT);
/* Start 按钮 */
du_fill_rounded_rect(&g_fb, (i64)DU_SPACE_SM, tb_y + 4, 48, 32,
DP_ABYSS_600, DU_RADIUS_SM);
du_draw_string(&g_fb, "D",
(i64)DU_SPACE_SM + 18, tb_y + 8,
DS_DARK_ACCENT_LIGHT, DP_ABYSS_600, DU_ASCII_STEP);
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
switch (i) {
case 0: draw_icon_terminal(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 1: draw_icon_editor(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 2: draw_icon_folder(&g_fb, icon_x + 4, (int)tb_y + 4); break;
case 3: draw_icon_calc(&g_fb, icon_x + 4, (int)tb_y + 4); break;
}
}
/* 窗口按钮 */
int wx = qx + 4 * 40 + (int)DU_SPACE_LG;
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized) continue;
u32 btn_bg = w->focused ? DS_DARK_ACCENT : DP_ABYSS_600;
du_fill_rounded_rect(&g_fb, wx, tb_y + 4, 100, 32, btn_bg, DU_RADIUS_SM);
/* Truncate title to fit button width (100px - 8px padding = ~11 chars) */
char truncated[16];
int max_chars = 11;
int tlen = 0;
while (w->title[tlen] && tlen < max_chars) { truncated[tlen] = w->title[tlen]; tlen++; }
if (tlen == max_chars && w->title[tlen]) {
/* Add ellipsis */
if (tlen > 8) tlen = 8;
truncated[tlen++] = '.';
truncated[tlen++] = '.';
truncated[tlen++] = '.';
}
truncated[tlen] = 0;
du_draw_string(&g_fb, truncated, wx + 4, tb_y + 8,
DS_DARK_TEXT_PRIMARY, btn_bg, DU_ASCII_STEP);
wx += 104;
if (wx > (int)g_fb_w - 200) break;
}
/* 时钟 */
u8 rtc_h = 0, rtc_m = 0;
outb(0x70, 4); rtc_h = inb(0x71);
outb(0x70, 2); rtc_m = inb(0x71);
/* BCD → 二进制 */
rtc_h = (u8)((rtc_h >> 4) * 10 + (rtc_h & 0xF));
rtc_m = (u8)((rtc_m >> 4) * 10 + (rtc_m & 0xF));
char clock_str[8];
clock_str[0] = '0' + rtc_h / 10;
clock_str[1] = '0' + rtc_h % 10;
clock_str[2] = ':';
clock_str[3] = '0' + rtc_m / 10;
clock_str[4] = '0' + rtc_m % 10;
clock_str[5] = 0;
du_draw_string(&g_fb, clock_str,
(i64)g_fb_w - 5 * (i64)DU_ASCII_STEP - (i64)DU_SPACE_MD,
tb_y + (TASKBAR_H - (i64)DU_ASCII_CELL_H) / 2,
DS_DARK_TEXT_SECONDARY, DP_ABYSS_800, DU_ASCII_STEP);
}
/* ============================================================
* 桌面图标绘制
* ============================================================ */
static void draw_desktop_icons(void) {
for (int i = 0; i < g_icon_count; i++) {
desktop_icon *ic = &g_icons[i];
int cx = ic->x + (DESKTOP_ICON_W - 32) / 2; /* center icon in slot */
int cy = ic->y;
/* 根据应用 ID 画不同图标 */
switch (ic->app_id) {
case 0: draw_icon_terminal(&g_fb, cx, cy); break;
case 1: draw_icon_editor(&g_fb, cx, cy); break;
case 2: draw_icon_folder(&g_fb, cx, cy); break;
case 3: draw_icon_calc(&g_fb, cx, cy); break;
default: draw_icon_terminal(&g_fb, cx, cy); break;
}
/* 标签文字 */
int label_len = 0;
while (ic->label[label_len]) label_len++;
int label_x = ic->x + (DESKTOP_ICON_W - label_len * (int)DU_ASCII_STEP) / 2;
if (label_x < ic->x) label_x = ic->x;
du_draw_string(&g_fb, ic->label, label_x, cy + 36,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
}
du_rect_outline(&g_fb, w->x, w->y, w->w, w->h, border, 2);
}
/* ============================================================
* PS/2 鼠标处理
* ============================================================ */
/* PS/2 控制器等待 — 输入缓冲区空(可写) */
static int ps2_wait_write(void) {
for (int t = 0; t < 200000; t++) {
if (!(inb(0x64) & 0x02)) return 0;
@@ -595,7 +779,6 @@ static int ps2_wait_write(void) {
return -1;
}
/* PS/2 控制器等待 — 输出缓冲区满(可读) */
static int ps2_wait_read(void) {
for (int t = 0; t < 200000; t++) {
if (inb(0x64) & 0x01) return 0;
@@ -604,7 +787,6 @@ static int ps2_wait_read(void) {
return -1;
}
/* 排干输出缓冲区中所有残留数据(带超时) */
static void ps2_drain(void) {
for (int i = 0; i < 16; i++) {
if (!(inb(0x64) & 0x01)) break;
@@ -613,56 +795,45 @@ static void ps2_drain(void) {
}
static void ps2_mouse_init(void) {
/* 1. 刷新固件/上一阶段留下的残留数据 */
ps2_drain();
/* 2. 启用 AUX(鼠标)端口 */
ps2_wait_write();
outb(0x64, 0xA8);
/* 3. 读取控制器配置字节 */
ps2_wait_write();
outb(0x64, 0x20);
ps2_wait_read();
u8 cfg = inb(0x60);
/* 4. 设置配置:bit5=0(启用 AUX 时钟), bit1=1(启用 AUX IRQ12), bit6=1(Set2→Set1 转换) */
cfg &= ~0x20; /* enable AUX clock */
cfg |= 0x02; /* enable AUX IRQ12 */
cfg |= 0x40; /* enable Set2→Set1 translation */
cfg &= ~0x20;
cfg |= 0x02;
cfg |= 0x40;
/* 5. 写回控制器配置 */
ps2_wait_write();
outb(0x64, 0x60);
ps2_wait_write();
outb(0x60, cfg);
/* 6. 复位鼠标 (0xFF) — 响应为 ACK(0xFA) + 自检(0xAA) + 设备ID(0x00),共 3 字节 */
ps2_wait_write();
outb(0x64, 0xD4);
ps2_wait_write();
outb(0x60, 0xFF);
/* 排干所有复位响应字节(最多 8 字节,带超时) */
for (int i = 0; i < 8; i++) {
if (ps2_wait_read() != 0) break;
inb(0x60);
}
/* 7. 启用数据流模式 (0xF4) — 响应为 ACK(0xFA) */
ps2_wait_write();
outb(0x64, 0xD4);
ps2_wait_write();
outb(0x60, 0xF4);
/* 排干 ACK */
for (int i = 0; i < 4; i++) {
if (ps2_wait_read() != 0) break;
inb(0x60);
}
/* 8. 最终刷新:丢弃任何杂散字节,确保数据包从干净状态开始 */
ps2_drain();
/* 重置鼠标数据包解析状态 */
g_mouse_idx = 0;
g_mouse_has_pkt = 0;
@@ -672,20 +843,18 @@ static void ps2_mouse_init(void) {
static int ps2_mouse_poll(void) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
if (!(st & 0x20)) 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;
/* 检查 bit3 同步 */
if (!(g_mouse_buf[0] & 0x08)) return 0;
int dx = (int)(i8)g_mouse_buf[1];
int dy = (int)(i8)g_mouse_buf[2];
/* (i8) 转换已处理符号位,无需再检查 sign bits */
dy = -dy; /* PS/2 Y 向上为正,屏幕 Y 向下为正,需翻转 */
dy = -dy;
g_mouse_x += dx;
g_mouse_y += dy;
@@ -695,8 +864,10 @@ static int ps2_mouse_poll(void) {
if (g_mouse_y >= (int)g_fb_h - CURSOR_SIZE) g_mouse_y = (int)g_fb_h - CURSOR_SIZE;
int old_btn = g_mouse_btn;
g_mouse_btn = (g_mouse_buf[0] & 0x01) ? 1 : 0;
/* Only signal redraw if something visual changed */
g_mouse_btn = g_mouse_buf[0] & 0x03;
if ((g_mouse_btn & 1) && !(old_btn & 1)) g_left_pressed = 1;
if (!(g_mouse_btn & 1) && (old_btn & 1)) g_left_released = 1;
if ((g_mouse_btn & 2) && !(old_btn & 2)) g_right_pressed = 1;
if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0;
g_mouse_has_pkt = 1;
return 1;
@@ -724,7 +895,7 @@ static char scan_to_ascii(u8 sc, int shift) {
}
/* ============================================================
* 内置应用:终端 (Bash)
* 终端 (Bash) — 面板/窗口共用核心
* ============================================================ */
#define TERM_MAX_COLS 80
@@ -739,46 +910,40 @@ typedef struct {
char input_buf[256];
int input_len, input_cursor;
int prompt_len;
/* 性能优化:脏行标记(增量渲染) */
u8 dirty_rows[TERM_MAX_ROWS]; /* 标记哪些行需要重绘 */
int any_dirty; /* 是否有任何脏行 */
} bash_state;
static const char *BASH_PROMPT = "deshab# ";
static void *bash_on_create(app_ctx *ctx) {
bash_state *s = (bash_state *)0x4000000; /* 固定地址分配,避免堆依赖 */
static void bash_reset(bash_state *s, int w, int h) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
s->ch[i] = ' '; s->fg[i] = DS_DARK_TEXT_PRIMARY;
s->ch[i] = ' '; s->fg[i] = KS_TEXT_PRIMARY;
}
s->cols = (ctx->client_w - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_STEP;
s->rows = (ctx->client_h - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_LINE_H;
s->cols = (w - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_STEP;
s->rows = (h - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_LINE_H;
if (s->cols > TERM_MAX_COLS) s->cols = TERM_MAX_COLS;
if (s->rows > TERM_MAX_ROWS) s->rows = TERM_MAX_ROWS;
if (s->cols < 10) s->cols = 10;
if (s->rows < 3) s->rows = 3;
s->cur_col = 0; s->cur_row = 0;
s->input_len = 0; s->input_cursor = 0;
s->prompt_len = 0;
while (BASH_PROMPT[s->prompt_len]) s->prompt_len++;
/* 打印欢迎 */
const char *welcome = "Deshab Bash v0.1\n输入 help 查看命令\n\n";
while (*welcome) {
if (*welcome == '\n') {
s->cur_col = 0; s->cur_row++;
} else {
if (s->cur_row < s->rows && s->cur_col < s->cols) {
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)*welcome;
s->fg[idx] = DS_DARK_TEXT_PRIMARY;
}
s->cur_col++;
}
welcome++;
}
return s;
s->prompt_len = kstrlen(BASH_PROMPT);
/* 初始化脏行标记:所有行标记为脏,确保首次渲染 */
for (int i = 0; i < TERM_MAX_ROWS; i++) s->dirty_rows[i] = 1;
s->any_dirty = 1;
}
static void bash_putc(bash_state *s, char c, u32 color) {
if (c == '\n') { s->cur_col = 0; s->cur_row++; return; }
if (c == '\n') {
s->dirty_rows[s->cur_row] = 1; /* 标记当前行为脏 */
s->any_dirty = 1;
s->cur_col = 0; s->cur_row++;
return;
}
if (s->cur_row >= s->rows) {
/* 滚动 */
/* 滚动:所有行都需要重绘 */
for (int r = 1; r < s->rows; r++) {
for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2];
@@ -789,11 +954,17 @@ static void bash_putc(bash_state *s, char c, u32 color) {
s->ch[(s->rows-1)*TERM_MAX_COLS+c2] = ' ';
}
s->cur_row = s->rows - 1;
/* 标记所有行为脏 */
for (int r = 0; r < s->rows; r++) s->dirty_rows[r] = 1;
s->any_dirty = 1;
}
if (s->cur_col >= s->cols) { s->cur_col = 0; s->cur_row++; }
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
s->ch[idx] = (u8)c; s->fg[idx] = color;
s->cur_col++;
/* 标记当前行为脏 */
s->dirty_rows[s->cur_row] = 1;
s->any_dirty = 1;
}
static void bash_puts(bash_state *s, const char *str, u32 color) {
@@ -802,66 +973,63 @@ static void bash_puts(bash_state *s, const char *str, u32 color) {
static void bash_execute(bash_state *s, const char *cmd) {
while (*cmd == ' ') cmd++;
bash_puts(s, BASH_PROMPT, DS_DARK_PROMPT);
bash_puts(s, cmd, DS_DARK_TEXT_PRIMARY);
bash_puts(s, BASH_PROMPT, KS_ACCENT2);
bash_puts(s, cmd, KS_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
if (*cmd == 0) return;
if (cmd[0]=='h'&&cmd[1]=='e'&&cmd[2]=='l'&&cmd[3]=='p') {
bash_puts(s, " help clear echo version\n", DP_SUCCESS);
bash_puts(s, " uname date about reboot\n", DP_SUCCESS);
bash_puts(s, " halt pwd whoami id\n", DP_SUCCESS);
bash_puts(s, " help clear echo version\n", KS_ACCENT2);
bash_puts(s, " uname about reboot halt\n", KS_ACCENT2);
bash_puts(s, " pwd whoami id\n", KS_ACCENT2);
} else if (cmd[0]=='c'&&cmd[1]=='l'&&cmd[2]=='e'&&cmd[3]=='a'&&cmd[4]=='r') {
for (int i = 0; i < TERM_MAX_CHARS; i++) { s->ch[i] = ' '; }
s->cur_col = 0; s->cur_row = 0;
} else if (cmd[0]=='v'&&cmd[1]=='e'&&cmd[2]=='r') {
bash_puts(s, "Deshab OS v0.1.0\n", DP_SUCCESS);
bash_puts(s, "Deshab OS v0.1.0 (Winux-Kate shell)\n", KS_ACCENT2);
} else if (cmd[0]=='u'&&cmd[1]=='n'&&cmd[2]=='a') {
bash_puts(s, "Deshab\n", DS_DARK_TEXT_PRIMARY);
bash_puts(s, "Deshab\n", KS_TEXT_PRIMARY);
} else if (cmd[0]=='a'&&cmd[1]=='b'&&cmd[2]=='o'&&cmd[3]=='u'&&cmd[4]=='t') {
bash_puts(s, "Deshab OS - SAS-R0 Kernel\n", DP_SEAL_300);
bash_puts(s, "Sealed Arc UI Design\n", DP_ARC_300);
bash_puts(s, "Deshab OS - SAS-R0 Kernel\n", KS_ACCENT);
bash_puts(s, "Winux-Kate UI Structure\n", KS_ACCENT2);
} else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') {
const char *arg = cmd + 4;
while (*arg == ' ') arg++;
bash_puts(s, arg, DS_DARK_TEXT_PRIMARY);
bash_puts(s, arg, KS_TEXT_PRIMARY);
bash_putc(s, '\n', 0);
} else if (cmd[0]=='p'&&cmd[1]=='w'&&cmd[2]=='d') {
bash_puts(s, "/\n", DS_DARK_TEXT_PRIMARY);
bash_puts(s, "/\n", KS_TEXT_PRIMARY);
} else if (cmd[0]=='w'&&cmd[1]=='h'&&cmd[2]=='o') {
bash_puts(s, "root\n", DS_DARK_TEXT_PRIMARY);
bash_puts(s, "root\n", KS_TEXT_PRIMARY);
} else if (cmd[0]=='i'&&cmd[1]=='d') {
bash_puts(s, "uid=0(root) gid=0(root)\n", DS_DARK_TEXT_PRIMARY);
bash_puts(s, "uid=0(root) gid=0(root)\n", KS_TEXT_PRIMARY);
} else if (cmd[0]=='r'&&cmd[1]=='e'&&cmd[2]=='b') {
bash_puts(s, "Rebooting...\n", DS_DARK_PROMPT);
bash_puts(s, "Rebooting...\n", KS_ACCENT2);
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
} else if (cmd[0]=='h'&&cmd[1]=='a'&&cmd[2]=='l'&&cmd[3]=='t') {
bash_puts(s, "Halted.\n", DS_DARK_PROMPT);
bash_puts(s, "Halted.\n", KS_ACCENT2);
for(;;) __asm__("hlt");
} else {
bash_puts(s, "unknown: ", DP_ERROR);
bash_puts(s, cmd, DP_ERROR);
bash_puts(s, "unknown: ", KS_DANGER);
bash_puts(s, cmd, KS_DANGER);
bash_putc(s, '\n', 0);
}
}
static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
bash_state *s = (bash_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
if (sc == 0x1C) { /* Enter */
static void bash_key(bash_state *s, u8 sc, int shift) {
if (sc == 0x1C) {
s->input_buf[s->input_len] = 0;
bash_execute(s, s->input_buf);
s->input_len = 0; s->input_cursor = 0;
} else if (sc == 0x0E) { /* Backspace */
} else if (sc == 0x0E) {
if (s->input_cursor > 0) {
for (int i = s->input_cursor-1; i < s->input_len-1; i++)
s->input_buf[i] = s->input_buf[i+1];
s->input_len--; s->input_cursor--;
}
} else {
char c = scan_to_ascii(sc, ev->shift);
char c = scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && s->input_len < 255) {
for (int i = s->input_len; i > s->input_cursor; i--)
s->input_buf[i] = s->input_buf[i-1];
@@ -869,55 +1037,76 @@ static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
s->input_len++;
}
}
ctx->win->dirty = 1;
}
static void bash_on_draw(void *state, app_ctx *ctx) {
bash_state *s = (bash_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
/* 绘制终端到指定矩形区域(面板或窗口客户区)
* 性能优化:增量渲染,只绘制脏行 */
static void bash_draw_to(bash_state *s, int cx, int cy, int cw, int ch) {
/* 快速退出:终端内容未修改,跳过绘制 */
if (!s->any_dirty) return;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_PRIMARY);
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_PRIMARY);
/* 渲染所有字符 */
for (int r = 0; r < s->rows; r++) {
/* 增量渲染:只绘制脏行 */
if (!s->dirty_rows[r]) continue;
for (int c = 0; c < s->cols; c++) {
int idx = r * TERM_MAX_COLS + c;
if (s->ch[idx] == ' ') continue;
du_draw_char(&g_fb, s->ch[idx],
cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP,
cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H,
s->fg[idx], DS_DARK_BG_PRIMARY);
}
int px = cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP;
int py = cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H;
if (px + (int)DU_ASCII_CELL_W > cx + cw) continue;
if (py + (int)DU_ASCII_CELL_H > cy + ch) continue;
du_draw_char(&g_fb, s->ch[idx], px, py, s->fg[idx], KS_BG_PRIMARY);
}
/* 清除脏行标记 */
s->dirty_rows[r] = 0;
}
/* 绘制当前输入行 */
int input_y = cy + (int)DU_SPACE_SM + s->cur_row * (int)DU_ASCII_LINE_H;
/* 提示符 */
if (input_y + (int)DU_ASCII_CELL_H <= cy + ch) {
du_draw_string(&g_fb, BASH_PROMPT,
cx + (int)DU_SPACE_SM, input_y,
DS_DARK_PROMPT, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 输入内容 */
KS_ACCENT2, KS_BG_PRIMARY, DU_ASCII_STEP);
du_draw_string(&g_fb, s->input_buf,
cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP,
input_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
/* 光标 */
KS_TEXT_PRIMARY, KS_BG_PRIMARY, DU_ASCII_STEP);
int cursor_x = cx + (int)DU_SPACE_SM + (s->prompt_len + s->input_cursor) * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, input_y + (int)DU_ASCII_CELL_H - 3,
(int)DU_ASCII_CELL_W, 2, DS_DARK_CURSOR);
(int)DU_ASCII_CELL_W, 2, KS_ACCENT);
}
static void bash_on_destroy(void *state) {
(void)state;
/* 清除全局脏标记 */
s->any_dirty = 0;
}
/* ---- 窗口适配 ---- */
__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; }
/* ============================================================
* 内置应用:文本编辑器 (Editor)
* 文本编辑器 (Editor) — 面板/IDE/窗口共用核心
* ============================================================ */
#define EDITOR_BUF_SIZE 65536
@@ -926,11 +1115,13 @@ typedef struct {
char text[EDITOR_BUF_SIZE];
int text_len;
int cursor_pos;
int scroll_y; /* 行偏移 */
int scroll_y;
int cursor_line;
int cursor_col;
int modified;
char filename[64];
int has_file; /* 关联了磁盘文件 */
char name11[11]; /* FAT32 8.3 名 */
char disp[13]; /* 显示名 */
} editor_state;
static void editor_recalc_cursor(editor_state *s) {
@@ -950,37 +1141,20 @@ static int editor_line_start(editor_state *s, int line) {
return s->text_len;
}
static void *editor_on_create(app_ctx *ctx) {
editor_state *s = (editor_state *)0x5000000; /* 固定地址 */
static void editor_reset(editor_state *s, const char *sample) {
s->text_len = 0; s->cursor_pos = 0;
s->scroll_y = 0; s->modified = 0;
s->filename[0] = 0;
/* 默认内容 */
const char *sample = "# Welcome to Deshab Editor\n#\n\nStart typing here...\n";
s->has_file = 0;
s->disp[0] = 0;
while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) {
s->text[s->text_len++] = *sample++;
}
s->cursor_pos = s->text_len;
editor_recalc_cursor(s);
/* 设置窗口标题 */
du_draw_string(&g_fb, "", 0, 0, 0, 0, 0); /* 占位 */
for (int i = 0; i < 63; i++) ctx->win->title[i] = 0;
const char *t = "Editor - untitled";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
(void)ctx;
return s;
}
static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
editor_state *s = (editor_state *)state;
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
int e0 = 0; /* TODO: 扩展键处理 */
if (sc == 0x1C) { /* Enter */
static void editor_key(editor_state *s, u8 sc, int shift) {
if (sc == 0x1C) {
if (s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
@@ -988,14 +1162,14 @@ static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
s->text_len++; s->cursor_pos++;
s->modified = 1;
}
} else if (sc == 0x0E) { /* Backspace */
} else if (sc == 0x0E) {
if (s->cursor_pos > 0) {
for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++)
s->text[i] = s->text[i+1];
s->text_len--; s->cursor_pos--;
s->modified = 1;
}
} else if (sc == 0x48) { /* Up */
} else if (sc == 0x48) {
if (s->cursor_line > 0) {
int prev_start = editor_line_start(s, s->cursor_line - 1);
int col = s->cursor_col;
@@ -1003,26 +1177,26 @@ static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x50) { /* Down */
if (s->cursor_line < 999) {
} else if (sc == 0x50) {
int next_start = editor_line_start(s, s->cursor_line + 1);
if (next_start < s->text_len || s->cursor_line < 999) {
int col = s->cursor_col;
s->cursor_pos = next_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++;
}
} else if (sc == 0x4B) { /* Left */
} else if (sc == 0x4B) {
if (s->cursor_pos > 0) s->cursor_pos--;
} else if (sc == 0x4D) { /* Right */
} else if (sc == 0x4D) {
if (s->cursor_pos < s->text_len) s->cursor_pos++;
} else if (sc == 0x47) { /* Home */
} else if (sc == 0x47) {
s->cursor_pos = editor_line_start(s, s->cursor_line);
} else if (sc == 0x4F) { /* End */
} else if (sc == 0x4F) {
s->cursor_pos = editor_line_start(s, s->cursor_line);
while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n')
s->cursor_pos++;
} else {
char c = scan_to_ascii(sc, ev->shift);
char c = scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1];
@@ -1032,31 +1206,23 @@ static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
}
}
editor_recalc_cursor(s);
ctx->win->dirty = 1;
(void)e0;
}
static void editor_on_draw(void *state, app_ctx *ctx) {
editor_state *s = (editor_state *)state;
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
/* 绘制编辑器到指定矩形区域 */
static void editor_draw_to(editor_state *s, int cx, int cy, int cw, int ch) {
int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM;
int text_area_x = cx + line_num_w;
int text_area_w = cw - line_num_w;
int visible_rows = ch / (int)DU_ASCII_LINE_H;
int visible_rows = (ch - 20) / (int)DU_ASCII_LINE_H;
int visible_cols = text_area_w / (int)DU_ASCII_STEP;
if (visible_rows < 1) visible_rows = 1;
if (visible_cols < 4) visible_cols = 4;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_SECONDARY);
/* 自动滚动 */
if (s->cursor_line < s->scroll_y) s->scroll_y = s->cursor_line;
if (s->cursor_line >= s->scroll_y + visible_rows) s->scroll_y = s->cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0;
int pos = 0;
for (int i = 0; i < s->text_len || line <= s->cursor_line; ) {
@@ -1065,26 +1231,23 @@ static void editor_on_draw(void *state, app_ctx *ctx) {
if (line >= s->scroll_y) {
int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H;
/* 行号 */
char ln[8];
ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10;
ln[3] = 0;
du_draw_string(&g_fb, ln, cx + 2, screen_y,
DS_DARK_TEXT_DIM, DS_DARK_BG_SECONDARY, DU_ASCII_STEP);
KS_TEXT_DIM, KS_BG_SECONDARY, DU_ASCII_STEP);
/* 行号分隔线 */
du_fill_rect(&g_fb, cx + line_num_w - 2, screen_y, 1,
(int)DU_ASCII_CELL_H, DS_DARK_DIVIDER);
(int)DU_ASCII_CELL_H, KS_BORDER_DIM);
/* 行文本 */
int col = 0;
while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) {
du_draw_char(&g_fb, s->text[pos],
text_area_x + col * (int)DU_ASCII_STEP,
screen_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_SECONDARY);
KS_TEXT_PRIMARY, KS_BG_SECONDARY);
col++; pos++;
}
if (pos < s->text_len && s->text[pos] == '\n') pos++;
@@ -1096,19 +1259,19 @@ static void editor_on_draw(void *state, app_ctx *ctx) {
if (pos >= s->text_len && line > s->cursor_line) break;
}
/* 光标 */
/* 光标(青色竖条) */
if (s->cursor_line >= s->scroll_y && s->cursor_line < s->scroll_y + visible_rows) {
int cursor_y = cy + (s->cursor_line - s->scroll_y) * (int)DU_ASCII_LINE_H;
int cursor_x = text_area_x + s->cursor_col * (int)DU_ASCII_STEP;
du_fill_rect(&g_fb, cursor_x, cursor_y, 2, (int)DU_ASCII_CELL_H, DS_DARK_CURSOR);
du_fill_rect(&g_fb, cursor_x, cursor_y, 2, (int)DU_ASCII_CELL_H, KS_ACCENT);
}
/* 状态栏 */
/* 底部迷你状态栏 */
int status_y = cy + ch - 20;
du_fill_rect(&g_fb, cx, status_y, cw, 20, DP_ABYSS_800);
du_fill_rect(&g_fb, cx, status_y, cw, 20, KS_BG_TERTIARY);
du_divider_h(&g_fb, cx, status_y, cw, KS_BORDER);
char status[64];
int sp = 0;
/* 行:列 */
const char *p1 = "L:"; while(*p1) status[sp++] = *p1++;
status[sp++] = '0' + (s->cursor_line+1)/10%10;
status[sp++] = '0' + (s->cursor_line+1)%10;
@@ -1116,15 +1279,37 @@ static void editor_on_draw(void *state, app_ctx *ctx) {
status[sp++] = '0' + (s->cursor_col+1)/10%10;
status[sp++] = '0' + (s->cursor_col+1)%10;
if (s->modified) { const char *m = " *"; while(*m) status[sp++] = *m++; }
if (s->has_file) {
const char *m = " "; while(*m) status[sp++] = *m++;
for (int i = 0; s->disp[i] && sp < 60; i++) status[sp++] = s->disp[i];
}
status[sp] = 0;
du_draw_string(&g_fb, status, cx + 4, status_y + 1,
DS_DARK_TEXT_DIM, DP_ABYSS_800, DU_ASCII_STEP);
KS_TEXT_DIM, KS_BG_TERTIARY, DU_ASCII_STEP);
}
/* ---- 窗口适配 ---- */
static void *editor_on_create(app_ctx *ctx) {
editor_state *s = (editor_state *)ADDR_EDWIN;
editor_reset(s, "# Welcome to Deshab Editor\n#\n\nStart typing here...\n");
for (int i = 0; i < 47; i++) ctx->win->title[i] = 0;
const char *t = "Editor - untitled";
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
return s;
}
static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
if (ev->type != EV_KEY_DOWN) return;
editor_key((editor_state *)state, ev->scancode, ev->shift);
ctx->win->dirty = 1;
}
static void editor_on_draw(void *state, app_ctx *ctx) {
editor_draw_to((editor_state *)state, ctx->client_x, ctx->client_y,
ctx->client_w, ctx->client_h);
}
static void editor_on_destroy(void *state) { (void)state; }
/* ============================================================
* 内置应用:计算器 (Calculator)
* 计算器 (Calculator)
* ============================================================ */
typedef struct {
@@ -1132,7 +1317,7 @@ typedef struct {
int display_len;
i64 accumulator;
i64 current;
int op; /* 0=none, 1=+, 2=-, 3=*, 4=/ */
int op;
int new_number;
} calc_state;
@@ -1150,7 +1335,7 @@ static void calc_update_display(calc_state *s) {
}
static void *calc_on_create(app_ctx *ctx) {
calc_state *s = (calc_state *)0x6000000;
calc_state *s = (calc_state *)ADDR_CALC;
s->accumulator = 0; s->current = 0;
s->op = 0; s->new_number = 1;
calc_update_display(s);
@@ -1192,20 +1377,17 @@ static void calc_on_draw(void *state, app_ctx *ctx) {
int cw = ctx->client_w;
int ch = ctx->client_h;
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_SECONDARY);
/* 显示屏 */
int disp_h = 40;
du_fill_rounded_rect(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DP_ABYSS_900, DU_RADIUS_MD);
KS_BG_PRIMARY, DU_RADIUS_MD);
du_rect_outline(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
DS_DARK_BORDER, DU_RADIUS_MD);
/* 数字右对齐 */
KS_ACCENT, DU_RADIUS_MD);
int text_x = cx + cw - 16 - s->display_len * (int)DU_ASCII_STEP - 8;
du_draw_string(&g_fb, s->display, text_x, cy + 16,
DS_DARK_TEXT_PRIMARY, DP_ABYSS_900, DU_ASCII_STEP);
KS_ACCENT, KS_BG_PRIMARY, DU_ASCII_STEP);
/* 按钮网格 4×4 */
const char *btn_labels[] = {
"C", "+/-", "%", "/",
"7", "8", "9", "*",
@@ -1221,15 +1403,17 @@ static void calc_on_draw(void *state, app_ctx *ctx) {
int bx = cx + 8 + col * (btn_w + 3);
int by = btn_y + row * (btn_h + 3);
if (by + btn_h > cy + ch - 4) break;
u32 bg = (i < 4) ? DP_ABYSS_600 :
(i % 4 == 3) ? DS_DARK_ACCENT :
(i == 18) ? DP_SUCCESS :
DP_ABYSS_700;
u32 bg = (i < 4) ? KS_ACCENT_DIM :
(i % 4 == 3) ? KS_ACCENT :
(i == 18) ? KS_ACCENT2 :
KS_BG_TERTIARY;
u32 fg = ((i % 4 == 3) || (i == 18)) ? KS_TEXT_INVERT : KS_TEXT_PRIMARY;
du_fill_rounded_rect(&g_fb, bx, by, btn_w, btn_h, bg, DU_RADIUS_SM);
du_rect_outline(&g_fb, bx, by, btn_w, btn_h, KS_BORDER, DU_RADIUS_SM);
du_draw_string(&g_fb, btn_labels[i],
bx + (btn_w - 1*(int)DU_ASCII_STEP)/2,
by + (btn_h - (int)DU_ASCII_CELL_H)/2,
DS_DARK_TEXT_PRIMARY, bg, DU_ASCII_STEP);
fg, bg, DU_ASCII_STEP);
}
}
@@ -1240,144 +1424,1262 @@ static void calc_on_destroy(void *state) { (void)state; }
* ============================================================ */
static void register_apps(void) {
/* 0: Terminal */
/* 命令行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
"shell", "Terminal", 720, 480,
0, 0, 0, 0, "SHELL ELF"
};
g_app_count++;
/* 1: Editor */
/* 命令行2:cmd.elf(Windows 风格命令行 + PE/EXE 执行)*/
g_apps[1] = (app_descriptor){
"editor", "Editor", 600, 450,
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy
"cmd", "CMD", 720, 480,
0, 0, 0, 0, "CMD ELF"
};
g_app_count++;
/* 2: File Manager */
g_apps[2] = (app_descriptor){
"fileman", "Files", 500, 400,
0, 0, 0, 0 /* placeholder */
"editor", "Editor", 600, 450,
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy, 0
};
g_app_count++;
/* 3: Calculator */
g_apps[3] = (app_descriptor){
"fileman", "Files", 500, 400,
0, 0, 0, 0, "FILEMAN ELF"
};
g_app_count++;
g_apps[4] = (app_descriptor){
"calc", "Calculator", 280, 400,
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy, 0
};
g_app_count++;
g_apps[5] = (app_descriptor){
"browser", "Browser", 700, 500,
0, 0, 0, 0, "BROWSER ELF"
};
g_app_count++;
}
/* Winux-Kate .desktop-grid:canvas padding 24px,列宽 104px,gap 18px,
* 左→右排列,超出画布宽度则换行(align-content: start)
*
* 注意:Terminal 和 CMD 已在第一页 Dashboard(TERM-01 / TERM-02),
* 桌面不再重复显示其图标。
*/
static void setup_desktop_icons(void) {
int start_x = (int)g_fb_w - DESKTOP_ICON_W - ICON_GUTTER_X;
int y = ICON_GUTTER_Y;
int pad = KATE_ICON_PAD; /* 24 */
int x = pad;
int y = KATE_TOPBAR_H + KATE_PAGE_PAD + pad;
int max_x = (int)g_fb_w - pad; /* canvas 右缘 */
for (int i = 0; i < g_app_count; i++) {
if (!g_apps[i].on_create) continue;
if (!g_apps[i].on_create && !g_apps[i].elf_name) continue;
/* 跳过 Terminal 和 CMD(已在 Dashboard 第一页) */
if (g_apps[i].name[0] == 's' && g_apps[i].name[1] == 'h' && g_apps[i].name[2] == 'e') continue;
if (g_apps[i].name[0] == 'c' && g_apps[i].name[1] == 'm' && g_apps[i].name[2] == 'd') continue;
if (x > pad && x + KATE_ICON_W > max_x) {
x = pad;
y += KATE_ICON_H + KATE_ICON_GAP;
}
g_icons[g_icon_count].app_id = i;
g_icons[g_icon_count].x = start_x;
g_icons[g_icon_count].x = x;
g_icons[g_icon_count].y = y;
g_icons[g_icon_count].label = g_apps[i].display_name;
g_icons[g_icon_count].selected = 0;
g_icon_count++;
y += DESKTOP_ICON_H;
x += KATE_ICON_W + KATE_ICON_GAP;
}
}
/* ============================================================
* 应用启动
* 外部 ELF 加载
* ============================================================ */
#define DT_ELFCLASS64 2
#define DT_EM_X86_64 62
#define DT_PT_LOAD 1
#define DT_PT_DYNAMIC 2
#define DT_DT_RELA 7
#define DT_DT_RELASZ 8
#define DT_R_X86_64_RELATIVE 8
typedef struct { u8 ident[16]; u16 type,machine; u32 ver; u64 entry,phoff,shoff; u32 flags; u16 ehsize,phentsize,phnum,shentsize,shnum,shstrndx; } dt_elf64_ehdr;
typedef struct { u32 type,flags; u64 offset,vaddr,paddr,filesz,memsz,align; } dt_elf64_phdr;
typedef struct { i64 tag; u64 val; } dt_elf64_dyn;
typedef struct { u64 offset; u64 info; i64 addend; } dt_elf64_rela;
static u8 g_elf_image[262144];
static int dt_load_elf(u8 *data, u32 data_size, void **entry_out) {
const dt_elf64_ehdr *eh = (const dt_elf64_ehdr *)data;
if (data_size < sizeof(dt_elf64_ehdr)) return -1;
if (eh->ident[0] != 0x7F || eh->ident[4] != DT_ELFCLASS64) return -2;
if (eh->machine != DT_EM_X86_64) return -3;
u64 min_vaddr = ~0ULL, max_vaddr = 0;
u32 lc = 0;
for (u16 i = 0; i < eh->phnum; i++) {
const dt_elf64_phdr *ph = (const dt_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
if (ph->type != DT_PT_LOAD) continue;
if (ph->filesz > ph->memsz) return -4;
if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr;
if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz;
lc++;
}
if (!lc || min_vaddr == ~0ULL) return -5;
u64 isize = (max_vaddr - min_vaddr + 0xFFF) & ~0xFFFULL;
if (isize > sizeof(g_elf_image)) return -6;
u8 *image = g_elf_image;
for (u64 i = 0; i < isize; i++) image[i] = 0;
for (u16 i = 0; i < eh->phnum; i++) {
const dt_elf64_phdr *ph = (const dt_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
if (ph->type != DT_PT_LOAD) continue;
u64 off = ph->vaddr - min_vaddr;
for (u64 j = 0; j < ph->filesz; j++)
image[off + j] = data[ph->offset + j];
}
for (u16 i = 0; i < eh->phnum; i++) {
const dt_elf64_phdr *ph = (const dt_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
if (ph->type != DT_PT_DYNAMIC) continue;
const dt_elf64_dyn *dyn = (const dt_elf64_dyn *)(data + ph->offset);
u64 rela_off = 0, rela_sz = 0;
for (u32 j = 0; j < ph->filesz / sizeof(dt_elf64_dyn); j++) {
if (dyn[j].tag == DT_DT_RELA) rela_off = dyn[j].val;
else if (dyn[j].tag == DT_DT_RELASZ) rela_sz = dyn[j].val;
else if (dyn[j].tag == 0) break;
}
if (rela_off && rela_sz) {
const dt_elf64_rela *r = (const dt_elf64_rela *)(data + rela_off);
u32 rn = (u32)(rela_sz / sizeof(dt_elf64_rela));
for (u32 j = 0; j < rn; j++) {
u32 type = (u32)(r[j].info & 0xFFFFFFFF);
if (type == DT_R_X86_64_RELATIVE) {
u64 target = r[j].offset - min_vaddr;
u64 val = min_vaddr + (u64)r[j].addend;
*(u64 *)(image + target) = val;
}
}
}
}
*entry_out = (void *)(image + (eh->entry - min_vaddr));
return 0;
}
static void launch_external_elf(const char *name11) {
u8 *data = 0; u32 size = 0;
if (f32_read_root_file(name11, &data, &size) != 0) {
slog("elf not found");
return;
}
void *entry = 0;
if (dt_load_elf(data, size, &entry) != 0) {
slog("elf load failed");
return;
}
slog("launching external elf");
void (*elf_entry)(const dsk_boot_context *) = (void (*)(const dsk_boot_context *))entry;
elf_entry(g_boot_ctx);
g_cursor_saved = 0;
g_cursor_old_x = -1; g_cursor_old_y = -1;
files_refresh(); /* 外部应用可能改动了磁盘 */
redraw_all();
slog("external elf returned");
}
/* ============================================================
* 应用启动(单实例聚焦策略)
* ============================================================ */
static void fill_app_ctx(app_ctx *ctx, desktop_window *win) {
ctx->fb = &g_fb;
ctx->win = win;
ctx->client_x = win->x + BORDER_W;
ctx->client_y = win->y + KATE_TITLEBAR_H;
ctx->client_w = win->w - 2 * BORDER_W;
ctx->client_h = win->h - KATE_TITLEBAR_H - BORDER_W;
ctx->should_exit = 0;
ctx->block_read = g_block_read;
ctx->block_write = g_block_write;
ctx->kernel_api = g_kernel_api;
}
static void launch_app(int app_id) {
if (app_id < 0 || app_id >= g_app_count) return;
app_descriptor *app = &g_apps[app_id];
if (app->elf_name) {
launch_external_elf(app->elf_name);
return;
}
if (!app->on_create) return;
/* 计算窗口位置(居中偏移) */
int wx = 80 + g_win_count * 30;
int wy = 40 + g_win_count * 30;
if (wx + (int)app->default_w > (int)g_fb_w - 20) wx = 80;
if (wy + (int)app->default_h > (int)g_fb_h - TASKBAR_H - 20) wy = 40;
/* 单实例:已有同类窗口则聚焦并迁移到当前工作区 */
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].app_id == app_id && g_windows[i].visible) {
g_windows[i].ws = g_ws_cur;
win_focus(&g_windows[i]);
slog("focus existing app window");
return;
}
}
int top = KATE_TOPBAR_H, bot = (int)g_fb_h - KATE_TASKBAR_H;
int wx = ICON_PAD + g_win_count * 30;
int wy = top + 8 + g_win_count * 30;
if (wx + (int)app->default_w > (int)g_fb_w - 20) wx = ICON_PAD;
if (wy + (int)app->default_h > bot - 20) wy = top + 8;
desktop_window *win = win_create(app_id, app->display_name,
wx, wy, (int)app->default_w, (int)app->default_h);
if (!win) return;
/* 构建应用上下文 */
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = win;
ctx.client_x = win->x + BORDER_W;
ctx.client_y = win->y + TITLEBAR_H;
ctx.client_w = win->w - 2 * BORDER_W;
ctx.client_h = win->h - TITLEBAR_H - BORDER_W;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
fill_app_ctx(&ctx, win);
win->app_state = app->on_create(&ctx);
win->dirty = 1;
/* Window open animation - expanding rectangle */
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();
/* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */
cursor_restore_bg();
du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_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");
}
/* ============================================================
* FILES 面板(FAT32 真实读写)
* ============================================================ */
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);
/* disp (NAME.EXT) → name11 (NAME EXT) */
for (int i = 0; i < 11; i++) f->name11[i] = ' ';
int i = 0, o = 0;
while (name[i] && name[i] != '.' && o < 8) f->name11[o++] = name[i++];
while (name[i] && name[i] != '.') i++;
if (name[i] == '.') {
i++;
o = 8;
while (name[i] && o < 11) f->name11[o++] = name[i++];
}
g_file_count++;
return 0;
}
static void files_refresh(void) {
g_file_count = 0;
g_file_sel = -1;
if (!g_block_read) { g_files_loaded = 0; return; }
if (f32_list_root(files_list_cb, 0) == 0) g_files_loaded = 1;
else g_files_loaded = 0;
}
/* 双击打开文件 → 载入 DASHBOARD EDITOR */
static void files_open(int idx) {
if (idx < 0 || idx >= g_file_count) return;
editor_state *ed = (editor_state *)ADDR_EDDASH;
fentry *f = &g_files[idx];
u8 *data = 0; u32 size = 0;
if (f32_read_root_file(f->name11, &data, &size) != 0) {
slog("file open failed");
return;
}
ed->text_len = 0;
u32 cap = EDITOR_BUF_SIZE - 1;
if (size > cap) size = cap;
for (u32 i = 0; i < size; i++) {
char c = (char)data[i];
if ((u8)c >= 32 || c == '\n' || c == '\t')
ed->text[ed->text_len++] = c;
}
for (int i = 0; i < 11; i++) ed->name11[i] = f->name11[i];
kstrcpy(ed->disp, f->disp, 13);
ed->has_file = 1;
ed->modified = 0;
ed->scroll_y = 0;
ed->cursor_pos = 0;
editor_recalc_cursor(ed);
g_dash_focus = 2;
slog("file loaded into editor");
}
/* EDITOR SAVE 回写磁盘 */
static void editor_save(void) {
editor_state *ed = (editor_state *)ADDR_EDDASH;
if (!ed->has_file) return;
if (f32_write_root_file(ed->name11, (const u8 *)ed->text, (u32)ed->text_len) == 0) {
ed->modified = 0;
slog("file saved");
} else {
slog("file save failed");
}
}
/* ============================================================
* 通用小绘制
* ============================================================ */
/* 矩形内居中文字 */
static void draw_centered(const char *s, int cx, int y, u32 fg, u32 bg) {
int l = kstrlen(s);
du_draw_string(&g_fb, s, cx - l * (int)DU_ASCII_STEP / 2, y, fg, bg, DU_ASCII_STEP);
}
/* 虚线矩形(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);
du_fill_rect(&g_fb, i, y + h - 1, 4, 1, color);
}
for (int i = y; i < y + h; i += 8) {
du_fill_rect(&g_fb, x, i, 1, 4, color);
du_fill_rect(&g_fb, x + w - 1, i, 1, 4, color);
}
}
/* ============================================================
* Boot 屏(打字机)
* ============================================================ */
static const char *BOOT_LINES[] = {
"DESHAB SHELL v0.1.0",
"initializing UTSM sealed memory...",
"mounting FAT32 root filesystem............[ OK ]",
"loading window manager (SAS-R0 Ring0).....[ OK ]",
"registering PS/2 input drivers............[ OK ]",
"probing AHCI block device.................[ OK ]",
"resolving desktop shortcuts...............[ OK ]",
"desktop replaced by Winux-Kate shell......[ OK ]",
"shell ready.",
"DEAICUP STUDIO",
};
#define BOOT_LINE_COUNT 10
static void busy_delay(u32 cycles) {
for (volatile u32 i = 0; i < cycles; i++) __asm__("pause");
}
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;
int ty = H / 2 - 180;
/* boot-title */
const char *title = "DESHAB";
int tl = kstrlen(title);
for (int i = 0; i < tl; i++) {
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);
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);
flip_buffer();
busy_delay(2500000);
/* boot-lines 打字机;进度条固定在全部行下方(对应 Kate .boot-bar) */
int ly = ty + 76;
int bar_y = ly + BOOT_LINE_COUNT * ((int)DU_ASCII_LINE_H + 2) + 12;
int bw = 260;
for (int li = 0; li < BOOT_LINE_COUNT; li++) {
const char *s = BOOT_LINES[li];
u32 fg = (li == 1 || li == BOOT_LINE_COUNT - 1) ? KS_TEXT_DIM : KS_ACCENT2;
du_draw_string(&g_fb, ">", lx, ly, KS_ACCENT, 0, DU_ASCII_STEP);
for (int ci = 0; s[ci]; ci++) {
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);
flip_buffer();
busy_delay(125000);
}
ly += (int)DU_ASCII_LINE_H + 2;
/* boot-bar:渐变填充(accent → accent2) */
int fill = bw * (li + 1) / BOOT_LINE_COUNT;
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);
flip_buffer();
busy_delay(10000000);
}
/* ============================================================
* 顶栏(brand + WorkspaceSwitcher + PageSwitcher + PG 时钟)
* ============================================================ */
static void draw_topbar(void) {
int W = (int)g_fb_w;
du_fill_rect_gradient(&g_fb, 0, 0, W, KATE_TOPBAR_H,
0xFF0A2840u, KS_BG_PRIMARY);
du_divider_h(&g_fb, 0, KATE_TOPBAR_H, W, KS_BORDER);
int x = 12;
int ty = (KATE_TOPBAR_H - (int)DU_ASCII_LINE_H) / 2 + 1;
/* brand:方块点 + DESHAB + brand-credit(对应 Kate "由 Deaicup 工作室制作") */
du_fill_rect(&g_fb, x, KATE_TOPBAR_H / 2 - 3, 6, 6, KS_ACCENT);
du_draw_string(&g_fb, "DESHAB", x + 12, ty, KS_ACCENT, 0, DU_ASCII_STEP + 2);
x += 12 + 6 * ((int)DU_ASCII_STEP + 2) + 10;
du_draw_string(&g_fb, "DEAICUP STUDIO", x, ty + 2, KS_ACCENT2, 0, DU_ASCII_STEP);
x += 14 * (int)DU_ASCII_STEP + 14;
/* 分隔线 + 任务视图按钮(三横线图标) */
du_divider_v(&g_fb, x - 6, 6, KATE_TOPBAR_H - 12, KS_BORDER);
g_tvbtn_rect[0] = x; g_tvbtn_rect[1] = 5;
g_tvbtn_rect[2] = 28; g_tvbtn_rect[3] = 24;
{
int hover = g_mouse_x >= x && g_mouse_x < x + 28 &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (hover) du_fill_rect(&g_fb, x, 5, 28, 24, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, 5, 28, 24, KS_BORDER, 1);
du_fill_rect(&g_fb, x + 6, 11, 16, 2, KS_ACCENT);
du_fill_rect(&g_fb, x + 6, 16, 16, 2, KS_ACCENT);
du_fill_rect(&g_fb, x + 6, 21, 16, 2, KS_ACCENT);
}
x += 34;
/* 工作区按钮 "name|n" */
for (int i = 0; i < g_ws_count; i++) {
char label[24];
kstrcpy(label, g_ws[i].name, 18);
kstrcat(label, "|", 24);
int cnt = ws_win_count(i);
char cn[4];
cn[0] = '0' + (char)(cnt % 10); cn[1] = 0;
kstrcat(label, cn, 24);
int bw = kstrlen(label) * (int)DU_ASCII_STEP + 16;
int active = (i == g_ws_cur);
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (active) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT2);
du_kate_glow_border(&g_fb, x, 5, bw, 24, KS_ACCENT2);
} else if (hover) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT_DIM);
}
du_rect_outline(&g_fb, x, 5, bw, 24, active ? KS_ACCENT2 : KS_BORDER, 1);
du_draw_string(&g_fb, label, x + 8, ty,
active ? KS_TEXT_INVERT : KS_TEXT_DIM, 0, DU_ASCII_STEP);
g_ws_rect[i][0] = x; g_ws_rect[i][1] = 5;
g_ws_rect[i][2] = bw; g_ws_rect[i][3] = 24;
x += bw + 6;
}
/* ws-add "+" */
g_wsadd_rect[0] = x; g_wsadd_rect[1] = 5;
g_wsadd_rect[2] = 26; g_wsadd_rect[3] = 24;
{
int hover = g_mouse_x >= x && g_mouse_x < x + 26 &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (hover) du_fill_rect(&g_fb, x, 5, 26, 24, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, 5, 26, 24, KS_ACCENT2, 1);
du_draw_string(&g_fb, "+", x + 7, ty - 1, KS_ACCENT2, 0, DU_ASCII_STEP);
}
x += 32;
/* 分隔线 */
du_divider_v(&g_fb, x - 4, 6, KATE_TOPBAR_H - 12, KS_BORDER);
x += 4;
/* 页面切换:精简按钮文字 */
static const char *pg_names[3] = { "DASH", "IDE", "DESK" };
for (int i = 0; i < 3; i++) {
int bw = kstrlen(pg_names[i]) * (int)DU_ASCII_STEP + 24;
int active = (g_page == i + 1);
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (active) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT);
du_kate_glow_border(&g_fb, x, 5, bw, 24, KS_ACCENT);
} else if (hover) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT_DIM);
}
du_rect_outline(&g_fb, x, 5, bw, 24, active ? KS_ACCENT : KS_BORDER, 1);
du_draw_string(&g_fb, pg_names[i], x + 12, ty,
active ? KS_TEXT_INVERT : KS_TEXT_DIM, 0, DU_ASCII_STEP);
g_pg_rect[i][0] = x; g_pg_rect[i][1] = 5;
g_pg_rect[i][2] = bw; g_pg_rect[i][3] = 24;
x += bw + 8;
}
/* 自定义页 */
for (int i = 0; i < g_cpage_count; i++) {
char label[20];
label[0] = '0' + (char)g_cpages[i].id;
label[1] = '.'; label[2] = 0;
kstrcat(label, g_cpages[i].name, 20);
int bw = kstrlen(label) * (int)DU_ASCII_STEP + 28;
int active = (g_page == g_cpages[i].id);
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (active) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT);
} else if (hover) {
du_fill_rect(&g_fb, x, 5, bw, 24, KS_ACCENT_DIM);
}
du_rect_outline(&g_fb, x, 5, bw, 24, active ? KS_ACCENT : KS_BORDER, 1);
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[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 "+" */
if (g_cpage_count < MAX_CPAGES) {
g_pgadd_rect[0] = x; g_pgadd_rect[1] = 5;
g_pgadd_rect[2] = 26; g_pgadd_rect[3] = 24;
int hover = g_mouse_x >= x && g_mouse_x < x + 26 &&
g_mouse_y >= 5 && g_mouse_y < 29;
if (hover) du_fill_rect(&g_fb, x, 5, 26, 24, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, 5, 26, 24, KS_ACCENT2, 1);
du_draw_string(&g_fb, "+", x + 7, ty - 1, KS_ACCENT2, 0, DU_ASCII_STEP);
} else {
g_pgadd_rect[2] = 0;
}
/* 右侧:credit-top + PG 时钟(对应 Kate "© Deaicup Studio" + "PG n") */
char pgclk[8];
pgclk[0] = 'P'; pgclk[1] = 'G'; pgclk[2] = ' ';
pgclk[3] = '0' + (char)(g_page % 10); pgclk[4] = 0;
const char *credit = "(C) DEAICUP STUDIO";
int cl = kstrlen(credit) + 1 + kstrlen(pgclk);
du_draw_string(&g_fb, credit, W - 12 - cl * (int)DU_ASCII_STEP, ty,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
du_draw_string(&g_fb, pgclk, W - 12 - kstrlen(pgclk) * (int)DU_ASCII_STEP, ty,
KS_ACCENT2, 0, DU_ASCII_STEP);
}
/* ============================================================
* 滑块(VOL / BRIGHT)
* ============================================================ */
static void draw_slider(int x, int y, int w, int val, int rect_out[4]) {
du_fill_rect(&g_fb, x, y + 5, w, 3, KS_BG_TERTIARY);
du_rect_outline(&g_fb, x, y + 4, w, 5, KS_BORDER, 1);
int fw = w * val / 100;
if (fw > 0) du_fill_rect(&g_fb, x, y + 5, fw, 3, KS_ACCENT);
int kx = x + fw - 3;
if (kx < x) kx = x;
if (kx > x + w - 6) kx = x + w - 6;
du_fill_rect(&g_fb, kx, y, 6, 13, KS_ACCENT);
du_rect_outline(&g_fb, kx, y, 6, 13, KS_ACCENT, 1);
rect_out[0] = x; rect_out[1] = y - 2;
rect_out[2] = w; rect_out[3] = 17;
}
/* ============================================================
* 状态栏(DASHBOARD 底部 32px)
* ============================================================ */
static void draw_statusbar_full(void) {
int W = (int)g_fb_w;
int y = (int)g_fb_h - KATE_STATUSBAR_H;
du_fill_rect(&g_fb, 0, y, W, KATE_STATUSBAR_H, 0xFF051828u);
du_divider_h(&g_fb, 0, y, W, KS_BORDER);
int x = 12;
int ty = y + (KATE_STATUSBAR_H - (int)DU_ASCII_LINE_H) / 2 + 1;
/* 时钟(日期 · 秒级时间,accent2) */
int yy, mo, dd, hh, mi, ss;
rtc_read(&yy, &mo, &dd, &hh, &mi, &ss);
char clk[32];
int cp2 = 0;
clk[cp2++] = '2'; clk[cp2++] = '0';
clk[cp2++] = '0' + (char)(yy / 10); clk[cp2++] = '0' + (char)(yy % 10);
clk[cp2++] = '-';
clk[cp2++] = '0' + (char)(mo / 10); clk[cp2++] = '0' + (char)(mo % 10);
clk[cp2++] = '-';
clk[cp2++] = '0' + (char)(dd / 10); clk[cp2++] = '0' + (char)(dd % 10);
clk[cp2++] = ' '; clk[cp2++] = '.'; clk[cp2++] = ' ';
char hms[9];
fmt_hms(hms, hh * 3600 + mi * 60 + ss);
for (int i = 0; hms[i]; i++) clk[cp2++] = hms[i];
clk[cp2] = 0;
du_draw_string(&g_fb, clk, x, ty, KS_ACCENT2, 0, DU_ASCII_STEP);
x += cp2 * (int)DU_ASCII_STEP + 18;
/* UP 计时 */
{
int up = hh * 3600 + mi * 60 + ss - g_rtc_boot_sec;
if (up < 0) up += 86400;
char upb[16];
kstrcpy(upb, "UP ", 16);
char uh[9];
fmt_hms(uh, up);
kstrcat(upb, uh, 16);
du_draw_string(&g_fb, upb, x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
x += kstrlen(upb) * (int)DU_ASCII_STEP + 18;
}
/* VOL 滑块 */
du_draw_string(&g_fb, "VOL", x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
x += 3 * (int)DU_ASCII_STEP + 6;
draw_slider(x, y + 9, 90, g_vol, g_vol_rect);
x += 90 + 8;
{
char vb[6];
vb[0] = '0' + (char)(g_vol / 100 % 10);
vb[1] = '0' + (char)(g_vol / 10 % 10);
vb[2] = '0' + (char)(g_vol % 10);
vb[3] = '%'; vb[4] = 0;
const char *vp = (g_vol >= 100) ? vb : (g_vol >= 10 ? vb + 1 : vb + 2);
du_draw_string(&g_fb, vp, x, ty, KS_ACCENT2, 0, DU_ASCII_STEP);
x += 4 * (int)DU_ASCII_STEP + 14;
}
/* BRIGHT 滑块 */
du_draw_string(&g_fb, "BRIGHT", x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
x += 6 * (int)DU_ASCII_STEP + 6;
draw_slider(x, y + 9, 90, g_bright, g_bri_rect);
x += 90 + 8;
{
char vb[6];
vb[0] = '0' + (char)(g_bright / 100 % 10);
vb[1] = '0' + (char)(g_bright / 10 % 10);
vb[2] = '0' + (char)(g_bright % 10);
vb[3] = '%'; vb[4] = 0;
const char *vp = (g_bright >= 100) ? vb : (g_bright >= 10 ? vb + 1 : vb + 2);
du_draw_string(&g_fb, vp, x, ty, KS_ACCENT2, 0, DU_ASCII_STEP);
x += 4 * (int)DU_ASCII_STEP + 14;
}
/* BT / WIFI */
du_draw_string(&g_fb, "BT OFF", x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
x += 6 * (int)DU_ASCII_STEP + 14;
du_draw_string(&g_fb, "WIFI --", x, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* 右侧 hint */
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);
}
/* ============================================================
* 页面绘制
* ============================================================ */
static void draw_desktop_icons(void) {
/* desktop-empty(Kate:无快捷方式提示) */
if (g_icon_count == 0) {
du_draw_string(&g_fb, "NO SHORTCUTS ON DESKTOP",
KATE_ICON_PAD, KATE_TOPBAR_H + KATE_PAGE_PAD + KATE_ICON_PAD + 16,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
return;
}
for (int i = 0; i < g_icon_count; i++) {
desktop_icon *ic = &g_icons[i];
du_kate_desktop_icon(&g_fb, ic->x, ic->y, ic->label, ic->selected);
int ix = ic->x + (KATE_ICON_W - KATE_ICON_IMG) / 2;
int iy = ic->y + 10;
draw_app_icon(&g_fb, ic->app_id, ix, iy);
}
}
/* ---- 1·DASHBOARD ---- */
static void dash_layout(void) {
int top = KATE_TOPBAR_H, bot = (int)g_fb_h - KATE_STATUSBAR_H;
int pad = 8, gap = 8;
int avail_w = (int)g_fb_w - 2 * pad;
int avail_h = bot - top - 2 * pad;
int left_w = avail_w * 35 / 100; /* 终端面板:42%→35% */
int right_x = pad + left_w + gap;
int right_w = (int)g_fb_w - pad - right_x;
int row_h = (avail_h - gap) / 2;
g_dash_rect[0][0] = pad; g_dash_rect[0][1] = top + pad;
g_dash_rect[0][2] = left_w; g_dash_rect[0][3] = row_h;
g_dash_rect[1][0] = pad; g_dash_rect[1][1] = top + pad + row_h + gap;
g_dash_rect[1][2] = left_w; g_dash_rect[1][3] = row_h;
g_dash_rect[2][0] = right_x; g_dash_rect[2][1] = top + pad;
g_dash_rect[2][2] = right_w; g_dash_rect[2][3] = row_h;
g_dash_rect[3][0] = right_x; g_dash_rect[3][1] = top + pad + row_h + gap;
g_dash_rect[3][2] = right_w; g_dash_rect[3][3] = row_h;
}
static void draw_dashboard(void) {
dash_layout();
const char *headers[4] = { "TERM-01", "TERM-02", 0, "FILES" };
char ed_hdr[40];
editor_state *ed = (editor_state *)ADDR_EDDASH;
kstrcpy(ed_hdr, "EDITOR . ", 40);
kstrcat(ed_hdr, ed->has_file ? ed->disp : "untitled", 40);
headers[2] = ed_hdr;
for (int i = 0; i < 4; i++) {
int x = g_dash_rect[i][0], y = g_dash_rect[i][1];
int w = g_dash_rect[i][2], h = g_dash_rect[i][3];
du_kate_panel(&g_fb, x, y, w, h, headers[i]);
if (g_dash_focus == i) {
du_rect_outline(&g_fb, x, y, w, h, KS_ACCENT, 2);
}
}
/* EDITOR 头 SAVE 按钮(仅关联文件后可用) */
{
int ex = g_dash_rect[2][0], ey = g_dash_rect[2][1], ew = g_dash_rect[2][2];
int bw = 52, bh = 18;
int bx = ex + ew - bw - 6, by = ey + 3;
g_save_rect[0] = bx; g_save_rect[1] = by;
g_save_rect[2] = bw; g_save_rect[3] = bh;
u32 fg = ed->has_file ? KS_ACCENT : KS_TEXT_DIM;
int hover = ed->has_file &&
g_mouse_x >= bx && g_mouse_x < bx + bw &&
g_mouse_y >= by && g_mouse_y < by + bh;
if (hover) du_fill_rect(&g_fb, bx, by, bw, bh, KS_ACCENT_DIM);
du_rect_outline(&g_fb, bx, by, bw, bh, ed->has_file ? KS_BORDER : KS_BORDER_DIM, 1);
du_draw_string(&g_fb, "SAVE", bx + 5, by + 1, fg, 0, DU_ASCII_STEP);
}
/* 面板内容 */
bash_draw_to((bash_state *)ADDR_TERM1,
g_dash_rect[0][0] + 2, g_dash_rect[0][1] + KATE_PANEL_HEADER_H + 2,
g_dash_rect[0][2] - 4, g_dash_rect[0][3] - KATE_PANEL_HEADER_H - 4);
bash_draw_to((bash_state *)ADDR_TERM2,
g_dash_rect[1][0] + 2, g_dash_rect[1][1] + KATE_PANEL_HEADER_H + 2,
g_dash_rect[1][2] - 4, g_dash_rect[1][3] - KATE_PANEL_HEADER_H - 4);
editor_draw_to(ed,
g_dash_rect[2][0] + 2, g_dash_rect[2][1] + KATE_PANEL_HEADER_H + 2,
g_dash_rect[2][2] - 4, g_dash_rect[2][3] - KATE_PANEL_HEADER_H - 4);
/* FILES 面板内容 */
{
int fx = g_dash_rect[3][0] + 2, fy = g_dash_rect[3][1] + KATE_PANEL_HEADER_H + 2;
int fw = g_dash_rect[3][2] - 4, fh = g_dash_rect[3][3] - KATE_PANEL_HEADER_H - 4;
du_fill_rect(&g_fb, fx, fy, fw, fh, KS_BG_PRIMARY);
char path_buf[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);
path_buf[pl++] = ')'; path_buf[pl] = 0;
du_draw_string(&g_fb, path_buf, fx + 8, fy + 3, KS_ACCENT, KS_BG_PRIMARY, DU_ASCII_STEP);
du_divider_h(&g_fb, fx, fy + 22, fw, KS_BORDER);
int row_h = 22;
int max_rows = (fh - 24) / row_h;
for (int i = 0; i < g_file_count && i < max_rows; i++) {
int ry = fy + 24 + i * row_h;
if (i == g_file_sel) {
du_fill_rect(&g_fb, fx, ry, fw, row_h, KS_ACCENT_DIM);
}
int hover = (g_mouse_x >= fx && g_mouse_x < fx + fw &&
g_mouse_y >= ry && g_mouse_y < ry + row_h);
if (hover && i != g_file_sel) {
du_fill_rect(&g_fb, fx, ry, fw, row_h, KS_ACCENT_DIM);
}
u32 fg = (i == g_file_sel) ? KS_ACCENT : KS_TEXT_PRIMARY;
du_draw_string(&g_fb, g_files[i].disp, fx + 8, ry + 2, fg, 0, DU_ASCII_STEP);
char sz[12];
u32 kb = g_files[i].size / 1024;
int sp = 0;
if (kb >= 100) { sz[sp++] = '0' + (char)(kb / 100 % 10); }
if (kb >= 10) { sz[sp++] = '0' + (char)(kb / 10 % 10); }
sz[sp++] = '0' + (char)(kb % 10);
sz[sp++] = 'K'; sz[sp] = 0;
du_draw_string(&g_fb, sz, fx + fw - sp * (int)DU_ASCII_STEP - 8, ry + 2,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
}
if (!g_files_loaded) {
du_draw_string(&g_fb, "NO BLOCK DEVICE", fx + 8, fy + 28,
KS_DANGER, KS_BG_PRIMARY, DU_ASCII_STEP);
} else if (g_file_count == 0) {
du_draw_string(&g_fb, "(empty)", fx + 8, fy + 28,
KS_TEXT_DIM, KS_BG_PRIMARY, DU_ASCII_STEP);
}
}
draw_statusbar_full();
}
/* ---- 2·IDE ---- */
static int g_ide_attached = 0;
static void draw_ide(void) {
int W = (int)g_fb_w;
int top = KATE_TOPBAR_H;
/* ide-tabs(36px):dot + 实例标签 + "+ NEW IDE" + ide-hint(Kate 结构) */
du_fill_rect(&g_fb, 0, top, W, 36, 0xFF051828u);
du_divider_h(&g_fb, 0, top + 36, W, KS_BORDER);
int x = 12;
int ty = top + (36 - (int)DU_ASCII_LINE_H) / 2 + 1;
/* .dot(8×8 accent2 发光点) */
du_fill_rect(&g_fb, x, top + 14, 8, 8, KS_ACCENT2);
x += 16;
/* 实例标签 EDITOR ×(Kate .ide-tab) */
{
const char *label = "EDITOR";
int bw = kstrlen(label) * (int)DU_ASCII_STEP + 32;
if (g_ide_attached) {
du_fill_rect(&g_fb, x, top + 5, bw, 26, KS_ACCENT);
du_kate_glow_border(&g_fb, x, top + 5, bw, 26, KS_ACCENT);
}
du_rect_outline(&g_fb, x, top + 5, bw, 26,
g_ide_attached ? KS_ACCENT : KS_BORDER, 1);
du_draw_string(&g_fb, label, x + 8, ty,
g_ide_attached ? 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_ide_tabx[0] = x + bw - 20; g_ide_tabx[1] = top + 5;
g_ide_tabx[2] = 20; g_ide_tabx[3] = 26;
x += bw + 8;
}
/* "+ NEW IDE" 按钮(Kate .btn,透明底 + 边框 + accent 字) */
{
const char *nb = "+ NEW IDE";
int bw = kstrlen(nb) * (int)DU_ASCII_STEP + 20;
g_ide_new_rect[0] = x; g_ide_new_rect[1] = top + 7;
g_ide_new_rect[2] = bw; g_ide_new_rect[3] = 22;
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= top + 7 && g_mouse_y < top + 29;
if (hover) du_fill_rect(&g_fb, x, top + 7, bw, 22, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, top + 7, bw, 22, KS_BORDER, 1);
du_draw_string(&g_fb, nb, x + 10, ty, KS_ACCENT, 0, DU_ASCII_STEP);
x += bw + 8;
}
/* ide-hint */
du_draw_string(&g_fb, "CTRL+SHIFT+TAB CTX . x DETACH . DBL-CLICK HOST ATTACH",
x + 8, ty + 1, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* page-credit(对应 Kate "由 Deaicup 工作室制作") */
du_draw_string(&g_fb, "DEAICUP STUDIO",
W - 12 - 14 * (int)DU_ASCII_STEP, ty + 1,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* ide-host(虚线,margin 8px) */
int hx = 8, hy = top + 36 + 8;
int hw = W - 16, hh = (int)g_fb_h - hy - 8;
g_ide_host[0] = hx; g_ide_host[1] = hy;
g_ide_host[2] = hw; g_ide_host[3] = hh;
draw_dashed_rect(hx, hy, hw, hh, KS_BORDER);
if (g_ide_attached) {
editor_draw_to((editor_state *)ADDR_EDDASH, hx + 2, hy + 2, hw - 4, hh - 4);
} else {
/* embed-empty(Kate:未启动 VSCode 提示) */
draw_centered("EDITOR NOT ATTACHED . DOUBLE-CLICK OR + NEW IDE",
W / 2, hy + hh / 2 - 8, KS_TEXT_DIM, 0);
}
}
/* ---- 3·DESKTOP ---- */
static void draw_desktop_page(void) {
/* 图标网格 */
draw_desktop_icons();
/* 窗口(当前工作区,从底到顶) */
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized || !w->visible) continue;
if (w->ws != g_ws_cur) continue;
draw_window_frame(w);
if (w->app_id >= 0 && w->app_id < g_app_count &&
g_apps[w->app_id].on_draw && w->app_state) {
app_ctx ctx;
fill_app_ctx(&ctx, w);
g_apps[w->app_id].on_draw(w->app_state, &ctx);
}
du_kate_scanlines(&g_fb, w->x + 1, w->y + KATE_TITLEBAR_H,
w->w - 2, w->h - KATE_TITLEBAR_H - 1);
}
/* 任务栏(40px) */
int W = (int)g_fb_w;
int tb_y = (int)g_fb_h - KATE_TASKBAR_H;
du_fill_rect_gradient(&g_fb, 0, tb_y, W, KATE_TASKBAR_H,
0xFF0A2840u, KS_BG_PRIMARY);
du_divider_h(&g_fb, 0, tb_y, W, KS_BORDER);
int x = 8;
int ty = tb_y + (KATE_TASKBAR_H - (int)DU_ASCII_LINE_H) / 2 + 1;
/* tb-item per window(当前工作区) */
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (!w->visible || w->ws != g_ws_cur) {
g_tb_rect[i][2] = 0; g_tb_close_rect[i][2] = 0;
continue;
}
char t[16];
int tl = 0;
while (w->title[tl] && tl < 11) { t[tl] = w->title[tl]; tl++; }
if (w->title[tl]) { t[tl-1] = '.'; t[tl] = '.'; tl++; }
t[tl] = 0;
int bw = tl * (int)DU_ASCII_STEP + 34;
if (bw > 150) bw = 150;
int active = w->focused;
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= tb_y + 6 && g_mouse_y < tb_y + 34;
if (active) du_fill_rect(&g_fb, x, tb_y + 6, bw, 28, KS_ACCENT_DIM);
else if (hover) du_fill_rect(&g_fb, x, tb_y + 6, bw, 28, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, tb_y + 6, bw, 28,
active ? KS_ACCENT : KS_BORDER, 1);
du_draw_string(&g_fb, t, x + 6, ty,
active ? KS_ACCENT : KS_TEXT_PRIMARY, 0, DU_ASCII_STEP);
du_draw_string(&g_fb, "x", x + bw - 14, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
g_tb_rect[i][0] = x; g_tb_rect[i][1] = tb_y + 6;
g_tb_rect[i][2] = bw; g_tb_rect[i][3] = 28;
g_tb_close_rect[i][0] = x + bw - 20; g_tb_close_rect[i][1] = tb_y + 6;
g_tb_close_rect[i][2] = 20; g_tb_close_rect[i][3] = 28;
x += bw + 6;
if (x > W - 260) break;
}
/* Winux-Kate 任务栏右组(margin-left:auto):
* [+ ADOPT 虚框] [tb-credit 署名] [EXIT 危险色] */
const char *credit = "DEAICUP STUDIO";
const char *quit = "EXIT";
const char *adopt = "+ ADOPT";
int qw = 60;
int qx = W - qw - 8;
int credit_w = kstrlen(credit) * (int)DU_ASCII_STEP;
int cx = qx - 8 - credit_w;
int aw = kstrlen(adopt) * (int)DU_ASCII_STEP + 16;
int ax = cx - 8 - aw;
/* tb-adopt:虚线 accent 边框 */
g_tb_adopt_rect[0] = ax; g_tb_adopt_rect[1] = tb_y + 6;
g_tb_adopt_rect[2] = aw; g_tb_adopt_rect[3] = 28;
{
int hover = g_mouse_x >= ax && g_mouse_x < ax + aw &&
g_mouse_y >= tb_y + 6 && g_mouse_y < tb_y + 34;
if (hover) du_fill_rect(&g_fb, ax, tb_y + 6, aw, 28, KS_ACCENT_DIM);
draw_dashed_rect(ax, tb_y + 6, aw, 28, KS_ACCENT);
du_draw_string(&g_fb, adopt, ax + 8, ty, KS_ACCENT, 0, DU_ASCII_STEP);
}
/* tb-credit */
du_draw_string(&g_fb, credit, cx, ty, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* tb-quit */
g_tb_quit_rect[0] = qx; g_tb_quit_rect[1] = tb_y + 6;
g_tb_quit_rect[2] = qw; g_tb_quit_rect[3] = 28;
{
int hover = g_mouse_x >= qx && g_mouse_x < qx + qw &&
g_mouse_y >= tb_y + 6 && g_mouse_y < tb_y + 34;
if (hover) du_fill_rect(&g_fb, qx, tb_y + 6, qw, 28, 0x30FF4D6Du);
du_rect_outline(&g_fb, qx, tb_y + 6, qw, 28, KS_DANGER, 1);
du_draw_string(&g_fb, quit, qx + (qw - 4 * (int)DU_ASCII_STEP) / 2, ty,
KS_DANGER, 0, DU_ASCII_STEP);
}
}
/* ---- 5+/CUSTOM ---- */
static int g_cp_new_rect[4]; /* 自定义页 "+ NEW INSTANCE" 按钮 */
static void draw_custom_page(int cp_idx) {
int W = (int)g_fb_w;
int top = KATE_TOPBAR_H;
cpage *cp = &g_cpages[cp_idx];
/* ide-tabs 栏(36px):dot + 页名 + "+ NEW INSTANCE" + ide-hint(Kate 结构) */
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 + 页名 */
du_fill_rect(&g_fb, 12, top + 14, 8, 8, KS_ACCENT2);
du_draw_string(&g_fb, cp->name, 28, ty, KS_ACCENT, 0, DU_ASCII_STEP);
int x = 28 + kstrlen(cp->name) * (int)DU_ASCII_STEP + 12;
/* "+ NEW INSTANCE" 按钮(对应 Kate custom launch_custom_new) */
{
const char *nb = "+ NEW INSTANCE";
int bw = kstrlen(nb) * (int)DU_ASCII_STEP + 20;
g_cp_new_rect[0] = x; g_cp_new_rect[1] = top + 7;
g_cp_new_rect[2] = bw; g_cp_new_rect[3] = 22;
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
g_mouse_y >= top + 7 && g_mouse_y < top + 29;
if (hover) du_fill_rect(&g_fb, x, top + 7, bw, 22, KS_ACCENT_DIM);
du_rect_outline(&g_fb, x, top + 7, bw, 22, KS_BORDER, 1);
du_draw_string(&g_fb, nb, x + 10, ty, KS_ACCENT, 0, DU_ASCII_STEP);
x += bw + 8;
}
/* ide-hint */
du_draw_string(&g_fb, "DBL-CLICK HOST LAUNCH . EXTERNAL ELF FULLSCREEN",
x + 8, ty + 1, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* page-credit */
du_draw_string(&g_fb, "DEAICUP STUDIO",
W - 12 - 14 * (int)DU_ASCII_STEP, ty + 1,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* ide-host(虚线,margin 8px) */
int hx = 8, hy = top + 36 + 8;
int hw = W - 16, hh = (int)g_fb_h - hy - 8;
g_cp_host[0] = hx; g_cp_host[1] = hy;
g_cp_host[2] = hw; g_cp_host[3] = hh;
draw_dashed_rect(hx, hy, hw, hh, KS_BORDER);
/* embed-empty(Kate:正在启动提示) */
char msg[40];
kstrcpy(msg, "DOUBLE-CLICK TO LAUNCH ", 40);
kstrcat(msg, cp->name, 40);
draw_centered(msg, hx + hw / 2, hy + hh / 2 - 12, KS_TEXT_DIM, 0);
char sub[40];
kstrcpy(sub, "ELF: ", 40);
{
char disp[13];
f32_name_from_83(cp->elf11, disp);
kstrcat(sub, disp, 40);
}
draw_centered(sub, hx + hw / 2, hy + hh / 2 + 12, KS_ACCENT, 0);
}
/* ============================================================
* 任务视图 overlay
* ============================================================ */
static void draw_taskview(void) {
int W = (int)g_fb_w;
/* 背景暗化(全屏 blend,模态 overlay 下文字可读性优先) */
for (u64 ry = 0; ry < g_fb_h; ry++) {
u32 *line = (u32 *)((u8 *)g_fb.fb + ry * g_fb_pitch);
for (u64 rx = 0; rx < g_fb_w; rx++) {
line[rx] = du_blend(line[rx], KS_BG_PRIMARY, 220);
}
}
/* tv-header */
du_draw_string(&g_fb, "TASK VIEW", 60, 44, KS_ACCENT, 0, DU_ASCII_STEP + 4);
du_draw_string(&g_fb, "CLICK TO SWITCH . ESC CLOSE . x CLOSE WS",
60 + 9 * ((int)DU_ASCII_STEP + 4) + 20, 48,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
du_divider_h(&g_fb, 60, 76, W - 120, KS_BORDER);
/* tv-grid 卡片 */
int cw = 260, ch = 170, gap = 20;
int cols = (W - 120 + gap) / (cw + gap);
if (cols < 1) cols = 1;
for (int i = 0; i <= g_ws_count; i++) {
int row = i / cols, col = i % cols;
int x = 60 + col * (cw + gap);
int y = 96 + row * (ch + gap);
g_tv_card[i][0] = x; g_tv_card[i][1] = y;
g_tv_card[i][2] = cw; g_tv_card[i][3] = ch;
if (i == g_ws_count) {
/* 新建工作区卡片(虚线 +) */
draw_dashed_rect(x, y, cw, ch, KS_ACCENT2);
du_draw_string(&g_fb, "+", x + cw / 2 - 6, y + ch / 2 - 24,
KS_ACCENT2, 0, DU_ASCII_STEP);
draw_centered("NEW WORKSPACE", x + cw / 2, y + ch / 2 + 8,
KS_ACCENT2, 0);
continue;
}
int active = (i == g_ws_cur);
int hover = g_mouse_x >= x && g_mouse_x < x + cw &&
g_mouse_y >= y && g_mouse_y < y + ch;
du_fill_rect(&g_fb, x, y, cw, ch, KS_BG_SECONDARY);
du_rect_outline(&g_fb, x, y, cw, ch,
active ? KS_ACCENT2 : (hover ? KS_ACCENT : KS_BORDER), 1);
if (active) du_kate_glow_border(&g_fb, x, y, cw, ch, KS_ACCENT2);
/* tv-card-top */
du_draw_string(&g_fb, g_ws[i].name, x + 12, y + 10, KS_ACCENT, 0, DU_ASCII_STEP);
du_divider_h(&g_fb, x, y + 34, cw, KS_BORDER);
/* × close */
g_tv_close[i][0] = x + cw - 26; g_tv_close[i][1] = y + 4;
g_tv_close[i][2] = 22; g_tv_close[i][3] = 26;
du_draw_string(&g_fb, "x", x + cw - 20, y + 8, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* tv-stat */
char nbuf[4];
int wc = ws_win_count(i);
nbuf[0] = '0' + (char)(wc % 10); nbuf[1] = 0;
draw_centered(nbuf, x + cw / 4, y + 70, KS_ACCENT2, 0);
draw_centered("WINS", x + cw / 4, y + 96, KS_TEXT_DIM, 0);
char pbuf[6];
kstrcpy(pbuf, "PG", 6);
pbuf[2] = '0' + (char)(g_ws[i].page % 10); pbuf[3] = 0;
draw_centered(pbuf, x + cw * 3 / 4, y + 70, KS_ACCENT2, 0);
draw_centered("PAGE", x + cw * 3 / 4, y + 96, KS_TEXT_DIM, 0);
/* badge CURRENT */
if (active) {
int bx = x + cw - 84, by = y + 6;
du_fill_rect(&g_fb, bx, by, 56, 14, KS_ACCENT2);
du_draw_string(&g_fb, "CURRENT", bx + 2, by - 1,
KS_TEXT_INVERT, 0, DU_ASCII_STEP);
}
}
}
/* ============================================================
* 右键菜单(移到工作区)
* ============================================================ */
/* 构建"移到工作区"列表(排除当前工作区,与 Winux-Kate 一致),返回行数 */
static int ctx_list_build(int *map) {
int n = 0;
for (int i = 0; i < g_ws_count; i++) {
if (i == g_ws_cur) continue;
map[n++] = i;
}
return n;
}
static void draw_ctx_menu(void) {
if (!g_ctx_open) return;
int lmap[MAX_WS];
int ln = ctx_list_build(lmap);
int rows = (ln ? ln : 1) + 1; /* 列表行(或空态行) + 新建行 */
int mw = 200;
int mh = 24 + rows * 22 + 8;
int x = g_ctx_x, y = g_ctx_y;
if (x + mw > (int)g_fb_w) x = (int)g_fb_w - mw - 4;
if (y + mh > (int)g_fb_h) y = (int)g_fb_h - mh - 4;
g_ctx_rect[0] = x; g_ctx_rect[1] = y;
g_ctx_rect[2] = mw; g_ctx_rect[3] = mh;
du_fill_rect(&g_fb, x, y, mw, mh, 0xFF050A18u);
du_rect_outline(&g_fb, x, y, mw, mh, KS_ACCENT, 1);
du_kate_glow_border(&g_fb, x, y, mw, mh, KS_ACCENT);
du_draw_string(&g_fb, "MOVE TO WORKSPACE", x + 12, y + 5,
KS_ACCENT, 0, DU_ASCII_STEP);
du_divider_h(&g_fb, x, y + 22, mw, KS_BORDER);
int iy = y + 26;
int row = 0;
if (ln == 0) {
/* 空态(不可点击) */
du_draw_string(&g_fb, "NO OTHER WORKSPACE", x + 12, iy + 2,
KS_TEXT_DIM, 0, DU_ASCII_STEP);
g_ctx_item[0][2] = 0; /* 禁用命中 */
g_ctx_item_ws[0] = -2;
iy += 22;
row = 1;
}
for (int r = 0; r < ln; r++) {
int wi = lmap[r];
int hover = g_mouse_x >= x && g_mouse_x < x + mw &&
g_mouse_y >= iy && g_mouse_y < iy + 22;
if (hover) du_fill_rect(&g_fb, x + 1, iy, mw - 2, 22, KS_ACCENT_DIM);
du_draw_string(&g_fb, g_ws[wi].name, x + 12, iy + 2,
hover ? KS_ACCENT : KS_TEXT_PRIMARY, 0, DU_ASCII_STEP);
char cn[4];
cn[0] = '0' + (char)(ws_win_count(wi) % 10); cn[1] = 0;
du_draw_string(&g_fb, cn, x + mw - 24, iy + 2, KS_TEXT_DIM, 0, DU_ASCII_STEP);
g_ctx_item[row][0] = x; g_ctx_item[row][1] = iy;
g_ctx_item[row][2] = mw; g_ctx_item[row][3] = 22;
g_ctx_item_ws[row] = wi;
iy += 22;
row++;
}
/* + NEW WORKSPACE */
du_divider_h(&g_fb, x, iy, mw, KS_BORDER);
int hover = g_mouse_x >= x && g_mouse_x < x + mw &&
g_mouse_y >= iy && g_mouse_y < iy + 22;
if (hover) du_fill_rect(&g_fb, x + 1, iy, mw - 2, 22, KS_ACCENT_DIM);
du_draw_string(&g_fb, "+ NEW WORKSPACE", x + 12, iy + 2,
KS_ACCENT2, 0, DU_ASCII_STEP);
g_ctx_item[row][0] = x; g_ctx_item[row][1] = iy;
g_ctx_item[row][2] = mw; g_ctx_item[row][3] = 22;
g_ctx_item_ws[row] = -1;
/* 清除残余行命中 */
for (int r = row + 1; r <= MAX_WS; r++) g_ctx_item[r][2] = 0;
}
/* ============================================================
* 全屏重绘
* ============================================================ */
/* 全局亮度(BRIGHT 滑块真实调暗)
* 性能优化:仅在亮度变化时处理,避免每帧全屏遍历 */
static void apply_brightness(void) {
/* 快速退出:亮度100%无需调整 */
if (g_bright >= 100) {
g_bright_applied = 100;
return;
}
/* 快速退出:亮度未变化,跳过处理 */
if (g_bright == g_bright_applied) return;
/* 记录当前应用的亮度值 */
g_bright_applied = g_bright;
u32 f = (u32)g_bright;
for (u64 y = 0; y < g_fb_h; y++) {
u32 *line = (u32 *)((u8 *)g_fb.fb + y * g_fb_pitch);
for (u64 x = 0; x < g_fb_w; x++) {
u32 c = line[x];
u32 b = ((c >> 16) & 0xFF) * f / 100;
u32 g = ((c >> 8) & 0xFF) * f / 100;
u32 r = (c & 0xFF) * f / 100;
line[x] = 0xFF000000u | (b << 16) | (g << 8) | r;
}
}
}
static void redraw_all(void) {
/* 渐变背景 */
du_fill_bg_gradient(&g_fb, DP_ABYSS_900, DP_ABYSS_800);
/* 深空黑渐变背景 */
du_fill_bg_gradient(&g_fb, KS_BG_PRIMARY, KS_BG_TERTIARY);
/* 桌面图标 */
draw_desktop_icons();
/* 顶栏 */
draw_topbar();
/* 窗口(从底到顶) */
for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i];
if (w->minimized || !w->visible) continue;
/* 窗口装饰(先画框架作为窗口背景:主体、标题栏、边框) */
draw_window_frame(w);
/* 窗口客户区背景 */
int cx = w->x + BORDER_W;
int cy = w->y + TITLEBAR_H;
int cw = w->w - 2 * BORDER_W;
int ch = w->h - TITLEBAR_H - BORDER_W;
/* 调用应用绘制(在框架之上绘制客户区内容) */
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_draw && w->app_state) {
app_ctx ctx;
ctx.fb = &g_fb;
ctx.win = w;
ctx.client_x = cx; ctx.client_y = cy;
ctx.client_w = cw; ctx.client_h = ch;
ctx.should_exit = 0;
ctx.block_read = g_block_read;
ctx.kernel_api = g_kernel_api;
g_apps[w->app_id].on_draw(w->app_state, &ctx);
}
/* 页面 */
if (g_page == 1) draw_dashboard();
else if (g_page == 2) draw_ide();
else if (g_page == 3) draw_desktop_page();
else {
int idx = -1;
for (int i = 0; i < g_cpage_count; i++)
if (g_cpages[i].id == g_page) { idx = i; break; }
if (idx >= 0) draw_custom_page(idx);
else g_page = 1;
}
/* 任务栏 */
draw_taskbar();
/* 任务视图 overlay */
if (g_taskview) draw_taskview();
/* 右键菜单(最顶) */
if (g_ctx_open) draw_ctx_menu();
/* 亮度调整 */
apply_brightness();
/* 鼠标光标 */
cursor_save_bg(g_mouse_x, g_mouse_y);
@@ -1385,7 +2687,433 @@ static void redraw_all(void) {
}
/* ============================================================
* 主循环
* 工作区 / 页面管理
* ============================================================ */
static void ws_switch(int id) {
if (id < 0 || id >= g_ws_count || id == g_ws_cur) { g_taskview = 0; return; }
g_ws[g_ws_cur].page = g_page; /* 记住离开时的页面 */
g_ws_cur = id;
g_page = g_ws[id].page;
g_taskview = 0;
}
static void ws_create(void) {
if (g_ws_count >= MAX_WS) return;
char *nm = g_ws[g_ws_count].name;
nm[0] = 'W'; nm[1] = 'S'; nm[2] = '-';
nm[3] = '0' + (char)(g_ws_count + 1); nm[4] = 0;
g_ws[g_ws_count].page = 1;
g_ws_count++;
}
/* 关闭工作区:窗口释放到关闭后的当前工作区(Kate release 语义) */
static void ws_close(int id) {
if (g_ws_count <= 1 || id < 0 || id >= g_ws_count) return;
int cur_after = g_ws_cur;
if (g_ws_cur == id) cur_after = (id > 0) ? id - 1 : 0;
else if (g_ws_cur > id) cur_after = g_ws_cur - 1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].ws == id) g_windows[i].ws = cur_after;
else if (g_windows[i].ws > id) g_windows[i].ws--;
}
for (int i = id; i < g_ws_count - 1; i++) g_ws[i] = g_ws[i + 1];
g_ws_count--;
g_ws_cur = cur_after;
g_taskview = 0;
}
static void page_switch(int p) {
if (p < 1 || p > 3 + g_cpage_count) return;
g_page = p;
}
/* 自定义页:为尚未挂载的外部 .elf 应用新建页面 */
static void cpage_add(void) {
if (g_cpage_count >= MAX_CPAGES) return;
for (int a = 0; a < g_app_count; a++) {
if (!g_apps[a].elf_name) continue;
int dup = 0;
for (int i = 0; i < g_cpage_count; i++)
if (f32_neq11(g_cpages[i].elf11, g_apps[a].elf_name)) { dup = 1; break; }
if (dup) continue;
cpage *cp = &g_cpages[g_cpage_count];
int n = kstrlen(g_apps[a].display_name);
if (n > 11) n = 11;
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 = 4 + g_cpage_count;
g_cpage_count++;
g_page = cp->id;
return;
}
}
static void cpage_remove(int idx) {
if (idx < 0 || idx >= g_cpage_count) return;
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 = 4 + i;
if (g_page == removed_page) g_page = 1;
else if (g_page > removed_page) g_page--;
}
/* ============================================================
* 事件处理
* ============================================================ */
static int rect_hit(const int r[4], int mx, int my) {
return r[2] > 0 && mx >= r[0] && mx < r[0] + r[2] &&
my >= r[1] && my < r[1] + r[3];
}
static void slider_set(const int rect[4], int *val, int mx) {
int v = (mx - rect[0]) * 100 / rect[2];
if (v < 0) v = 0;
if (v > 100) v = 100;
*val = v;
}
/* 双击判定(TSC 半秒 + 8px 容差),不更新状态 */
static int dbl_check(int mx, int my) {
if (!g_tsc_per_sec) return 0;
u64 now = rdtsc();
int dx = mx - g_last_click_x, dy = my - g_last_click_y;
return (now - g_last_click_tsc) < g_tsc_per_sec / 2 &&
dx > -8 && dx < 8 && dy > -8 && dy < 8;
}
/* ---- 1·DASHBOARD 点击 ---- */
static int dash_click(int mx, int my, int dbl) {
editor_state *ed = (editor_state *)ADDR_EDDASH;
/* SAVE 按钮 */
if (ed->has_file && rect_hit(g_save_rect, mx, my)) { editor_save(); return 1; }
/* 面板聚焦 */
for (int i = 0; i < 4; i++) {
if (!rect_hit(g_dash_rect[i], mx, my)) continue;
g_dash_focus = i;
if (i == 3) {
/* FILES 行命中 / 双击打开 */
int fx = g_dash_rect[3][0] + 2;
int fy = g_dash_rect[3][1] + KATE_PANEL_HEADER_H + 2;
int fw = g_dash_rect[3][2] - 4;
int fh = g_dash_rect[3][3] - KATE_PANEL_HEADER_H - 4;
if (mx >= fx && mx < fx + fw && my >= fy + 24) {
int max_rows = (fh - 24) / 22;
int row = (my - fy - 24) / 22;
if (row >= 0 && row < max_rows && row < g_file_count) {
if (dbl && row == g_file_sel) files_open(row);
else g_file_sel = row;
}
}
}
return 1;
}
/* 状态栏滑块 */
if (rect_hit(g_vol_rect, mx, my)) {
g_drag_slider = 1;
slider_set(g_vol_rect, &g_vol, mx);
return 1;
}
if (rect_hit(g_bri_rect, mx, my)) {
g_drag_slider = 2;
slider_set(g_bri_rect, &g_bright, mx);
return 1;
}
return 0;
}
/* ---- 2·IDE 点击 ---- */
static int ide_click(int mx, int my, int dbl) {
if (g_ide_attached && rect_hit(g_ide_tabx, mx, my)) { g_ide_attached = 0; return 1; }
if (!g_ide_attached && rect_hit(g_ide_new_rect, mx, my)) { g_ide_attached = 1; return 1; }
if (!g_ide_attached && dbl && rect_hit(g_ide_host, mx, my)) {
g_ide_attached = 1;
return 1;
}
return 0;
}
/* ---- 4·DESKTOP 点击 ---- */
static int desktop_click(int mx, int my, int dbl) {
int tb_y = (int)g_fb_h - KATE_TASKBAR_H;
/* 任务栏 */
if (my >= tb_y) {
if (rect_hit(g_tb_quit_rect, mx, my)) { g_quit = 1; return 1; }
if (rect_hit(g_tb_adopt_rect, mx, my)) {
/* 收纳窗口:全部工作区窗口收归当前工作区并还原最小化 */
for (int i = 0; i < g_win_count; i++) {
g_windows[i].ws = g_ws_cur;
g_windows[i].minimized = 0;
}
return 1;
}
for (int i = 0; i < g_win_count; i++) {
if (!g_tb_rect[i][2] || !rect_hit(g_tb_rect[i], mx, my)) continue;
if (rect_hit(g_tb_close_rect[i], mx, my)) {
win_destroy(&g_windows[i]);
} else {
win_focus(&g_windows[i]);
}
return 1;
}
return 0;
}
/* 窗口 */
desktop_window *w = win_find_at(mx, my);
if (w) {
int wid = w->id;
win_focus(w);
w = win_find(wid); /* focus 重排后重新定位 */
if (!w) return 1;
if (win_hit_close_btn(w, mx, my)) { win_destroy(w); return 1; }
if (win_hit_titlebar(w, mx, my)) {
g_dragging = 1;
g_drag_win = w->id;
g_drag_off_x = mx - w->x;
g_drag_off_y = my - w->y;
return 1;
}
/* 客户区事件 → 应用 */
if (w->app_id >= 0 && w->app_id < g_app_count &&
g_apps[w->app_id].on_event && w->app_state) {
desktop_event dev;
dev.type = EV_MOUSE_DOWN;
dev.mx = mx; dev.my = my;
dev.button = 0; dev.scancode = 0;
dev.shift = g_shift; dev.ctrl = g_ctrl; dev.alt = g_alt;
app_ctx actx;
fill_app_ctx(&actx, w);
g_apps[w->app_id].on_event(w->app_state, &actx, &dev);
}
return 1;
}
/* 图标(单击选中 / 双击启动) */
for (int i = 0; i < g_icon_count; i++) {
int ix = g_icons[i].x, iy = g_icons[i].y;
if (mx >= ix && mx < ix + ICON_W && my >= iy && my < iy + ICON_H) {
if (dbl && g_icons[i].selected) {
launch_app(g_icons[i].app_id);
} else {
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
g_icons[i].selected = 1;
}
return 1;
}
}
/* 空白:取消选择 */
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
return 1;
}
/* ---- 左键按下 ---- */
static int on_left_press(void) {
int mx = g_mouse_x, my = g_mouse_y;
int dbl = dbl_check(mx, my);
g_last_click_tsc = rdtsc();
g_last_click_x = mx; g_last_click_y = my;
/* 任务视图 overlay(模态) */
if (g_taskview) {
for (int i = 0; i <= g_ws_count; i++) {
if (!rect_hit(g_tv_card[i], mx, my)) continue;
if (i == g_ws_count) ws_create();
else if (rect_hit(g_tv_close[i], mx, my)) ws_close(i);
else ws_switch(i);
return 1;
}
g_taskview = 0; /* 背景点击关闭 */
return 1;
}
/* 右键菜单(模态) */
if (g_ctx_open) {
if (rect_hit(g_ctx_rect, mx, my)) {
for (int i = 0; i <= MAX_WS; i++) {
if (!rect_hit(g_ctx_item[i], mx, my)) continue;
int wi = g_ctx_item_ws[i];
if (wi == -1) {
/* + NEW WORKSPACE:新建并迁移 */
ws_create();
desktop_window *w = win_find(g_ctx_win);
if (w && g_ws_count > 0) w->ws = g_ws_count - 1;
} else if (wi >= 0) {
desktop_window *w = win_find(g_ctx_win);
if (w) w->ws = wi;
}
break;
}
}
g_ctx_open = 0;
return 1;
}
/* 顶栏 */
if (my < KATE_TOPBAR_H) {
if (rect_hit(g_tvbtn_rect, mx, my)) { g_taskview = 1; return 1; }
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 < 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; }
}
if (rect_hit(g_pgadd_rect, mx, my)) { cpage_add(); return 1; }
return 0;
}
/* 页面内容 */
if (g_page == 1) return dash_click(mx, my, dbl);
if (g_page == 2) return ide_click(mx, my, dbl);
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;
if (launch) {
for (int i = 0; i < g_cpage_count; i++) {
if (g_cpages[i].id != g_page) continue;
launch_external_elf(g_cpages[i].elf11);
break;
}
return 1;
}
return 0;
}
return 0;
}
/* ---- 右键按下:窗口/任务栏项 → 移到工作区菜单 ---- */
static int on_right_press(void) {
int mx = g_mouse_x, my = g_mouse_y;
if (g_taskview) { g_taskview = 0; return 1; }
if (g_ctx_open) { g_ctx_open = 0; return 1; }
if (g_page != 3) return 0;
int tb_y = (int)g_fb_h - KATE_TASKBAR_H;
if (my >= tb_y) {
for (int i = 0; i < g_win_count; i++) {
if (!g_tb_rect[i][2] || !rect_hit(g_tb_rect[i], mx, my)) continue;
int lmap[MAX_WS];
int ln = ctx_list_build(lmap);
int mh = 24 + ((ln ? ln : 1) + 1) * 22 + 8;
g_ctx_win = g_windows[i].id;
g_ctx_x = mx;
g_ctx_y = my - mh; /* 菜单向上展开 */
if (g_ctx_y < KATE_TOPBAR_H) g_ctx_y = my;
g_ctx_open = 1;
return 1;
}
return 0;
}
desktop_window *w = win_find_at(mx, my);
if (w) {
g_ctx_win = w->id;
g_ctx_x = mx; g_ctx_y = my;
g_ctx_open = 1;
return 1;
}
return 0;
}
/* ---- 键盘 ---- */
static int handle_key(u8 sc) {
if (sc == 0xE0) { g_e0 = 1; return 0; }
if (sc == 0x2A || sc == 0x36) { g_shift = 1; return 0; }
if (sc == 0xAA || sc == 0xB6) { g_shift = 0; return 0; }
if (sc == 0x1D) { g_ctrl = 1; g_e0 = 0; return 0; }
if (sc == 0x9D) { g_ctrl = 0; return 0; }
if (sc == 0x38) { g_alt = 1; g_e0 = 0; return 0; }
if (sc == 0xB8) { g_alt = 0; return 0; }
if (sc & 0x80) { g_e0 = 0; return 0; } /* 其他释放码忽略 */
/* Esc:关闭 overlay */
if (sc == 0x01) {
if (g_taskview) { g_taskview = 0; return 1; }
if (g_ctx_open) { g_ctx_open = 0; return 1; }
return 0;
}
if (g_taskview || g_ctx_open) return 0; /* 模态期间吞掉按键 */
/* Ctrl+Tab 切页 / Ctrl+Shift+Tab 上下文菜单(DESKTOP 页) */
if (g_ctrl && sc == 0x0F) {
if (g_shift) {
/* Ctrl+Shift+Tab:对当前聚焦窗口打开"移到工作区"菜单 */
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;
return 1;
}
return 0;
}
int maxp = 3 + g_cpage_count;
int p = g_page + 1;
if (p > maxp) p = 1;
page_switch(p);
return 1;
}
if (g_ctrl && sc == 0x1F) { editor_save(); return 1; }
/* 页面路由 */
if (g_page == 1) {
if (g_dash_focus <= 1) {
bash_key((bash_state *)(g_dash_focus == 0 ? ADDR_TERM1 : ADDR_TERM2),
sc, g_shift);
return 1;
}
if (g_dash_focus == 2) {
editor_key((editor_state *)ADDR_EDDASH, sc, g_shift);
return 1;
}
/* FILES 面板导航 */
if (sc == 0x48) { if (g_file_sel > 0) g_file_sel--; return 1; }
if (sc == 0x50) { if (g_file_sel < g_file_count - 1) g_file_sel++; return 1; }
if (sc == 0x1C) { files_open(g_file_sel); return 1; }
return 0;
}
if (g_page == 2) {
if (g_ide_attached) {
editor_key((editor_state *)ADDR_EDDASH, sc, g_shift);
return 1;
}
return 0;
}
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) {
desktop_event dev;
dev.type = EV_KEY_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
dev.scancode = sc;
dev.shift = g_shift; dev.ctrl = g_ctrl; dev.alt = g_alt;
app_ctx actx;
fill_app_ctx(&actx, fw);
g_apps[fw->app_id].on_event(fw->app_state, &actx, &dev);
return 1;
}
}
return 0;
}
/* ============================================================
* 主入口
* ============================================================ */
__attribute__((visibility("default")))
@@ -1397,170 +3125,166 @@ void dsk_entry(const dsk_boot_context *ctx) {
slog("bad context");
for (;;) __asm__("hlt");
}
g_boot_ctx = ctx;
g_fb_addr = ctx->framebuffer_address;
g_fb_w = ctx->framebuffer_width;
g_fb_h = ctx->framebuffer_height;
g_fb_pitch = ctx->framebuffer_pitch;
g_kernel_api = (const void *)ctx->dkm_kernel_api;
g_block_read = (desktop_block_read_fn)(*(u64 *)(ctx->dkm_kernel_api + 0xA8));
/* block API:kernel_api + 0xA8 → block_api 结构,read @ +0x10, write @ +0x18 */
{
u64 blk = *(u64 *)(ctx->dkm_kernel_api + 0xA8);
g_block_read = blk ? (desktop_block_read_fn)*(u64 *)(blk + 16) : 0;
g_block_write = blk ? (desktop_block_write_fn)*(u64 *)(blk + 24) : 0;
}
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();
g_rtc_boot_sec = rtc_day_sec();
g_last_sec = g_rtc_boot_sec;
/* 初始化 PS/2 鼠标 */
ps2_mouse_init();
/* 注册应用 */
register_apps();
setup_desktop_icons();
/* 初始绘制 */
/* 工作区 0 */
kstrcpy(g_ws[0].name, "WS-1", 16);
g_ws[0].page = 1;
g_ws_count = 1;
g_ws_cur = 0;
/* DASHBOARD 面板共享状态 */
{
bash_state *t1 = (bash_state *)ADDR_TERM1;
bash_reset(t1, 640, 280);
bash_puts(t1, "Deshab Bash v0.1 - TERM-01\nType help for commands\n\n", KS_TEXT_PRIMARY);
bash_state *t2 = (bash_state *)ADDR_TERM2;
bash_reset(t2, 640, 280);
bash_puts(t2, "Deshab Bash v0.1 - TERM-02\nType help for commands\n\n", KS_TEXT_PRIMARY);
editor_reset((editor_state *)ADDR_EDDASH,
"Welcome to Deshab Editor\n"
"\n"
"- Double-click a file in FILES panel to open it here\n"
"- SAVE button / Ctrl+S writes back to FAT32 disk\n"
"- IDE page hosts this same editor full-screen\n");
}
files_refresh();
boot_screen();
g_booted = 1;
redraw_all();
slog("desktop ready");
/* 主事件循环 */
for (;;) {
while (!g_quit) {
int need_redraw = 0;
g_mouse_has_pkt = 0;
int moved = 0;
/* 轮询 PS/2 鼠标 */
if (ps2_mouse_poll()) {
/* 拖拽处理 - 需要全屏重绘 */
if (g_dragging && g_drag_win >= 0) {
/* 鼠标:排空所有数据包 */
while (ps2_mouse_poll()) moved = 1;
if (g_left_pressed) {
g_left_pressed = 0;
if (on_left_press()) need_redraw = 1;
}
if (g_right_pressed) {
g_right_pressed = 0;
if (on_right_press()) need_redraw = 1;
}
if (g_left_released) {
g_left_released = 0;
if (g_dragging || g_drag_slider) {
g_dragging = 0;
g_drag_win = -1;
g_drag_slider = 0;
need_redraw = 1;
}
}
/* 窗口拖拽 */
if (moved && g_dragging && g_drag_win >= 0) {
desktop_window *w = win_find(g_drag_win);
if (w) {
w->x = g_mouse_x - g_drag_off_x;
w->y = g_mouse_y - g_drag_off_y;
if (w->y < KATE_TOPBAR_H) w->y = KATE_TOPBAR_H;
if (w->x < 0 - w->w + 40) w->x = 0 - w->w + 40;
if (w->x > (int)g_fb_w - 40) w->x = (int)g_fb_w - 40;
if (w->y > (int)g_fb_h - KATE_TASKBAR_H - 20)
w->y = (int)g_fb_h - KATE_TASKBAR_H - 20;
}
need_redraw = 1;
}
/* 鼠标点击事件 - 需要全屏重绘 */
if (g_mouse_btn) {
/* 检查任务栏点击 */
int tb_y = (int)g_fb_h - TASKBAR_H;
if (g_mouse_y >= tb_y) {
/* 快速启动图标 */
int qx = 64;
for (int i = 0; i < g_app_count && i < 4; i++) {
int icon_x = qx + i * 40;
if (g_mouse_x >= icon_x && g_mouse_x < icon_x + 40) {
launch_app(i);
need_redraw = 1;
break;
}
}
} else {
/* 窗口命中测试 */
desktop_window *hit = win_find_at(g_mouse_x, g_mouse_y);
if (hit) {
win_bring_to_front(hit);
/* 取消其他焦点 */
for (int i = 0; i < g_win_count; i++)
g_windows[i].focused = (g_windows[i].id == hit->id);
g_focused_win = hit->id;
/* 滑块拖拽 */
if (moved && g_drag_slider == 1) { slider_set(g_vol_rect, &g_vol, g_mouse_x); need_redraw = 1; }
if (moved && g_drag_slider == 2) { slider_set(g_bri_rect, &g_bright, g_mouse_x); need_redraw = 1; }
/* 检查关闭按钮 */
if (win_hit_close_btn(hit, g_mouse_x, g_mouse_y)) {
win_destroy(hit);
need_redraw = 1;
}
/* 检查标题栏拖拽 */
else if (win_hit_titlebar(hit, g_mouse_x, g_mouse_y)) {
g_dragging = 1;
g_drag_win = hit->id;
g_drag_off_x = g_mouse_x - hit->x;
g_drag_off_y = g_mouse_y - hit->y;
}
/* 传递鼠标事件给应用 */
else if (hit->app_id >= 0 && g_apps[hit->app_id].on_event && hit->app_state) {
desktop_event dev;
dev.type = EV_MOUSE_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
app_ctx actx;
actx.fb = &g_fb; actx.win = hit;
actx.client_x = hit->x + BORDER_W;
actx.client_y = hit->y + TITLEBAR_H;
actx.client_w = hit->w - 2*BORDER_W;
actx.client_h = hit->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[hit->app_id].on_event(hit->app_state, &actx, &dev);
}
} else {
/* 桌面图标点击 */
for (int i = 0; i < g_icon_count; i++) {
int ix = g_icons[i].x, iy = g_icons[i].y;
if (g_mouse_x >= ix && g_mouse_x < ix + 32 &&
g_mouse_y >= iy && g_mouse_y < iy + 32) {
launch_app(g_icons[i].app_id);
need_redraw = 1;
break;
}
}
}
}
} else {
if (g_dragging) {
g_dragging = 0;
g_drag_win = -1;
}
}
}
/* 轮询 PS/2 键盘 */
{
/* 键盘:排空 */
for (;;) {
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
u8 sc = data;
if (sc == 0xE0) { g_e0 = 1; goto next; }
if (sc == 0x2A || sc == 0x36) { g_shift = 1; goto next; }
if (sc == 0xAA || sc == 0xB6) { g_shift = 0; goto next; }
if (sc & 0x80) { g_e0 = 0; goto next; }
if (!(st & 1) || (st & 0x20)) break;
u8 sc = inb(0x60);
if (handle_key(sc)) need_redraw = 1;
}
/* 发送键盘事件到焦点窗口 */
if (g_focused_win >= 0) {
desktop_window *fw = win_find(g_focused_win);
if (fw && fw->app_id >= 0 && g_apps[fw->app_id].on_event && fw->app_state) {
desktop_event dev;
dev.type = EV_KEY_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
dev.scancode = sc;
dev.shift = g_shift;
dev.ctrl = g_ctrl;
dev.alt = g_alt;
app_ctx actx;
actx.fb = &g_fb; actx.win = fw;
actx.client_x = fw->x + BORDER_W;
actx.client_y = fw->y + TITLEBAR_H;
actx.client_w = fw->w - 2*BORDER_W;
actx.client_h = fw->h - TITLEBAR_H - BORDER_W;
actx.should_exit = 0;
actx.block_read = g_block_read;
actx.kernel_api = g_kernel_api;
g_apps[fw->app_id].on_event(fw->app_state, &actx, &dev);
need_redraw = 1;
/* RTC 秒 tick:DASHBOARD 状态栏时钟/UP 计时刷新 */
{
int sec = rtc_day_sec();
if (sec != g_last_sec) {
g_last_sec = sec;
if (g_page == 1 && !g_taskview) need_redraw = 1;
}
}
g_e0 = 0;
}
}
next:
if (need_redraw) {
cursor_restore_bg();
/* 脏矩形渲染:合并脏矩形,只 flip 变化区域 */
if (g_full_redraw || g_dirty_count == 0) {
/* 全屏重绘 */
redraw_all();
} else if (g_mouse_has_pkt) {
/* 鼠标移动但无需全屏重绘:只更新光标位置 */
flip_buffer();
} else {
/* 合并脏矩形 */
int dx, dy, dw, dh;
merge_dirty_rects(&dx, &dy, &dw, &dh);
/* 重绘并 flip 脏区域 */
if (dw > 0 && dh > 0) {
/* 清除脏矩形标记 */
clear_dirty_rects();
/* 重绘整个屏幕(简化:暂不支持局部重绘) */
redraw_all();
/* Flip 合并后的脏区域 */
flip_rect(dx, dy, dw, dh);
}
}
} 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");
}
slog("exit");
/* 返回到 DSK */
}
+3 -1
View File
@@ -17,8 +17,10 @@ SECTIONS
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
.bss : ALIGN(4K) {
*(.bss*)
} :data
}
+5 -13
View File
@@ -66,7 +66,7 @@ static block_write_fn g_block_write;
static u64 g_tsc_per_ms = 0;
static u8 g_disk[131072];
static u8 g_cluster[4096];
static u8 g_fdata[262144];
static u8 g_fdata[1048576];
static int fat32_read_sectors(u32 lba, u32 count, u8 *out) { return g_block_read ? g_block_read(0,lba,count,out) : -1; }
static int fat32_write_sectors(u32 lba, u32 count, const u8 *buf) { return g_block_write ? g_block_write(0,lba,count,buf) : -1; }
@@ -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) */
@@ -529,7 +521,7 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) {
if (!lc || min_vaddr == ~0ULL) return -4;
u64 isize = max_vaddr - min_vaddr;
isize = (isize + 0xFFF) & ~0xFFFULL;
/* 加载缓冲:需容纳 shell.elf(~536KB,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */
/* 加载缓冲:需容纳 shell.elf(~862KB memsz,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */
static u8 ibuf[1048576];
u8 *image = ibuf;
if (isize > 1048576) return -5; /* 镜像过大,防止越界 */
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -302,7 +302,7 @@ static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius,
}
static void delay_frame(void) {
delay_ms_fi(33); /* 实机: 基于 TSC 的 33ms 帧间隔 (~30fps) */
delay_ms_fi(11); /* 实机: 基于 TSC 的 11ms 帧间隔 (原33ms/3,加快打字机效果) */
}
#include "text_bitmaps.c"
Binary file not shown.
+114
View File
@@ -0,0 +1,114 @@
# Linux 6.6 LTS for UTSM Dual-Kernel Architecture
## Overview
This directory contains the Linux 6.6 LTS configuration and build scripts for the
UTSM + Linux dual-kernel architecture. Linux runs as a VMX non-root guest under
the UTSM hypervisor (VMX root).
## Prerequisites
- **WSL** (Windows Subsystem for Linux) with Ubuntu 22.04+ installed
```powersShell
wsl --install -d Ubuntu-22.04
```
- Build dependencies (inside WSL):
```bash
sudo apt update
sudo apt install -y build-essential bc flex bison libelf-dev libssl-dev cpio gzip git
```
- ~3GB free disk space
## Building Linux
### 1. From WSL
```bash
# Navigate to the project (adjust path for your WSL mount)
cd /mnt/d/Code/Deshab/CODE/linux
# Make the build script executable
chmod +x build.sh
# Run the build (clones Linux 6.6 LTS, configures, builds)
./build.sh
```
The build will:
1. Clone Linux 6.6 LTS source to `CODE/linux/src/`
2. Apply UTSM patches from `CODE/linux/patches/` (if any)
3. Configure with `configs/utsm_x86_64_defconfig`
4. Build `bzImage`
5. Copy to `SYSTEM/boot/linux-bzImage`
6. Build initramfs if `CODE/linux/initramfs/` exists
### 2. Enable in Limine
After building, uncomment the Linux module entries in `SYSTEM/limine/limine.conf`:
```
module_path: boot():/boot/linux-bzImage
module_cmdline: linux:bzImage
module_path: boot():/boot/linux-initrd.img
module_cmdline: linux:initrd
```
### 3. Rebuild the disk image
```PowerShell
cd d:\Code\Deshab
.\build.bat
```
### 4. Test with QEMU
**Note:** VMX requires hardware virtualization support. QEMU's TCG (software
emulation) does NOT support VMX instructions. To test the VMX path:
- **Real hardware**: Boot on an Intel machine with VT-x enabled in BIOS
- **QEMU with WHPX**: `qemu-system-x86_64 -accel whpx -cpu max` (may not expose
nested VMX to the guest)
- **QEMU with KVM** (Linux host only): `qemu-system-x86_64 -enable-kvm -cpu host`
Without VMX, UTSM gracefully falls back to the normal DSK boot flow.
## Configuration Details
The `utsm_x86_64_defconfig` enables:
- 64-bit kernel with SMP support
- Serial console (8250/16550 at 0x3F8, 115200 baud)
- virtio: console, block, net, fs (for UTSM host backends)
- ext4, FAT, tmpfs, proc, sysfs filesystems
- No module loading (all drivers built-in for Phase 1)
- No EFI stub (UTSM uses custom VMX boot protocol)
- Debug info and printk timestamps
## File Structure
```
CODE/linux/
├── configs/
│ └── utsm_x86_64_defconfig # Kernel configuration
├── patches/ # UTSM-specific kernel patches
│ ├── utsm-hcall.patch # /dev/utsm hypercall driver (Phase 1.3)
│ ├── utsm-ipc.patch # Shared memory IPC driver (Phase 1.3)
│ └── utsm-virtio.patch # virtio enhancements (Phase 1.4+)
├── initramfs/ # Minimal initramfs contents (optional)
├── build.sh # Build script (run in WSL/Linux)
└── README.md # This file
```
## Boot Flow
```
Limine (UEFI)
→ utsm.elf (VMX root)
→ vmm_init() + vmm_self_test()
→ linux_loader_init() (parse bzImage, set up boot_params, EPT)
→ linux_launch() (VMCS config + vmlaunch)
→ Linux 6.6 startup_64 (VMX non-root)
→ Serial console output
→ virtio-console ↔ UTSM
→ virtio-fs mount /utsm
→ User space (init/shell)
```
+208
View File
@@ -0,0 +1,208 @@
#!/bin/bash
#
# Build Linux 6.6 LTS bzImage for UTSM dual-kernel guest.
#
# Prerequisites:
# - Linux build environment (WSL, native Linux, or Docker)
# - gcc, make, bc, flex, bison, libelf-dev, libssl-dev
# - ~3GB free disk space
#
# Usage:
# cd CODE/linux
# ./build.sh
#
# Output:
# - SYSTEM/boot/linux-bzImage (kernel image)
# - SYSTEM/boot/linux-initrd.img (minimal initramfs, if initramfs/ exists)
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
LINUX_SRC="${LINUX_SRC:-$SCRIPT_DIR/src}"
OUTPUT_DIR="$PROJECT_ROOT/SYSTEM/boot"
CONFIG_FILE="$SCRIPT_DIR/configs/utsm_x86_64_defconfig"
INITRAMFS_DIR="$SCRIPT_DIR/initramfs"
echo "[build] Linux kernel build for UTSM"
echo "[build] Project root: $PROJECT_ROOT"
echo "[build] Linux source: $LINUX_SRC"
# 1. Clone Linux 6.6 LTS if not present
if [ ! -d "$LINUX_SRC" ]; then
echo "[build] Cloning Linux 6.6 LTS..."
# 优先 kernel.org,失败则 fallback 清华镜像
git clone --depth 1 --branch v6.6 \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \
"$LINUX_SRC" || {
echo "[build] kernel.org failed, trying TUNA mirror..."
git clone --depth 1 --branch v6.6 \
https://mirrors.tuna.tsinghua.edu.cn/git/linux.git \
"$LINUX_SRC"
}
fi
cd "$LINUX_SRC"
# 2. Apply UTSM patches if any
PATCH_DIR="$SCRIPT_DIR/patches"
if [ -d "$PATCH_DIR" ] && [ "$(ls -A $PATCH_DIR 2>/dev/null)" ]; then
echo "[build] Applying UTSM patches..."
# 2a. Apply .patch files (if any)
for patch in "$PATCH_DIR"/*.patch; do
if [ -f "$patch" ]; then
echo "[build] applying $(basename $patch)"
git apply --check "$patch" 2>/dev/null && git apply "$patch" || \
echo "[build] WARNING: $patch already applied or failed"
fi
done
# 2b. Install UTSM driver source files into drivers/utsm/
UTSM_DRV_DIR="$LINUX_SRC/drivers/utsm"
echo "[build] installing UTSM driver into drivers/utsm/"
mkdir -p "$UTSM_DRV_DIR/include"
# Copy driver source, Kconfig, and Makefile
cp "$PATCH_DIR/utsm_hcall.c" "$UTSM_DRV_DIR/utsm_hcall.c"
cp "$PATCH_DIR/utsm_Kconfig" "$UTSM_DRV_DIR/Kconfig"
cp "$PATCH_DIR/utsm_Makefile" "$UTSM_DRV_DIR/Makefile"
# Copy shared IPC protocol header (included by both UTSM and Linux)
cp "$PROJECT_ROOT/CODE/utsm-ipc/ipc_proto.h" "$UTSM_DRV_DIR/include/ipc_proto.h"
# Add UTSM to drivers/Kconfig (if not already present)
if ! grep -q "drivers/utsm/Kconfig" "$LINUX_SRC/drivers/Kconfig"; then
echo '[build] adding source "drivers/utsm/Kconfig" to drivers/Kconfig'
echo 'source "drivers/utsm/Kconfig"' >> "$LINUX_SRC/drivers/Kconfig"
fi
# Add UTSM to drivers/Makefile (if not already present)
if ! grep -q "CONFIG_UTSM_HCALL" "$LINUX_SRC/drivers/Makefile"; then
echo '[build] adding obj-$(CONFIG_UTSM_HCALL) += utsm/ to drivers/Makefile'
echo 'obj-$(CONFIG_UTSM_HCALL) += utsm/' >> "$LINUX_SRC/drivers/Makefile"
fi
echo "[build] UTSM driver installed:"
echo "[build] $UTSM_DRV_DIR/utsm_hcall.c"
echo "[build] $UTSM_DRV_DIR/Kconfig"
echo "[build] $UTSM_DRV_DIR/Makefile"
echo "[build] $UTSM_DRV_DIR/include/ipc_proto.h"
fi
# 3. Configure
echo "[build] Configuring kernel..."
cp "$CONFIG_FILE" .config
make olddefconfig
# 4. Build bzImage
echo "[build] Building bzImage (this may take 10-30 minutes)..."
make -j"$(nproc)" bzImage
# 5. Copy bzImage to SYSTEM/boot/
mkdir -p "$OUTPUT_DIR"
cp arch/x86/boot/bzImage "$OUTPUT_DIR/linux-bzImage"
echo "[build] bzImage: $OUTPUT_DIR/linux-bzImage ($(du -h $OUTPUT_DIR/linux-bzImage | cut -f1))"
# 6. Build initramfs if initramfs/ directory exists
if [ -d "$INITRAMFS_DIR" ]; then
echo "[build] Building initramfs..."
INITRAMFS_TMP="$SCRIPT_DIR/initramfs.tmp"
rm -rf "$INITRAMFS_TMP"
mkdir -p "$INITRAMFS_TMP/bin"
# Copy initramfs contents (init + bin/)
cp -a "$INITRAMFS_DIR"/* "$INITRAMFS_TMP"/
# 6a. Static busybox(缓存于 CODE/linux/.cache/,只下载一次)
# busybox.net 官方预编译 x86_64 musl 静态二进制,含 sh/mount/uname 等 applet。
BUSYBOX_CACHE="$SCRIPT_DIR/.cache/busybox"
BUSYBOX_URL="https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox"
if [ ! -x "$BUSYBOX_CACHE" ]; then
echo "[build] Downloading static busybox..."
mkdir -p "$(dirname "$BUSYBOX_CACHE")"
curl -fL --retry 3 -o "$BUSYBOX_CACHE" "$BUSYBOX_URL" || \
wget -O "$BUSYBOX_CACHE" "$BUSYBOX_URL"
chmod +x "$BUSYBOX_CACHE"
fi
cp "$BUSYBOX_CACHE" "$INITRAMFS_TMP/bin/busybox"
chmod +x "$INITRAMFS_TMP/bin/busybox"
echo "[build] busybox: $(du -h "$INITRAMFS_TMP/bin/busybox" | cut -f1)"
# 6b. 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)..."
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'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "[utsm-linux] Boot complete"
exec /bin/sh
INITEOF
chmod +x "$INITRAMFS_TMP/init"
fi
# 6c. 用内核自带 gen_init_cpio 打包(无需 root、无需 cpio 二进制)。
# spec 文件声明目录/设备节点/文件/符号链接;设备节点元数据直接写入
# cpio 归档,guest 内核 unpack 时即有 /dev/console 与 /dev/ttyS0。
GEN_INIT_CPIO="$LINUX_SRC/usr/gen_init_cpio"
if [ ! -x "$GEN_INIT_CPIO" ]; then
echo "[build] Building gen_init_cpio host tool..."
make usr/ >/dev/null
fi
SPEC="$SCRIPT_DIR/initramfs.tmp.spec"
{
echo "dir /bin 755 0 0"
echo "dir /dev 755 0 0"
echo "dir /proc 755 0 0"
echo "dir /sys 755 0 0"
# 控制台设备节点:kernel console=ttyS0 输出与 busybox sh 依赖
echo "nod /dev/console 600 0 0 c 5 1"
echo "nod /dev/null 666 0 0 c 1 3"
echo "nod /dev/ttyS0 600 0 0 c 4 64"
echo "file /init $INITRAMFS_TMP/init 755 0 0"
echo "file /bin/busybox $INITRAMFS_TMP/bin/busybox 755 0 0"
if [ -f "$INITRAMFS_TMP/bin/utsm_exec_daemon" ]; then
echo "file /bin/utsm_exec_daemon $INITRAMFS_TMP/bin/utsm_exec_daemon 755 0 0"
fi
# busybox applet 符号链接(init 脚本与调试命令依赖)
for app in sh mount umount echo sleep uname ls cat ps setsid cttyhack; do
echo "slink /bin/$app /bin/busybox 777 0 0"
done
} > "$SPEC"
"$GEN_INIT_CPIO" "$SPEC" | gzip -9 > "$OUTPUT_DIR/linux-initrd.img"
echo "[build] initrd: $OUTPUT_DIR/linux-initrd.img ($(du -h $OUTPUT_DIR/linux-initrd.img | cut -f1))"
rm -rf "$INITRAMFS_TMP" "$SPEC"
cd "$LINUX_SRC"
fi
echo "[build] Done. Copy linux-bzImage (and linux-initrd.img) to your FAT32 image."
echo "[build] Limine will load them as boot modules for UTSM."
# 7. Enable Linux modules in limine.conf (uncomment the 4 commented lines)
LIMINE_CONF="$PROJECT_ROOT/SYSTEM/limine/limine.conf"
if [ -f "$LIMINE_CONF" ]; then
if grep -q "^ # module_path: boot():/boot/linux-bzImage" "$LIMINE_CONF"; then
echo "[build] Enabling Linux modules in limine.conf..."
sed -i 's|^ # module_path: boot():/boot/linux-bzImage| module_path: boot():/boot/linux-bzImage|' "$LIMINE_CONF"
sed -i 's|^ # module_cmdline: linux:bzImage| module_cmdline: linux:bzImage|' "$LIMINE_CONF"
sed -i 's|^ # module_path: boot():/boot/linux-initrd.img| module_path: boot():/boot/linux-initrd.img|' "$LIMINE_CONF"
sed -i 's|^ # module_cmdline: linux:initrd| module_cmdline: linux:initrd|' "$LIMINE_CONF"
echo "[build] Linux modules enabled in $LIMINE_CONF"
echo "[build] Run build.bat to rebuild the disk image with Linux modules"
else
echo "[build] limine.conf already has Linux modules enabled (or pattern changed)"
fi
fi
+117
View File
@@ -0,0 +1,117 @@
#!/bin/bash
#
# check_env.sh — Linux 内核构建环境依赖检查脚本
#
# 用途: 在 WSL/Linux 中运行,验证构建 Linux 6.6 LTS bzImage 所需依赖
#
# 使用:
# ! wsl bash /mnt/d/Code/Deshab/CODE/linux/check_env.sh
#
# 退出码:
# 0 = 所有依赖已满足
# 1 = 缺少依赖(脚本会打印安装命令)
set -e
echo "[check] Linux 内核构建环境检查"
echo "[check] ==========================="
# 1. 检查操作系统
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "[check] OS: $NAME $VERSION"
if [ "$ID" != "ubuntu" ] && [ "$ID_LIKE" != "debian" ]; then
echo "[check] WARNING: 非 Ubuntu/Debian,依赖名可能不同"
fi
else
echo "[check] WARNING: 无法识别操作系统"
fi
# 2. 检查必要包
echo "[check]"
echo "[check] 检查构建依赖..."
MISSING=()
for pkg in build-essential bc flex bison libelf-dev libssl-dev cpio gzip git; do
if dpkg -s "$pkg" >/dev/null 2>&1; then
echo "[check] ✓ $pkg"
else
echo "[check] ✗ $pkg (缺失)"
MISSING+=("$pkg")
fi
done
# 3. 检查 gcc 版本
echo "[check]"
echo "[check] 工具链版本:"
if command -v gcc >/dev/null 2>&1; then
GCC_VER=$(gcc -dumpversion)
echo "[check] gcc: $GCC_VER"
if [ "$(printf '%s\n' "11" "$GCC_VER" | sort -V | head -n1)" != "11" ]; then
echo "[check] WARNING: gcc 版本 < 11,Linux 6.6 可能需要 gcc 11+"
fi
else
echo "[check] ✗ gcc 未安装"
MISSING+=("gcc")
fi
if command -v make >/dev/null 2>&1; then
echo "[check] make: $(make --version | head -1 | awk '{print $3}')"
fi
# 4. 检查磁盘空间(需要 ~3GB)
echo "[check]"
PROJECT_ROOT="/mnt/d/Code/Deshab"
if [ -d "$PROJECT_ROOT" ]; then
AVAILABLE_KB=$(df "$PROJECT_ROOT" | awk 'NR==2 {print $4}')
AVAILABLE_GB=$((AVAILABLE_KB / 1024 / 1024))
echo "[check] 磁盘空间: ${AVAILABLE_GB}GB 可用 ($PROJECT_ROOT)"
if [ "$AVAILABLE_GB" -lt 3 ]; then
echo "[check] ✗ 磁盘空间不足 3GB"
MISSING+=("disk_space")
else
echo "[check] ✓ 磁盘空间充足"
fi
fi
# 5. 检查访问 Windows D: 盘
echo "[check]"
if [ -d "/mnt/d/Code/Deshab" ]; then
echo "[check] ✓ WSL 可访问 /mnt/d/Code/Deshab"
else
echo "[check] ✗ WSL 无法访问 /mnt/d/Code/Deshab"
echo "[check] 请确认在 WSL 中执行,且 D: 盘已挂载"
MISSING+=("wsl_mount")
fi
# 6. 检查 git 网络(克隆 Linux 6.6 需要)
echo "[check]"
echo "[check] 测试 Git 网络连通..."
if timeout 10 git ls-remote https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git v6.6 >/dev/null 2>&1; then
echo "[check] ✓ kernel.org 可达"
else
echo "[check] ✗ kernel.org 不可达,尝试清华镜像..."
if timeout 10 git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/linux.git v6.6 >/dev/null 2>&1; then
echo "[check] ✓ 清华镜像可达(build.sh 会自动使用)"
else
echo "[check] ✗ 两个源都不可达,请检查网络/代理"
MISSING+=("network")
fi
fi
# 7. 总结
echo "[check]"
echo "[check] ==========================="
if [ ${#MISSING[@]} -gt 0 ]; then
echo "[check] 缺失: ${MISSING[*]}"
echo "[check]"
echo "[check] 请运行以下命令安装依赖:"
echo "[check] sudo apt update"
echo "[check] sudo apt install -y build-essential bc flex bison libelf-dev libssl-dev cpio gzip git"
exit 1
fi
echo "[check] ✓ 所有依赖已满足,可以构建 Linux 内核"
echo "[check]"
echo "[check] 下一步执行:"
echo "[check] cd /mnt/d/Code/Deshab/CODE/linux && chmod +x build.sh && ./build.sh"
exit 0
+231
View File
@@ -0,0 +1,231 @@
#
# Linux 6.6 LTS minimal config for UTSM dual-kernel guest
#
# Build: make utsm_x86_64_defconfig && make -j$(nproc) bzImage
#
# Key design choices:
# - 64-bit only (CONFIG_64BIT=y)
# - Serial console (8250/16550 at 0x3F8)
# - virtio: console, block, net, fs (for UTSM host backends)
# - EFI stub disabled (UTSM uses custom VMX boot, not EFI)
# - Built-in drivers (no module loading needed for Phase 1)
# - No SMP (single CPU for Phase 1 simplicity)
# - printk to serial for debugging
CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_ARCH_MMAP_RND_BITS_MIN=28
CONFIG_ARCH_MMAP_RND_BITS_MAX=32
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_ARCH_MMAP_RND_BITS=28
CONFIG_SMP=y
CONFIG_NR_CPUS=4
CONFIG_X86_64_SMP=y
CONFIG_SCHED_MC=y
CONFIG_SCHED_SMT=y
# Processor type
CONFIG_MK8=y
CONFIG_X86_CPU=y
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_TSC=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_CMOV=y
CONFIG_X86_MINIMUM_CPU_FAMILY=64
CONFIG_PROCESSOR_SELECT=y
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_CENTAUR=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_DMI=y
# Memory
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_DIRECT_GBPAGES=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
CONFIG_X86_PMEM_LEGACY_DEVICE=y
CONFIG_X86_PMEM_LEGACY=y
CONFIG_X86_CHECK_BIOS_CORRUPTION=y
CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK=y
CONFIG_X86_RESERVE_LOW=64
CONFIG_MTRR=y
CONFIG_MTRR_SANITIZER=y
CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0
CONFIG_X86_PAT=y
CONFIG_ARCH_USES_PG_UNCACHED=y
CONFIG_ARCH_RANDOM=y
CONFIG_X86_SMAP=y
CONFIG_X86_INTEL_UMIP=y
CONFIG_X86_UMIP=y
# Power management (minimal)
CONFIG_PM=y
CONFIG_PM_CLK=y
# Bus options
CONFIG_PCI=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_MSI=y
CONFIG_PCI_MSI_IRQ_DOMAIN=y
CONFIG_PCI_LABEL=y
CONFIG_ISA_DMA_API=y
CONFIG_AMD_NB=y
#Executable file formats
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_BINFMT_MISC=y
# Networking
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_SYN_COOKIES=y
CONFIG_TCP_CONG_CUBIC=y
# Device Drivers
# Serial console (critical: UTSM captures serial output)
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_SERIAL=y
# PCI host bridge
CONFIG_PCI_HOST_GENERIC=y
# virtio (critical: UTSM provides virtio backends)
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
# virtio console (Phase 1.4: UTSM↔Linux console)
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_VIRTIO=y
# UTSM dual-kernel hypercall and IPC driver (Phase 1.3)
CONFIG_UTSM_HCALL=y
# virtio block (Phase 2: block device)
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
# virtio net (Phase 2: network)
CONFIG_VIRTIO_NET=y
# virtio-fs (Phase 1.5: shared filesystem)
CONFIG_VIRTIO_FS=y
CONFIG_FUSE_FS=y
# Filesystems
CONFIG_EXT4_FS=y
CONFIG_EXT4_USE_FOR_EXT2=y
CONFIG_FAT_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
CONFIG_TMPFS=y
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# Block devices
CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_LOOP=y
# Input (minimal)
CONFIG_INPUT=y
CONFIG_INPUT_KEYBOARD=y
# RTC
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# PNP/ACPI (minimal for QEMU)
CONFIG_ACPI=y
CONFIG_ACPI_AC=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_FAN=y
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_THERMAL=y
# File systems
CONFIG_FHANDLE=y
# Kernel hacking
CONFIG_PRINTK=y
CONFIG_PRINTK_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_DEBUG_WX=y
# Security (minimal)
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
# Crypto (minimal)
CONFIG_CRYPTO=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_SHA256=y
# Library routines
CONFIG_CRC16=y
CONFIG_CRC32=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
# Initramfs
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
# No module support (build everything in for Phase 1)
# CONFIG_MODULES is not set
# No EFI stub (UTSM uses custom VMX boot)
# CONFIG_EFI_STUB is not set
# CONFIG_EFI is not set
# Boot protocol
CONFIG_EARLY_PRINTK_SERIAL=y
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# Fix UTSM driver entries in Linux source tree
SRC="${1:-/tmp/linux-6.6}"
# Fix Kconfig: remove broken line, add correct one
sed -i '/utsm/d' "$SRC/drivers/Kconfig"
echo 'source "drivers/utsm/Kconfig"' >> "$SRC/drivers/Kconfig"
# Fix Makefile: remove broken line, add correct one
sed -i '/utsm/d' "$SRC/drivers/Makefile"
echo 'obj-$(CONFIG_UTSM_HCALL) += utsm/' >> "$SRC/drivers/Makefile"
echo "Fixed UTSM driver entries"
tail -2 "$SRC/drivers/Kconfig"
echo "---"
tail -2 "$SRC/drivers/Makefile"
+218
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
/* 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;
}
+56
View File
@@ -0,0 +1,56 @@
#!/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 <prog>` 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.
if [ -x /bin/utsm_exec_daemon ]; then
echo "[utsm-linux] starting utsm_exec_daemon..."
/bin/utsm_exec_daemon &
DAEMON_PID=$!
echo "[utsm-linux] daemon PID=$DAEMON_PID"
else
# 无 daemon(第一阶段 bring-up 冒烟测试构建):
# 直接在 ttyS0 上起交互 shell,验证 console 输入输出。
# 注意:此模式下 guest 不会 park,UTSM linux_launch() 不返回,
# 仅用于验证"Linux guest 进入 shell 提示符、可执行 uname -a"。
echo "[utsm-linux] WARNING: no daemon, starting interactive debug shell on ttyS0"
exec /bin/setsid /bin/cttyhack /bin/sh
fi
# 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
+33
View File
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0
#
# UTSM dual-kernel hypercall driver configuration
#
config UTSM_HCALL
tristate "UTSM dual-kernel hypercall and IPC driver"
default y
help
This driver enables communication between the Linux guest and
the UTSM monitor kernel in the Deshab dual-kernel architecture.
When loaded, it:
- Probes for the UTSM monitor via a VMCALL PING hypercall
- Maps the IPC shared memory ring buffer
- Registers /dev/utsm for userspace IPC message passing
If running on bare metal or a non-UTSM hypervisor, the driver
loads in passive mode (no /dev/utsm registered).
If unsure, say Y for UTSM guest builds, N otherwise.
config UTSM_EARLY_CONSOLE
bool "Redirect kernel printk to UTSM serial console"
depends on UTSM_HCALL
default n
help
Redirects Linux kernel console output to the UTSM monitor's
serial port via UTSM_HCALL_CONSOLE_WRITE hypercalls. Useful
for early boot debugging when the guest's own serial is not
configured.
If unsure, say N.
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# UTSM dual-kernel driver Makefile
#
obj-$(CONFIG_UTSM_HCALL) += utsm_hcall.o
# Include the shared IPC protocol header from CODE/utsm-ipc/
# This path is set up by build.sh when copying files into the source tree
ccflags-y += -I$(src)/include
+506
View File
@@ -0,0 +1,506 @@
// SPDX-License-Identifier: GPL-2.0
/*
* UTSM hypercall driver — Linux guest side.
*
* Provides:
* 1. VMCALL hypercall interface to communicate with the UTSM monitor kernel
* 2. /dev/utsm character device for userspace IPC message passing
* 3. Shared memory ring buffer mapping (queried via UTSM_HCALL_SHM_INFO)
*
* The driver is platform-agnostic: it probes for UTSM by issuing a PING
* hypercall at init. If UTSM is not present (e.g. running on bare metal
* or a different hypervisor), the driver exits gracefully.
*
* Build: configured via CONFIG_UTSM_HCALL (built-in or module).
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
/* Shared IPC protocol header (included via build.sh -I flag) */
#include <ipc_proto.h>
#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:
* RAX = UTSM_HCALL_MAGIC | op
* RDI = arg0, RSI = arg1, RDX = arg2
*
* UTSM handles the VMCALL VM-Exit and returns:
* RAX = return value
*
* Since we run in the guest, the VMCALL instruction triggers a VM-Exit
* to UTSM. The inline assembly must use the same register conventions.
*/
static inline long utsm_hcall(u64 op, u64 a0, u64 a1, u64 a2)
{
long ret;
register u64 rax asm("rax") = UTSM_HCALL_MAGIC | op;
register u64 rdi asm("rdi") = a0;
register u64 rsi asm("rsi") = a1;
register u64 rdx asm("rdx") = a2;
asm volatile("vmcall"
: "+r"(rax)
: "r"(rdi), "r"(rsi), "r"(rdx)
: "memory");
return (long)rax;
}
static inline long utsm_hcall_ping(void)
{
return utsm_hcall(UTSM_HCALL_PING, 0, 0, 0);
}
static inline long utsm_hcall_hello(void)
{
return utsm_hcall(UTSM_HCALL_HELLO, 0, 0, 0);
}
static inline long utsm_hcall_shm_info(u64 *gpa_out, u64 *size_out)
{
u64 gpa = 0, size = 0;
long ret;
/* Pass stack addresses (GVA=GPA due to identity mapping in early boot).
* UTSM translates GPA→HPA via EPT walk and writes the values there. */
ret = utsm_hcall(UTSM_HCALL_SHM_INFO, (u64)&gpa, (u64)&size, 0);
if (ret == 0) {
*gpa_out = gpa;
*size_out = size;
}
return ret;
}
static inline long utsm_hcall_console_write(const char *buf, u64 len)
{
return utsm_hcall(UTSM_HCALL_CONSOLE_WRITE, (u64)buf, len, 0);
}
/* ===== Shared memory state ===== */
static struct utsm_ipc_shm *g_shm; /* kernel virtual address of SHM */
static u64 g_shm_gpa; /* guest physical address of SHM */
static u64 g_shm_size; /* size of SHM region */
static int g_utsm_present; /* 1 = UTSM detected */
static int g_shm_mapped;
/* ===== /dev/utsm character device ===== */
static ssize_t utsm_dev_write(struct file *f, const char __user *buf,
size_t len, loff_t *off)
{
struct utsm_ipc_msg msg;
size_t copy_len;
ssize_t ret;
if (!g_shm_mapped)
return -ENODEV;
if (len == 0)
return 0;
/* Cap data to inline message size */
copy_len = min_t(size_t, len, UTSM_IPC_MSG_DATA_SIZE);
memset(&msg, 0, sizeof(msg));
msg.type = UTSM_MSG_DATA;
msg.data_len = (u32)copy_len;
if (copy_from_user(msg.data, buf, copy_len)) {
pr_err("[utsm] copy_from_user failed\n");
return -EFAULT;
}
/* Enqueue into linux_to_utsm ring (Linux produces, UTSM consumes) */
if (utsm_ipc_ring_push(&g_shm->linux_to_utsm, &msg) != 0) {
pr_warn("[utsm] linux_to_utsm ring full\n");
return -EAGAIN;
}
ret = (ssize_t)copy_len;
/* Also relay the data to the UTSM serial console via hypercall.
* This gives immediate visibility into Linux userspace output. */
if (copy_len > 0) {
char kbuf[UTSM_IPC_MSG_DATA_SIZE + 1];
memcpy(kbuf, msg.data, copy_len);
kbuf[copy_len] = '\0';
utsm_hcall_console_write(kbuf, copy_len);
}
return ret;
}
static ssize_t utsm_dev_read(struct file *f, char __user *buf,
size_t len, loff_t *off)
{
struct utsm_ipc_msg msg;
if (!g_shm_mapped)
return -ENODEV;
/* Dequeue from utsm_to_linux ring (UTSM produces, Linux consumes) */
if (utsm_ipc_ring_pop(&g_shm->utsm_to_linux, &msg) != 0) {
/* No message available */
return -EAGAIN; /* Non-blocking; userspace can poll */
}
if (msg.data_len == 0)
return 0;
if (len < msg.data_len)
return -EINVAL;
if (copy_to_user(buf, msg.data, msg.data_len))
return -EFAULT;
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,
.unlocked_ioctl = utsm_dev_ioctl,
.poll = utsm_dev_poll,
};
static struct miscdevice utsm_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = UTSM_DEV_NAME,
.fops = &utsm_fops,
.mode = 0666,
};
/* ===== Early console redirect (optional) =====
*
* Redirect kernel printk output to UTSM serial console via hypercall.
* This is useful for debugging when the guest's own serial is not set up.
* Currently disabled; can be enabled via CONFIG_UTSM_EARLY_CONSOLE.
*/
#ifdef CONFIG_UTSM_EARLY_CONSOLE
static void utsm_console_write(struct console *con, const char *s, unsigned int n)
{
if (g_utsm_present) {
char buf[256];
unsigned int chunk;
while (n > 0) {
chunk = min_t(unsigned int, n, sizeof(buf) - 1);
memcpy(buf, s, chunk);
buf[chunk] = '\0';
utsm_hcall_console_write(buf, chunk);
s += chunk;
n -= chunk;
}
}
}
static struct console utsm_console = {
.name = "utsmcon",
.write = utsm_console_write,
.flags = CON_PRINTBUFFER | CON_ENABLED,
.index = -1,
};
#endif
/* ===== Initialization ===== */
/* Forward declaration: used by init before /dev/utsm is registered */
static inline int ipc_shm_send_to_utsm(u32 type, const void *data, u32 len);
static int __init utsm_hcall_init(void)
{
long ping_ret;
u64 shm_gpa = 0, shm_size = 0;
int ret;
pr_info("[utsm] probing for UTSM monitor...\n");
/* 1. Ping UTSM to verify it's present */
ping_ret = utsm_hcall_ping();
if (ping_ret != 0x504F4E47) { /* "PONG" */
pr_info("[utsm] UTSM not detected (ping returned 0x%lx)\n", ping_ret);
pr_info("[utsm] Driver loaded in passive mode (no /dev/utsm)\n");
return 0; /* Not an error: we might be running on bare metal */
}
g_utsm_present = 1;
pr_info("[utsm] UTSM monitor detected (PONG=0x%lx)\n", ping_ret);
/* 2. Say hello */
utsm_hcall_hello();
/* 3. Query shared memory info */
ret = utsm_hcall_shm_info(&shm_gpa, &shm_size);
if (ret != 0) {
pr_err("[utsm] SHM_INFO hypercall failed: %ld\n", ret);
goto err_no_shm;
}
pr_info("[utsm] shared memory: gpa=0x%llx size=%llu KB\n",
shm_gpa, shm_size / 1024);
if (shm_gpa == 0 || shm_size == 0) {
pr_err("[utsm] invalid SHM info\n");
goto err_no_shm;
}
/* 4. Map the shared memory region.
* The GPA is a physical address from Linux's perspective.
* Use ioremap_cache for cacheable memory (the SHM region is
* configured as WB in EPT). */
g_shm = (struct utsm_ipc_shm *)ioremap_cache(shm_gpa, shm_size);
if (!g_shm) {
pr_err("[utsm] failed to ioremap SHM at 0x%llx\n", shm_gpa);
goto err_ioremap;
}
g_shm_gpa = shm_gpa;
g_shm_size = shm_size;
g_shm_mapped = 1;
/* 5. Verify SHM header */
if (g_shm->header.magic != UTSM_IPC_MAGIC) {
pr_err("[utsm] SHM magic mismatch: 0x%llx\n", g_shm->header.magic);
goto err_bad_magic;
}
pr_info("[utsm] SHM header: magic=OK version=%u utsm_ready=%u\n",
g_shm->header.version, g_shm->header.utsm_ready);
/* 6. Set linux_ready flag */
g_shm->header.linux_ready = 1;
mb(); /* Ensure visible to UTSM */
/* 7. Register /dev/utsm */
ret = misc_register(&utsm_miscdev);
if (ret) {
pr_err("[utsm] misc_register failed: %d\n", ret);
goto err_misc;
}
#ifdef CONFIG_UTSM_EARLY_CONSOLE
register_console(&utsm_console);
#endif
pr_info("[utsm] /dev/%s registered (IPC ready)\n", UTSM_DEV_NAME);
/* 8. Send a HELLO message via the ring buffer */
{
const char *hello_msg = "Linux booted OK";
ipc_shm_send_to_utsm(UTSM_MSG_DATA, hello_msg, 15);
}
return 0;
err_misc:
g_shm->header.linux_ready = 0;
err_bad_magic:
iounmap(g_shm);
g_shm = NULL;
g_shm_mapped = 0;
err_ioremap:
err_no_shm:
g_utsm_present = 0;
return -ENODEV;
}
/* Helper to send a message (used by init before /dev/utsm is available) */
static inline int ipc_shm_send_to_utsm(u32 type, const void *data, u32 len)
{
struct utsm_ipc_msg msg;
if (!g_shm_mapped || !g_shm)
return -1;
if (len > UTSM_IPC_MSG_DATA_SIZE)
len = UTSM_IPC_MSG_DATA_SIZE;
memset(&msg, 0, sizeof(msg));
msg.type = type;
msg.data_len = len;
if (data && len > 0)
memcpy(msg.data, data, len);
return utsm_ipc_ring_push(&g_shm->linux_to_utsm, &msg);
}
static void __exit utsm_hcall_exit(void)
{
if (g_shm_mapped) {
/* Clear linux_ready flag */
g_shm->header.linux_ready = 0;
mb();
/* Send shutdown notification */
ipc_shm_send_to_utsm(UTSM_MSG_SHUTDOWN, NULL, 0);
misc_deregister(&utsm_miscdev);
#ifdef CONFIG_UTSM_EARLY_CONSOLE
unregister_console(&utsm_console);
#endif
iounmap(g_shm);
g_shm = NULL;
g_shm_mapped = 0;
}
pr_info("[utsm] driver unloaded\n");
}
module_init(utsm_hcall_init);
module_exit(utsm_hcall_exit);
MODULE_AUTHOR("Deshab Project");
MODULE_DESCRIPTION("UTSM dual-kernel hypercall and IPC driver");
MODULE_LICENSE("GPL");
+4
View File
@@ -3,6 +3,7 @@ LD := ld.lld
ROOT := ../..
OUT := $(ROOT)/SYSTEM/system/user/use/login.elf
PATCHER := $(ROOT)/.build_tmp/patch_login_hash.ps1
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 -I../firstInit -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
@@ -14,9 +15,12 @@ OBJECTS := $(C_SOURCES:.c=.o)
all: $(OUT)
# 构建后用 patch_login_hash.ps1 计算 .text SHA256 并写入 .hashseg
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
@echo [login] patching .text SHA256 into .hashseg...
powershell -NoProfile -ExecutionPolicy Bypass -File $(PATCHER) $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
+16
View File
@@ -11,12 +11,28 @@ PHDRS
SECTIONS
{
. = 0x1000;
/* 防篡改:导出 .text 段范围供运行时自校验 */
__text_start = .;
.text : ALIGN(4K) { *(.text*) } :text
__text_end = .;
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
/* 防篡改:独立的 .hashseg 段存放预期 SHA256(32 字节)。
* 由构建后补丁脚本 patch_login_hash.exe 写入。
* 初始填充 0x00,运行时若发现全 0 则跳过校验(开发期便利)。 */
.hashseg : ALIGN(16) {
__expected_hash = .;
. = . + 32;
__expected_hash_end = .;
} :rodata
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
} :data
.bss : ALIGN(4K) {
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
+198 -2
View File
@@ -4,12 +4,22 @@
* extracts username + password hash, prompts for password,
* verifies via SHA256 comparison, sets ctx->reserved[2]=1 on success.
*
* 防篡改:对 .text 段做 SHA256 自校验,预期哈希由构建后补丁脚本
* 写入 .hashseg 段(__expected_hash)。运行时若发现哈希不匹配,
* 拒绝继续并触发串口告警。开发期哈希为全 0 时跳过校验。
*
* Returns to DSK in all cases (success, skip via Esc, or fatal error).
* DSK loads desktop.elf after this returns.
*/
#include "../UTSM/include/utsm/dsk.h"
/* 防篡改:linker 导出的 .text 段范围与预期哈希存储位置 */
extern const u8 __text_start[];
extern const u8 __text_end[];
extern const u8 __expected_hash[];
extern const u8 __expected_hash_end[];
typedef signed char i8;
typedef unsigned char u8;
typedef unsigned short u16;
@@ -216,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] = {
@@ -253,6 +361,69 @@ static void sha256(const char *msg, u8 out[32]) {
for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];}
}
/* ---- 防篡改:对任意字节缓冲计算 SHA256(分块处理,支持 >55 字节) ---- */
static void sha256_buf(const u8 *msg, u64 len, u8 out[32]) {
u32 h[8]={0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
u8 block[64];
u64 processed = 0;
while (processed + 64 <= len) {
for (int i = 0; i < 64; i++) block[i] = msg[processed + i];
u32 w[64];
for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3];
for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;}
u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
processed += 64;
}
/* 最终块:剩余数据 + 0x80 + 长度(64 位大端) */
u32 rem = (u32)(len - processed);
for (u32 i = 0; i < 64; i++) block[i] = 0;
for (u32 i = 0; i < rem; i++) block[i] = msg[processed + i];
block[rem] = 0x80;
u64 bit = len * 8;
for (int i = 0; i < 8; i++) block[63 - i] = (u8)(bit >> (i * 8));
u32 w[64];
for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3];
for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;}
u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];}
}
/* ---- 防篡改:运行时 .text 段完整性校验 ----
* 计算 __text_start ~ __text_end 的 SHA256,与 __expected_hash 对比。
* 返回 1=校验通过(或开发期哈希为空跳过),0=校验失败(被篡改) */
static int login_verify_integrity(void) {
const u8 *start = __text_start;
const u8 *end = __text_end;
const u8 *expected = __expected_hash;
u64 len = (u64)(end - start);
if (len == 0) return 0;
/* 开发期便利:预期哈希全 0 时跳过校验 */
int all_zero = 1;
for (int i = 0; i < 32; i++) if (expected[i] != 0) { all_zero = 0; break; }
if (all_zero) {
logl("[login] integrity: expected hash empty (dev mode), skip");
return 1;
}
u8 actual[32];
sha256_buf(start, len, actual);
/* 常量时间比较 */
u8 diff = 0;
for (int i = 0; i < 32; i++) diff |= actual[i] ^ expected[i];
if (diff == 0) {
logl("[login] integrity: OK");
return 1;
}
logl("[login] integrity: FAIL — code tampered!");
return 0;
}
/* ---- conf parsing ----
* USER.CONF format (built by FirstInit's build_user_conf, but stored unencrypted
* on disk for login.elf to parse):
@@ -333,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 */
@@ -359,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 ----
@@ -404,6 +581,14 @@ __attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[login] boot");
/* 防篡改:第一时间校验 .text 段完整性。失败则拒绝继续,立即返回。 */
if (!login_verify_integrity()) {
logl("[login] integrity check failed, refusing to continue");
if (ctx) ((dsk_boot_context *)ctx)->reserved[2] = 0;
return;
}
logl("[login] calibrating TSC");
tsc_calibrate_fi();
@@ -481,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) */
+310 -198
View File
@@ -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 <url> HTTP GET 请求(http://host[:port]/path)\n");
term_puts(" run <NAME.ELF> 运行根目录下的工具程序\n");
term_puts(" linux <prog> [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 <prog> [args] 执行 /bin/<prog>(或完整路径)
* 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; }
@@ -1836,6 +1942,12 @@ void dsk_entry(const dsk_boot_context *ctx) {
g_history_view = -1;
term_putc('\n');
execute_command(input_buf);
/* 确保命令输出后在新行绘制提示符 */
if (cur_col != 0) {
term_putc('\n');
}
/* 刷新终端显示 */
term_redraw_all();
input_len = 0;
input_cursor = 0;
input_buf[0] = 0;
BIN
View File
Binary file not shown.
+298 -59
View File
@@ -1,22 +1,170 @@
/* Deshab Browser — 全屏浏览器 (Stub)
* 地址栏 + 导航按钮 + 内容渲染区域。
* 当前显示 stub 提示,Esc 退出。
/* Deshab Browser — 全屏浏览器
* 接入 net_stack.h 共享协议栈,实现 HTTP GET 与简单内容显示。
* 地址栏输入 URL(如 http://10.0.2.2:8000/ 或 10.0.2.2),
* Enter 发起请求,内容区显示 HTTP 响应(分离 header/body,移除 HTML 标签)。
* Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#include "../net_stack.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define URL_BUF_SIZE 128
#define CONTENT_BUF_SIZE 32768
#define DISPLAY_LINES 200
/* 前向声明(do_fetch 引用 redraw_all_imm 和 br_ns_init) */
static void redraw_all_imm(void);
static int br_ns_init(void);
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
/* 地址栏 */
static char url_buf[URL_BUF_SIZE] = "http://10.0.2.2:8000/";
static int url_len = 22;
static int url_focused = 1; /* 启动时地址栏聚焦 */
/* 标题栏 */
/* 内容缓冲 */
static u8 content_buf[CONTENT_BUF_SIZE];
static u32 content_len = 0;
static int content_scroll = 0;
static int fetching = 0; /* 1=正在请求 */
static char status_msg[64] = "Ready";
/* ---- 状态消息 ---- */
static void br_set_status(const char *s) {
int i = 0;
while (*s && i < 63) status_msg[i++] = *s++;
status_msg[i] = 0;
}
/* ---- HTTP 请求(复制 shell cmd_curl 核心逻辑) ---- */
static void do_fetch(void) {
if (url_len == 0) { br_set_status("Empty URL"); return; }
br_set_status("Initializing net...");
redraw_all_imm();
if (br_ns_init() != 0) {
br_set_status("Net init failed");
return;
}
/* 解析 URL: [http://]host[:port][/path] */
const char *p = url_buf;
if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7;
char host[64]; int hl = 0;
while (*p && *p != ':' && *p != '/' && hl < 63) host[hl++] = *p++;
host[hl] = 0;
if (!hl) { br_set_status("Bad host"); 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;
/* DNS 解析 */
br_set_status("Resolving...");
redraw_all_imm();
u32 ip;
if (ns_dns_resolve(host, &ip) != 0) {
br_set_status("DNS failed");
return;
}
/* TCP 连接 */
br_set_status("Connecting...");
redraw_all_imm();
if (ns_tcp_connect(ip, port, 3000) != 0) {
br_set_status("TCP connect failed");
return;
}
/* 构造 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;
br_set_status("Sending request...");
redraw_all_imm();
if (ns_tcp_send((const u8 *)req, (u32)r) != 0) {
br_set_status("Send failed");
ns_tcp_close();
return;
}
br_set_status("Receiving...");
redraw_all_imm();
int n = ns_tcp_recv(0, 0, 5000);
ns_tcp_close();
if (n < 0) { br_set_status("Connection reset"); return; }
if (n == 0) { br_set_status("No response (timeout)"); return; }
/* 复制响应到 content_buf */
u32 total = ns_tcp_rx_len;
u32 copy = (total < CONTENT_BUF_SIZE) ? total : CONTENT_BUF_SIZE;
for (u32 i = 0; i < copy; i++) content_buf[i] = ns_tcp_rx[i];
content_len = copy;
content_scroll = 0;
char sm[32]; int sp = 0;
const char *ok = "OK "; while (*ok && sp < 30) sm[sp++] = *ok++;
char nb[12]; ns_u32_dec(nb, copy);
for (int i = 0; nb[i] && sp < 30; i++) sm[sp++] = nb[i];
sm[sp] = 0;
br_set_status(sm);
}
/* ---- 极简 HTML 文本提取:移除 <...> 标签,保留纯文本 ---- */
static int html_to_text(const u8 *in, u32 in_len, u8 *out, u32 out_cap) {
u32 oi = 0;
int in_tag = 0;
int prev_space = 0;
for (u32 i = 0; i < in_len && oi < out_cap - 1; i++) {
u8 c = in[i];
if (c == '<') { in_tag = 1; continue; }
if (c == '>') { in_tag = 0; continue; }
if (in_tag) continue;
/* 跳过 HTTP header(找到 \r\n\r\n 后才开始) */
/* 将 &nbsp; 等实体简化为空格 */
if (c == '\r') continue;
if (c == '\t') c = ' ';
/* 连续空格压缩 */
if (c == ' ') {
if (prev_space) continue;
prev_space = 1;
} else {
prev_space = 0;
}
out[oi++] = c;
}
out[oi] = 0;
return (int)oi;
}
/* ---- 立即重绘(无鼠标光标保存/恢复,用于 fetching 过程) ---- */
static void redraw_all_imm(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Browser", (i64)g_ac.fb_w);
/* 地址栏 */
@@ -24,69 +172,100 @@ static void redraw_all(void) {
int bar_h = 28;
int bar_x = 12;
int bar_w = (int)g_ac.fb_w - 24;
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BG_TERTIARY, 6);
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BORDER, 6);
u32 bar_bg = url_focused ? DA_BG_TERTIARY : DA_BG_SECONDARY;
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, bar_bg, 6);
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, url_focused ? DA_ACCENT : DA_BORDER, 6);
/* 导航按钮 */
int btn_w = 32;
da_fill_rounded_rect(&g_ac, bar_x + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "<", bar_x + 14, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + btn_w + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, ">", bar_x + 4 + btn_w + 4 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_fill_rounded_rect(&g_ac, bar_x + 4 + (btn_w + 4) * 2, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
da_draw_string(&g_ac, "R", bar_x + 4 + (btn_w + 4) * 2 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
/* URL 文本 + 光标 */
int url_x = bar_x + 12;
da_draw_string(&g_ac, url_buf, url_x, bar_y + 5, DA_TEXT_PRIMARY, bar_bg, CHAR_STEP);
if (url_focused) {
int cx = url_x + url_len * CHAR_STEP;
da_fill_rect(&g_ac, cx, bar_y + 5, 2, CHAR_H, DA_ACCENT_LIGHT);
}
/* URL 文本 */
int url_x = bar_x + (btn_w + 4) * 3 + 8;
da_draw_string(&g_ac, "deshab://home", url_x, bar_y + 5, DA_TEXT_DIM, DA_BG_TERTIARY, CHAR_STEP);
/* 提示 */
int hint_y = bar_y + bar_h + 4;
const char *hint = "Enter: fetch Esc: exit Up/Down: scroll";
da_draw_string(&g_ac, hint, 12, hint_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
/* 内容区域 */
int content_y = bar_y + bar_h + 12;
int content_x = 16;
int content_w = (int)g_ac.fb_w - 32;
/* 内容区 */
int content_y = hint_y + CHAR_H + 4;
int content_x = 12;
int content_w = (int)g_ac.fb_w - 24;
int content_h = (int)g_ac.fb_h - content_y - DA_STATUSBAR_H - 8;
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, content_h, DA_BG_SECONDARY, 4);
da_rect_outline(&g_ac, content_x, content_y, content_w, content_h, DA_BORDER, 4);
/* Stub 页面 */
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, 200, DA_BG_SECONDARY, 8);
da_rect_outline(&g_ac, content_x, content_y, content_w, 200, DA_BORDER, 8);
/* 内容显示 */
if (content_len > 0) {
/* 提取 body(跳过 HTTP header) */
u32 body_off = 0;
for (u32 i = 0; i + 3 < content_len; i++) {
if (content_buf[i]=='\r' && content_buf[i+1]=='\n' &&
content_buf[i+2]=='\r' && content_buf[i+3]=='\n') {
body_off = i + 4;
break;
}
}
/* HTML 转纯文本 */
static u8 plain[CONTENT_BUF_SIZE];
int plain_len = html_to_text(content_buf + body_off, content_len - body_off, plain, CONTENT_BUF_SIZE);
/* 居中图标 */
int icon_cx = content_x + content_w / 2 - 32;
int icon_cy = content_y + 24;
da_fill_rounded_rect(&g_ac, icon_cx, icon_cy, 64, 48, DA_BG_TERTIARY, 8);
/* 地球图标简化:圆圈 */
int cx = icon_cx + 32, cy = icon_cy + 24, r = 16;
for (int dy = -r; dy <= r; dy++)
for (int dx = -r; dx <= r; dx++)
if (dx*dx+dy*dy <= r*r && dx*dx+dy*dy > (r-1)*(r-1))
da_pixel(&g_ac, cx+dx, cy+dy, DA_ACCENT);
/* 横线 */
for (int dx = -r; dx <= r; dx++)
if (dx*dx + 0 <= r*r)
da_pixel(&g_ac, cx+dx, cy, DA_ACCENT);
/* 竖线 */
for (int dy = -r; dy <= r; dy++)
if (0 + dy*dy <= r*r)
da_pixel(&g_ac, cx, cy+dy, DA_ACCENT);
/* 提示文字 */
da_draw_string(&g_ac, "Deshab Browser",
content_x + content_w / 2 - 7 * CHAR_STEP, icon_cy + 60,
DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "HTTP + HTML rendering required",
content_x + content_w / 2 - 15 * CHAR_STEP, icon_cy + 82,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
da_draw_string(&g_ac, "Press Esc to exit",
content_x + content_w / 2 - 9 * CHAR_STEP, icon_cy + 104,
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 按行显示 */
int visible_cols = (content_w - 16) / CHAR_STEP;
int visible_rows = (content_h - 8) / CHAR_H;
int y = content_y + 4;
int pos = 0;
int line = 0;
/* 跳过 scroll 行 */
int skip = content_scroll;
while (pos < plain_len && skip > 0) {
if (plain[pos] == '\n') skip--;
pos++;
}
while (pos < plain_len && line < visible_rows) {
int ls = pos;
while (pos < plain_len && plain[pos] != '\n' && pos - ls < visible_cols) pos++;
int llen = pos - ls;
for (int i = 0; i < llen; i++) {
char c = (char)plain[ls + i];
if (c < ' ' || c > '~') c = '.';
char buf[2]; buf[0] = c; buf[1] = 0;
da_draw_string(&g_ac, buf, content_x + 8 + i * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
}
y += CHAR_H;
line++;
if (pos < plain_len && plain[pos] == '\n') pos++;
}
} else if (!fetching) {
const char *empty = "No content. Enter URL and press Enter to fetch.";
da_draw_string(&g_ac, empty, content_x + 16, content_y + 16, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
}
/* 状态栏 */
da_draw_statusbar(&g_ac, "Browser | Stub - No network stack", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
char status[96]; int p = 0;
const char *s1 = "Browser | "; while (*s1 && p < 95) status[p++] = *s1++;
for (int i = 0; status_msg[i] && p < 95; i++) status[p++] = status_msg[i];
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* 鼠标光标 */
static void redraw_all(void) {
redraw_all_imm();
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
/* ---- net_stack.h 的 ns_init 需要 dkm_kernel_api ---- */
/* 重写 ns_init 以从 da_app_context 获取 kernel_api */
static int br_ns_init(void) {
if (ns_ready) return 0;
u64 dkm_kernel_api = (u64)g_ac.kernel_api;
if (!dkm_kernel_api) return -1;
return ns_init(dkm_kernel_api);
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
@@ -100,25 +279,85 @@ void dsk_entry(const da_boot_context *ctx) {
da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
/* 初始化网络协议栈 */
br_ns_init();
br_set_status("Ready");
redraw_all();
int e0 = 0, shift = 0;
for (;;) {
int need_redraw = 0;
/* 鼠标轮询 */
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h))
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
need_redraw = 1;
/* 点击地址栏聚焦 */
int bar_y = DA_TITLEBAR_H + 6;
int bar_h = 28;
if (g_cursor.btn &&
g_cursor.my >= bar_y && g_cursor.my < bar_y + bar_h &&
g_cursor.mx >= 12 && g_cursor.mx < (int)g_ac.fb_w - 12) {
url_focused = 1;
need_redraw = 1;
}
}
/* 键盘轮询 */
u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60);
if (!(data & 0x80) && data == 0x01) {
u8 sc = data;
if (sc == 0xE0) { e0 = 1; goto skip; }
if (sc == 0x2A || sc == 0x36) { shift = 1; goto skip; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
if (sc & 0x80) { e0 = 0; goto skip; }
if (sc == 0x01) { /* Esc */
da_slog("browser", "exit");
return;
}
/* 地址栏输入模式 */
if (url_focused) {
if (sc == 0x1C) { /* Enter - 发起请求 */
url_focused = 0;
fetching = 1;
br_set_status("Fetching...");
redraw_all();
do_fetch();
fetching = 0;
need_redraw = 1;
goto skip;
}
if (sc == 0x0E) { /* Backspace */
if (url_len > 0) { url_len--; url_buf[url_len] = 0; }
need_redraw = 1;
goto skip;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && url_len < URL_BUF_SIZE - 1) {
/* 小写转大写便于 URL 输入(除协议头) */
url_buf[url_len++] = c;
url_buf[url_len] = 0;
need_redraw = 1;
}
goto skip;
}
/* 内容浏览模式 */
if (e0) {
if (sc == 0x48) { if (content_scroll > 0) content_scroll--; need_redraw = 1; }
else if (sc == 0x50) { content_scroll++; need_redraw = 1; }
e0 = 0;
goto skip;
}
if (sc == 0x49) { content_scroll -= 10; if (content_scroll < 0) content_scroll = 0; need_redraw = 1; }
else if (sc == 0x51) { content_scroll += 10; need_redraw = 1; }
else if (sc == 0x1C) { url_focused = 1; need_redraw = 1; } /* Enter 回到地址栏 */
}
skip:
if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor);
redraw_all();
+17 -9
View File
@@ -40,8 +40,9 @@ typedef struct {
u64 kernel_stack_top; u64 reserved[8];
} da_boot_context;
/* block_read 函数类型 */
typedef int (*da_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
/* block 设备函数类型(与 shell/fat32_io.h 一致:u32 index 固定 0) */
typedef int (*da_block_read_fn)(u32 index, u64 lba, u32 count, void *buffer);
typedef int (*da_block_write_fn)(u32 index, u64 lba, u32 count, const void *buffer);
/* 应用上下文 — 从 dsk_boot_context 提取的常用数据 */
typedef struct {
@@ -50,21 +51,26 @@ typedef struct {
u64 fb_h; /* 高度 */
u64 fb_pitch; /* 行宽(字节) */
da_block_read_fn block_read;
da_block_write_fn block_write;
const void *kernel_api;
} da_app_context;
/* 从 dsk_boot_context 初始化 da_app_context */
/* 从 dsk_boot_context 初始化 da_app_context。
* block API 从 kernel_api + 0xA8 获取:read @ +0x10,write @ +0x18。 */
static inline void da_init(da_app_context *ac, const da_boot_context *ctx) {
ac->fb = (u32 *)ctx->framebuffer_address;
ac->fb_w = ctx->framebuffer_width;
ac->fb_h = ctx->framebuffer_height;
ac->fb_pitch = ctx->framebuffer_pitch;
ac->kernel_api = (const void *)ctx->dkm_kernel_api;
/* block_read 从 kernel_api + 0xA8 获取 */
ac->block_read = 0;
ac->block_write = 0;
u64 api = ctx->dkm_kernel_api;
ac->block_read = (da_block_read_fn)*(u64 *)(api + 0xA8);
if (ac->block_read)
ac->block_read = (da_block_read_fn)*(u64 *)((u64)ac->block_read + 16);
u64 blk_api = *(u64 *)(api + 0xA8);
if (blk_api) {
ac->block_read = (da_block_read_fn)*(u64 *)(blk_api + 0x10);
ac->block_write = (da_block_write_fn)*(u64 *)(blk_api + 0x18);
}
}
/* ========== 帧缓冲绘制 ========== */
@@ -285,10 +291,12 @@ static inline int da_mouse_poll(da_mouse *m, da_cursor *c, i64 fb_w, i64 fb_h) {
if (m->idx < 3) return 0;
m->idx = 0;
if (!(m->buf[0] & 0x08)) return 0;
/* PS/2 鼠标包:buf[1]/buf[2] 已是 9 位补码的低 8 位,
* (signed char) 强转即可正确符号扩展,无需再依据 buf[0] 的 sign bit 反转。
* X 直接相加;Y 需翻转一次(PS/2 Y 向上为正,屏幕 Y 向下为正)。
* 与 desktop/main.c:ps2_mouse_poll 保持一致。 */
int dx = (int)(signed char)m->buf[1];
int dy = (int)(signed char)m->buf[2];
if (m->buf[0] & 0x10) dx = -dx;
if (!(m->buf[0] & 0x20)) dy = -dy; else dy = -dy;
dy = -dy;
c->mx += dx; c->my += dy;
if (c->mx < 0) c->mx = 0;
+118 -14
View File
@@ -1,14 +1,18 @@
/* Deshab Editor — 全屏文本编辑器
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。
* Ctrl+O 打开文件,Ctrl+S 保存(暂不支持),Esc 退出。
* Ctrl+O 打开文件(输入 8.3 名,从 FAT32 根目录读取)
* Ctrl+S 保存文件(输入 8.3 名,写入 FAT32 根目录)
* Esc 退出。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#include "../fat32_io.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define EDITOR_BUF_SIZE 65536
#define INPUT_BUF_SIZE 16
static da_app_context g_ac;
@@ -22,6 +26,11 @@ static int modified = 0;
static char filename[64] = "untitled";
static int ctrl_down = 0;
/* 输入模式:Ctrl+O/Ctrl+S 时进入,在状态栏输入文件名 */
static int input_mode = 0; /* 0=编辑, 1=open, 2=save */
static char input_buf[INPUT_BUF_SIZE];
static int input_len = 0;
static int line_start(int line) {
int l = 0;
for (int i = 0; i < text_len; i++) {
@@ -47,6 +56,63 @@ static void init_default_text(void) {
recalc_cursor();
}
/* ---- 文件打开/保存 ---- */
static void do_open(void) {
if (input_len == 0) return;
char name11[11];
if (f32_name_to_83(input_buf, name11) != 0) {
da_slog("editor", "open: bad name");
return;
}
u8 *data = 0; u32 size = 0;
if (f32_read_root_file(name11, &data, &size) != 0) {
da_slog("editor", "open: read failed");
return;
}
if (size > EDITOR_BUF_SIZE) size = EDITOR_BUF_SIZE;
text_len = (int)size;
for (int i = 0; i < text_len; i++) text_buf[i] = (char)data[i];
cursor_pos = 0;
modified = 0;
/* 更新 filename 显示 */
int p = 0;
for (int i = 0; i < input_len && p < 60; i++) filename[p++] = input_buf[i];
filename[p] = 0;
recalc_cursor();
da_slog("editor", "open: ok");
}
static void do_save(void) {
if (input_len == 0) return;
char name11[11];
if (f32_name_to_83(input_buf, name11) != 0) {
da_slog("editor", "save: bad name");
return;
}
if (f32_write_root_file(name11, (const u8 *)text_buf, (u32)text_len) != 0) {
da_slog("editor", "save: write failed");
return;
}
modified = 0;
int p = 0;
for (int i = 0; i < input_len && p < 60; i++) filename[p++] = input_buf[i];
filename[p] = 0;
da_slog("editor", "save: ok");
}
static void enter_input_mode(int mode) {
input_mode = mode;
input_len = 0;
input_buf[0] = 0;
}
static void commit_input(void) {
int mode = input_mode;
input_mode = 0;
if (mode == 1) do_open();
else if (mode == 2) do_save();
}
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_SECONDARY);
char title[80];
@@ -74,16 +140,13 @@ static void redraw_all(void) {
for (; pos <= text_len && line < scroll_y + visible_rows; ) {
if (line >= scroll_y) {
int screen_y = top_y + (line - scroll_y) * CHAR_H;
/* 行号 */
char ln[8];
ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10;
ln[3] = 0;
da_draw_string(&g_ac, ln, margin_x, screen_y, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
/* 分隔线 */
da_fill_rect(&g_ac, margin_x + line_num_w - 2, screen_y, 1, CHAR_H, DA_BORDER);
/* 文本 */
int col = 0;
while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) {
da_draw_char(&g_ac, text_buf[pos],
@@ -97,25 +160,35 @@ static void redraw_all(void) {
if (pos < text_len && text_buf[pos] == '\n') pos++;
line++;
if (pos >= text_len) {
/* 空行也画光标 */
if (line > cursor_line) break;
}
}
/* 光标 */
if (cursor_line >= scroll_y && cursor_line < scroll_y + visible_rows) {
/* 光标(编辑模式) */
if (!input_mode && cursor_line >= scroll_y && cursor_line < scroll_y + visible_rows) {
int cy = top_y + (cursor_line - scroll_y) * CHAR_H;
int cx = text_x + cursor_col * CHAR_STEP;
da_fill_rect(&g_ac, cx, cy, 2, CHAR_H, DA_ACCENT_LIGHT);
}
/* 状态栏 */
char status[64]; p = 0;
char status[96]; p = 0;
if (input_mode) {
const char *prompt = (input_mode == 1) ? "Open: " : "Save: ";
while (*prompt) status[p++] = *prompt++;
for (int i = 0; i < input_len; i++) status[p++] = input_buf[i];
status[p++] = '_';
const char *hint = " Enter:confirm Esc:cancel";
while (*hint && p < 95) status[p++] = *hint++;
} else {
const char *s1 = "L:"; while (*s1) status[p++] = *s1++;
status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10;
const char *s2 = " C:"; while (*s2) status[p++] = *s2++;
status[p++] = '0' + (cursor_col+1)/10%10; status[p++] = '0' + (cursor_col+1)%10;
if (modified) { const char *m = " *"; while (*m) status[p++] = *m++; }
const char *s3 = " Ctrl+O:Open Ctrl+S:Save Esc:Exit";
while (*s3 && p < 95) status[p++] = *s3++;
}
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
@@ -131,6 +204,7 @@ void dsk_entry(const da_boot_context *ctx) {
}
da_init(&g_ac, ctx);
f32_init((f32_block_read_fn)g_ac.block_read, (f32_block_write_fn)g_ac.block_write);
init_default_text();
redraw_all();
@@ -148,6 +222,36 @@ void dsk_entry(const da_boot_context *ctx) {
if (sc == 0x9D) { ctrl_down = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc:输入模式取消 / 编辑模式退出 */
if (sc == 0x01 && !e0) {
if (input_mode) { input_mode = 0; redraw_all(); continue; }
da_slog("editor", "exit");
return;
}
/* ---- 输入模式:输入文件名 ---- */
if (input_mode) {
if (sc == 0x1C) { /* Enter - 确认 */
commit_input();
redraw_all();
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_len > 0) input_len--;
input_buf[input_len] = 0;
redraw_all();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < INPUT_BUF_SIZE - 1) {
input_buf[input_len++] = c;
input_buf[input_len] = 0;
redraw_all();
}
continue;
}
/* ---- 编辑模式 ---- */
/* Esc 退出 */
if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; }
@@ -212,16 +316,16 @@ void dsk_entry(const da_boot_context *ctx) {
modified = 1;
}
} else {
/* Ctrl+O: 打开文件(提示暂不支持) */
/* Ctrl+O: 打开文件 */
if (ctrl_down && (sc == 0x18)) {
/* Ctrl+O - stub */
da_slog("editor", "Ctrl+O - open not yet");
enter_input_mode(1);
redraw_all();
continue;
}
/* Ctrl+S: 保存(提示暂不支持) */
/* Ctrl+S: 保存文件 */
if (ctrl_down && (sc == 0x1F)) {
da_slog("editor", "Ctrl+S - save not yet");
modified = 0;
enter_input_mode(2);
redraw_all();
continue;
}
char c = da_scan_to_ascii(sc, shift);
+557
View File
@@ -0,0 +1,557 @@
/* fat32_io.h — Deshab 用户态共享 FAT32 读写库(static inline,无库)
*
* 前置条件:包含者已定义 u8/u16/u32/u64/i64 基础类型。
* 通过 f32_init() 注入 block_read/block_write 函数指针(来自 dkm_kernel_api + 0xA8)。
*
* 能力:FAT32 根目录文件的读/写/删/列举,支持 FAT 链跟随、多簇文件。
* 限制:仅根目录(不支持子目录,Phase 2.2 fileman 将扩展);单文件最大 256KB。
*
* 用法:
* f32_init(block_read, block_write) — 初始化(block_write 可为 NULL,仅读场景)
* f32_name_to_83("file.txt", name11) — "file.txt" → 11 字符 8.3 名
* f32_read_root_file(name11, &data, &size) — 读根目录文件到 f32_data 缓冲
* f32_write_root_file(name11, data, size) — 写/替换根目录文件
* f32_delete_root_file(name11) — 删除根目录文件
* f32_list_root(emit_cb, user_data) — 列举根目录
*
* 缓冲区:每个包含本头的 .elf 独立持有 256KB g_disk + 4KB g_cluster + 256KB g_fdata(共 ~516KB BSS)。
* 与 net_stack.h 同模式,接受 static 缓冲的代价以换取无库依赖。
*/
#ifndef DESHAB_FAT32_IO_H
#define DESHAB_FAT32_IO_H
/* ---- block 设备函数类型(与 shell/DSK 一致:index 参数固定 0) ---- */
typedef int (*f32_block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
typedef int (*f32_block_write_fn)(u32 index, u64 lba, u32 count, const void *buf);
static f32_block_read_fn f32_blk_read = 0;
static f32_block_write_fn f32_blk_write = 0;
/* ---- FAT32 BPB / 目录项结构(packed,与 DSK/shell 一致) ---- */
typedef struct __attribute__((packed)) {
u8 jmp[3]; char oem[8]; u16 bps; u8 spc; u16 rsvd; u8 fc; u16 root_ent;
u16 ts16; u8 media; u16 spf16; u16 spt; u16 heads; u32 hidden; u32 ts32;
u32 spf; u16 flags; u16 ver; u32 root_clus; u16 fsi; u16 bkboot;
u8 res[12]; u8 drv; u8 ntfl; u8 sig; u32 ser; char lbl[11]; char typ[8];
u8 code[420]; u16 boot_sig;
} f32_bpb;
typedef struct __attribute__((packed)) {
char name[11]; u8 attr; u8 ntr; u8 ctenth;
u16 ctime; u16 cdate; u16 adate; u16 chigh;
u16 wtime; u16 wdate; u16 clow; u32 fsize;
} f32_dirent;
/* ---- 内部小工具 ---- */
static inline u32 f32_r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); }
static inline u16 f32_r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); }
static inline int f32_neq11(const char *a, const char *b) {
for (int i = 0; i < 11; i++) if (a[i] != b[i]) return 0;
return 1;
}
/* ---- 批量读写缓冲区(与 DSK/shell 对齐:256 扇区覆盖 BPB+FAT+根目录+小文件) ---- */
static u8 f32_disk[131072]; /* 256 扇区 BPB+FAT+根目录缓存 */
static u8 f32_cluster[4096]; /* 单簇缓冲 */
static u8 f32_data[262144]; /* 文件数据缓冲(最大 256KB) */
static int f32_disk_loaded = 0; /* f32_disk 是否已加载 BPB+FAT */
/* 初始化:注入 block_read/block_write 函数指针 */
static inline void f32_init(f32_block_read_fn rd, f32_block_write_fn wr) {
f32_blk_read = rd;
f32_blk_write = wr;
f32_disk_loaded = 0;
}
static inline int f32_read_sectors(u32 lba, u32 count, u8 *out) {
return f32_blk_read ? f32_blk_read(0, lba, count, out) : -1;
}
static inline int f32_write_sectors(u32 lba, u32 count, const u8 *buf) {
return f32_blk_write ? f32_blk_write(0, lba, count, buf) : -1;
}
/* 确保 f32_disk 已加载 BPB+FAT+根目录(256 扇区) */
static inline int f32_disk_load(void) {
if (f32_disk_loaded) return 0;
if (f32_read_sectors(0, 256, f32_disk) != 0) return -1;
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -2;
if (bpb->spf == 0 || bpb->root_clus < 2) return -3;
f32_disk_loaded = 1;
return 0;
}
/* ---- 8.3 名字转换 ---- */
/* "file.txt" / "NAME" → 11 字符 8.3 名(大写,空格填充)
* 返回 0 成功,-1 失败(过长或非法字符) */
static inline int f32_name_to_83(const char *in, char out[11]) {
for (int i = 0; i < 11; i++) out[i] = ' ';
int inlen = 0;
while (in[inlen] && inlen < 13) inlen++;
if (inlen == 0 || inlen > 12) return -1;
int dot = -1;
for (int i = 0; i < inlen; i++) if (in[i] == '.') dot = i;
int base_end = (dot >= 0) ? dot : inlen;
if (base_end == 0 || base_end > 8) return -1;
for (int j = 0; j < base_end; j++) {
char c = in[j];
if (c >= 'a' && c <= 'z') c -= 32;
if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1;
out[j] = c;
}
if (dot >= 0) {
int extlen = inlen - dot - 1;
if (extlen > 3) return -1;
for (int j = 0; j < extlen; j++) {
char c = in[dot + 1 + j];
if (c >= 'a' && c <= 'z') c -= 32;
if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1;
out[8 + j] = c;
}
}
return 0;
}
/* 11 字符 8.3 名 → 可显示字符串(如 "README.TXT") */
static inline void f32_name_from_83(const char in[11], char out[13]) {
int p = 0;
for (int j = 0; j < 8; j++) {
if (in[j] == ' ') break;
out[p++] = in[j];
}
if (in[8] != ' ') {
out[p++] = '.';
for (int j = 8; j < 11; j++) {
if (in[j] == ' ') break;
out[p++] = in[j];
}
}
out[p] = 0;
}
/* ---- 根目录文件查找 ---- */
/* 在根目录簇链中查找 11 字符名,输出首簇号与文件大小 */
static inline int f32_find_in_root(const u8 *clus, u32 clus_sectors, const char *target,
u32 *out_clus, u32 *out_size, u32 *out_idx) {
const f32_dirent *dir = (const f32_dirent *)clus;
for (u32 e = 0; e * 32 < clus_sectors * 512; e++) {
if (dir[e].name[0] == 0) break;
if ((u8)dir[e].name[0] == 0xE5) continue;
if (dir[e].attr == 0x0F) continue;
if (dir[e].attr & 0x08) continue;
if (f32_neq11(dir[e].name, target)) {
*out_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
*out_size = dir[e].fsize;
if (out_idx) *out_idx = e;
return 0;
}
}
return -1;
}
/* 读取根目录下指定 8.3 名文件到 f32_data。
* 返回 0 成功,*out_data 指向 f32_data,*out_size 为字节数;非 0 失败。 */
static inline int f32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) {
if (f32_disk_load() != 0) return -1;
const f32_bpb *bpb = (const f32_bpb *)f32_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 found_clus = 0, found_size = 0;
int found = 0;
while (clus >= 2 && clus < 0x0FFFFFF8 && !found) {
u32 lba = data_lba + (clus - 2) * spc;
const u8 *cb;
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
cb = f32_disk + (u64)lba * 512;
else {
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
cb = f32_cluster;
}
if (f32_find_in_root(cb, spc, name11, &found_clus, &found_size, 0) == 0) { found = 1; break; }
u32 fo = fat_byte_off + clus * 4;
if (fo + 4 > sizeof(f32_disk)) break;
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
if (!found) return -5;
if (found_size > sizeof(f32_data)) return -6;
u8 *dst = f32_data; u32 remaining = found_size; u32 fc = found_clus;
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
u32 fc_lba = data_lba + (fc - 2) * spc;
u32 fc_bytes = spc * 512;
if (fc_bytes > remaining) fc_bytes = remaining;
const u8 *fb;
if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512)
fb = f32_disk + (u64)fc_lba * 512;
else {
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
fb = f32_cluster;
}
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
dst += fc_bytes; remaining -= fc_bytes;
u32 fo = fat_byte_off + fc * 4;
if (fo + 4 > sizeof(f32_disk)) break;
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
*out_data = f32_data; *out_size = found_size;
return 0;
}
/* 写入根目录下指定 8.3 名文件(存在则替换,不存在则新建)。
* 依赖 f32_disk 已加载 BPB+FAT。返回 0 成功。 */
static inline int f32_write_root_file(const char *name11, const u8 *data, u32 size) {
if (f32_disk_load() != 0) return -1;
if (!f32_blk_write) return -10;
/* 写入前重新加载 BPB+FAT,保证与盘上一致 */
if (f32_read_sectors(0, 256, f32_disk) != 0) return -2;
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
u32 spc = bpb->spc;
u32 fat_lba = bpb->rsvd;
u32 fat_sectors = bpb->spf;
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
u32 fat_byte_off = bpb->rsvd * 512;
u32 root_clus = bpb->root_clus;
u32 cluster_bytes = spc * 512;
u32 root_lba = data_lba + (root_clus - 2) * spc;
u8 *root_buf = f32_disk + (u64)root_lba * 512;
u32 max_entries = cluster_bytes / 32;
f32_dirent *dir = (f32_dirent *)root_buf;
int free_entry = -1;
u32 existing_clus = 0;
int found = 0;
u32 existing_idx = 0;
for (u32 e = 0; e < max_entries; e++) {
if (dir[e].name[0] == 0) { if (free_entry < 0) free_entry = (int)e; break; }
if ((u8)dir[e].name[0] == 0xE5) { if (free_entry < 0) free_entry = (int)e; continue; }
if (dir[e].attr == 0x0F) continue;
if (dir[e].attr & 0x08) continue;
if (f32_neq11(dir[e].name, name11)) {
existing_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
found = 1; existing_idx = e;
if (free_entry < 0) free_entry = (int)e;
break;
}
}
u32 bytes_needed = size > 0 ? size : 1;
u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes;
u32 first_clus = 0;
u32 prev_clus = 0;
if (found && existing_clus >= 2) {
first_clus = existing_clus;
u32 cur = existing_clus;
u32 count = 0;
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
prev_clus = cur;
u32 fo = fat_byte_off + cur * 4;
if (fo + 4 > sizeof(f32_disk)) break;
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
count++;
}
while (count < clusters_needed) {
u32 newc = 0;
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
u32 fo = fat_byte_off + c * 4;
if (fo + 4 > sizeof(f32_disk)) break;
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
}
if (newc == 0) return -3;
u32 pfo = fat_byte_off + prev_clus * 4;
if (pfo + 4 <= sizeof(f32_disk)) {
f32_disk[pfo] = (u8)(newc & 0xFF);
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
}
prev_clus = newc;
count++;
}
if (prev_clus >= 2) {
u32 fo = fat_byte_off + prev_clus * 4;
if (fo + 4 <= sizeof(f32_disk)) {
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
}
}
/* 释放多余旧簇 */
u32 next = 0;
u32 fo = fat_byte_off + prev_clus * 4;
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
while (next >= 2 && next < 0x0FFFFFF8) {
u32 nfo = fat_byte_off + next * 4;
u32 nn = 0;
if (nfo + 4 <= sizeof(f32_disk)) nn = f32_r32(f32_disk + nfo) & 0x0FFFFFFF;
f32_disk[nfo] = 0; f32_disk[nfo+1] = 0; f32_disk[nfo+2] = 0; f32_disk[nfo+3] = 0;
next = nn;
}
} else {
if (free_entry < 0) return -4;
for (u32 i = 0; i < clusters_needed; i++) {
u32 newc = 0;
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
u32 fo = fat_byte_off + c * 4;
if (fo + 4 > sizeof(f32_disk)) break;
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
}
if (newc == 0) return -3;
if (i == 0) first_clus = newc;
if (prev_clus >= 2) {
u32 pfo = fat_byte_off + prev_clus * 4;
f32_disk[pfo] = (u8)(newc & 0xFF);
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
}
prev_clus = newc;
}
if (prev_clus >= 2) {
u32 fo = fat_byte_off + prev_clus * 4;
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
}
}
/* 写数据到簇 */
u32 remaining = size;
const u8 *src = data;
u32 cur = first_clus;
u32 ci = 0;
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
u32 clba = data_lba + (cur - 2) * spc;
u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes;
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
u8 *dst = f32_disk + (u64)clba * 512;
for (u32 b = 0; b < chunk; b++) dst[b] = src[b];
for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0;
} else {
if (f32_read_sectors(clba, spc, f32_cluster) != 0) return -5;
for (u32 b = 0; b < chunk; b++) f32_cluster[b] = src[b];
for (u32 b = chunk; b < cluster_bytes; b++) f32_cluster[b] = 0;
if (f32_write_sectors(clba, spc, f32_cluster) != 0) return -6;
}
src += chunk; remaining -= chunk; ci++;
u32 fo = fat_byte_off + cur * 4;
if (fo + 4 > sizeof(f32_disk)) break;
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
/* 更新目录项 */
u32 entry_idx = found ? existing_idx : (u32)free_entry;
f32_dirent *e = &dir[entry_idx];
if (!found) {
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
e->attr = 0x20; e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0;
}
e->chigh = (u16)((first_clus >> 16) & 0xFFFF);
e->clow = (u16)(first_clus & 0xFFFF);
e->fsize = size;
/* 回写 FAT(两份)+ 根目录 + 缓冲区内的数据簇 */
for (u32 f = 0; f < bpb->fc; f++) {
u32 flba = fat_lba + f * fat_sectors;
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -7;
}
u32 root_dir_lba = data_lba + (root_clus - 2) * spc;
if ((u64)root_dir_lba * 512 + cluster_bytes <= 256ULL * 512) {
if (f32_write_sectors(root_dir_lba, spc, f32_disk + (u64)root_dir_lba * 512) != 0) return -8;
}
cur = first_clus; ci = 0;
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
u32 clba = data_lba + (cur - 2) * spc;
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
if (f32_write_sectors(clba, spc, f32_disk + (u64)clba * 512) != 0) return -9;
}
u32 fo = fat_byte_off + cur * 4;
if (fo + 4 > sizeof(f32_disk)) break;
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
ci++;
}
f32_disk_loaded = 0; /* 下次操作重新加载,避免使用脏缓存 */
return 0;
}
/* 删除根目录下指定 8.3 名文件:清空簇链 + 标记目录项为 0xE5。
* 返回 0 成功,-1 未找到,其他负值失败。 */
static inline int f32_delete_root_file(const char *name11) {
if (f32_disk_load() != 0) return -2;
if (!f32_blk_write) return -10;
if (f32_read_sectors(0, 256, f32_disk) != 0) return -3;
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
u32 spc = bpb->spc;
u32 fat_lba = bpb->rsvd;
u32 fat_sectors = bpb->spf;
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
u32 fat_byte_off = bpb->rsvd * 512;
u32 root_clus = bpb->root_clus;
u32 root_lba = data_lba + (root_clus - 2) * spc;
u8 *root_buf = f32_disk + (u64)root_lba * 512;
u32 max_entries = (spc * 512) / 32;
f32_dirent *dir = (f32_dirent *)root_buf;
u32 found_idx = 0xFFFFFFFFu;
for (u32 e = 0; e < max_entries; e++) {
if (dir[e].name[0] == 0) break;
if ((u8)dir[e].name[0] == 0xE5) continue;
if (dir[e].attr == 0x0F) continue;
if (dir[e].attr & 0x08) continue;
if (f32_neq11(dir[e].name, name11)) { found_idx = e; break; }
}
if (found_idx == 0xFFFFFFFFu) return -1;
/* 释放簇链 */
u32 cur = (u32)f32_r16((const u8*)&dir[found_idx].clow) | ((u32)f32_r16((const u8*)&dir[found_idx].chigh) << 16);
while (cur >= 2 && cur < 0x0FFFFFF8) {
u32 fo = fat_byte_off + cur * 4;
u32 next = 0;
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
f32_disk[fo] = 0; f32_disk[fo+1] = 0; f32_disk[fo+2] = 0; f32_disk[fo+3] = 0;
cur = next;
}
/* 标记目录项为已删除 */
dir[found_idx].name[0] = (char)0xE5;
/* 回写 FAT + 根目录 */
for (u32 f = 0; f < bpb->fc; f++) {
u32 flba = fat_lba + f * fat_sectors;
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -4;
}
if (f32_write_sectors(root_lba, spc, f32_disk + (u64)root_lba * 512) != 0) return -5;
f32_disk_loaded = 0;
return 0;
}
/* 列举根目录条目回调签名:返回 0 继续,非 0 停止 */
typedef int (*f32_list_cb)(const char *name, u32 size, u8 attr, void *user_data);
/* 列出根目录所有文件/目录,调用回调输出。
* 回调返回非 0 立即停止。返回 0 成功(含回调提前停止),负值失败。 */
static inline int f32_list_root(f32_list_cb emit, void *user_data) {
if (f32_disk_load() != 0) return -1;
const f32_bpb *bpb = (const f32_bpb *)f32_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;
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 = f32_disk + (u64)lba * 512;
else {
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
cb = f32_cluster;
}
const f32_dirent *dir = (const f32_dirent *)cb;
for (u32 e = 0; e * 32 < spc * 512; e++) {
if (dir[e].name[0] == 0) goto done;
if ((u8)dir[e].name[0] == 0xE5) continue;
if (dir[e].attr == 0x0F) continue;
if (dir[e].attr & 0x08) continue;
char disp[13];
f32_name_from_83(dir[e].name, disp);
if (emit(disp, dir[e].fsize, dir[e].attr, user_data) != 0) goto done;
}
u32 fo = fat_byte_off + clus * 4;
if (fo + 4 > sizeof(f32_disk)) break;
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
done:
return 0;
}
/* ---- 子目录遍历支持(Phase 2.2 fileman 扩展) ---- */
/* 目录条目(供 fileman 等工具使用) */
typedef struct {
char name[13]; /* 8.3 显示名 */
u32 clus; /* 首簇号 */
u32 size; /* 文件大小(目录为 0) */
u8 attr;
u8 is_dir;
} f32_entry;
typedef int (*f32_list_entry_cb)(const f32_entry *e, void *user_data);
/* 列举指定目录簇的内容。
* dir_clus = 0 表示根目录(自动从 BPB 取 root_clus)。
* 回调返回非 0 立即停止。返回 0 成功,负值失败。 */
static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_data) {
if (f32_disk_load() != 0) return -1;
const f32_bpb *bpb = (const f32_bpb *)f32_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 == 0) ? bpb->root_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 = f32_disk + (u64)lba * 512;
else {
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
cb = f32_cluster;
}
const f32_dirent *dir = (const f32_dirent *)cb;
for (u32 e = 0; e * 32 < spc * 512; e++) {
if (dir[e].name[0] == 0) goto done;
if ((u8)dir[e].name[0] == 0xE5) continue;
if (dir[e].attr == 0x0F) continue;
if (dir[e].attr & 0x08) continue;
/* 跳过 "." 和 ".." 条目 */
if (dir[e].name[0] == '.' && (dir[e].name[1] == ' ' || dir[e].name[1] == '.'))
continue;
f32_entry fe;
f32_name_from_83(dir[e].name, fe.name);
fe.clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
fe.size = dir[e].fsize;
fe.attr = dir[e].attr;
fe.is_dir = (dir[e].attr & 0x10) ? 1 : 0;
if (emit(&fe, user_data) != 0) goto done;
}
u32 fo = fat_byte_off + clus * 4;
if (fo + 4 > sizeof(f32_disk)) break;
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
done:
return 0;
}
/* 按首簇号 + 大小读取文件内容到 out_buf。
* out_buf 至少 size 字节容量。返回 0 成功,负值失败。 */
static inline int f32_read_file_by_clus(u32 clus, u32 size, u8 *out_buf, u32 buf_cap) {
if (f32_disk_load() != 0) return -1;
if (size > buf_cap) size = buf_cap;
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
u32 fat_byte_off = bpb->rsvd * 512;
u32 spc = bpb->spc;
u32 remaining = size;
u8 *dst = out_buf;
u32 fc = clus;
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
u32 fc_lba = data_lba + (fc - 2) * spc;
u32 fc_bytes = spc * 512;
if (fc_bytes > remaining) fc_bytes = remaining;
const u8 *fb;
if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512)
fb = f32_disk + (u64)fc_lba * 512;
else {
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
fb = f32_cluster;
}
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
dst += fc_bytes; remaining -= fc_bytes;
u32 fo = fat_byte_off + fc * 4;
if (fo + 4 > sizeof(f32_disk)) break;
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
}
return 0;
}
#endif /* DESHAB_FAT32_IO_H */
+301 -68
View File
@@ -1,80 +1,123 @@
/* Deshab File Manager — 全屏文件管理器
* 文件列表视图,鼠标点击选择,键盘导航。
* FAT32 根目录遍历,Esc 退出。
* 接入 fat32_io.h 共享头,支持:
* - 子目录遍历(路径栈,Enter 进入 / Backspace 返回)
* - FAT 链跟随(多簇文件/目录)
* - 文件查看(Enter 打开文本文件,只读浏览,Esc 返回列表)
* - 鼠标点击选择 + 键盘导航
* Esc 退出返回 DSK/desktop。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#include "../fat32_io.h"
#define CHAR_STEP 12
#define CHAR_H 18
#define MAX_FILES 256
#define MAX_DEPTH 16
#define VIEW_BUF 24576 /* 文件查看器缓冲 24KB */
static da_app_context g_ac;
static da_cursor g_cursor;
static da_mouse g_mouse;
typedef struct {
char name[13]; /* 8.3 */
int is_dir;
char name[13];
u32 clus;
u32 size;
} file_entry;
u8 is_dir;
} fm_entry;
static file_entry files[MAX_FILES];
static fm_entry files[MAX_FILES];
static int file_count = 0;
static int selected = 0;
static int scroll_off = 0;
static void load_root_dir(void) {
file_count = 0;
if (!g_ac.block_read) return;
/* 路径栈:每层记录目录首簇(0=根)与显示名 */
static u32 path_clus[MAX_DEPTH];
static char path_name[MAX_DEPTH][13];
static int path_depth = 0; /* 0 = 根目录 */
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) return;
if (bpb[510] != 0x55 || bpb[511] != 0xAA) return;
/* 文件查看器状态 */
static int view_mode = 0;
static u8 view_buf[VIEW_BUF];
static u32 view_size = 0;
static int view_scroll = 0;
u32 root_cluster = *(u32 *)(bpb + 44);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
u8 dir_buf[8192];
u32 lba = data_start + (root_cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) return;
for (int i = 0; i < (int)(sectors_per_cluster * 512) && file_count < MAX_FILES; i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue;
if (dir_buf[i+11] & 0x10) continue; /* 暂跳过目录 */
file_entry *f = &files[file_count];
f->is_dir = (dir_buf[i+11] & 0x10) ? 1 : 0;
f->size = *(u32 *)(dir_buf + i + 28);
int p = 0;
for (int j = 0; j < 8 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
if (dir_buf[i+8] != ' ') {
f->name[p++] = '.';
for (int j = 8; j < 11 && dir_buf[i+j] != ' '; j++)
f->name[p++] = (char)dir_buf[i+j];
/* ---- 目录列举回调 ---- */
static int fm_collect_cb(const f32_entry *e, void *user_data) {
(void)user_data;
if (file_count >= MAX_FILES) return 1;
fm_entry *f = &files[file_count];
for (int i = 0; i < 13; i++) f->name[i] = e->name[i];
f->clus = e->clus;
f->size = e->size;
f->is_dir = e->is_dir;
file_count++;
return 0;
}
f->name[p] = 0;
static void load_current_dir(void) {
file_count = 0;
selected = 0;
scroll_off = 0;
u32 dir_clus = (path_depth == 0) ? 0 : path_clus[path_depth - 1];
f32_list_dir(dir_clus, fm_collect_cb, 0);
/* 如果不是根目录,在开头添加 ".." 返回上级条目 */
if (path_depth > 0 && file_count < MAX_FILES) {
/* 将现有文件向后移动 */
for (int i = file_count; i > 0; i--) {
files[i] = files[i - 1];
}
/* 在开头插入 ".." 条目 */
files[0].name[0] = '.';
files[0].name[1] = '.';
files[0].name[2] = 0;
files[0].clus = 0; /* 特殊标记 */
files[0].size = 0;
files[0].is_dir = 1;
file_count++;
}
}
static void redraw_all(void) {
/* ---- 路径显示 ---- */
static void build_path_str(char *out, int cap) {
int p = 0;
out[p++] = '/';
for (int i = 0; i < path_depth && p < cap - 13; i++) {
const char *n = path_name[i];
while (*n && p < cap - 2) out[p++] = *n++;
out[p++] = '/';
}
out[p] = 0;
}
/* ---- 数字转字符串 ---- */
static void fm_u32_dec(char *buf, u32 v) {
char tmp[12]; int n = 0;
if (!v) { buf[0] = '0'; buf[1] = 0; return; }
while (v && n < 11) { tmp[n++] = '0' + (v % 10); v /= 10; }
for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i];
buf[n] = 0;
}
/* ---- 列表视图绘制 ---- */
static void redraw_list(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Files", (i64)g_ac.fb_w);
/* 当前路径 */
char path_str[200];
build_path_str(path_str, sizeof(path_str));
da_draw_string(&g_ac, path_str, 12, DA_TITLEBAR_H + 4, DA_ACCENT, DA_BG_PRIMARY, CHAR_STEP);
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int top_y = DA_TITLEBAR_H + 4 + CHAR_H + 4;
int row_h = CHAR_H + 6;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
int col_name_x = margin + 32;
int col_size_x = (int)g_ac.fb_w - 120;
int col_size_x = (int)g_ac.fb_w - 160;
int col_type_x = (int)g_ac.fb_w - 60;
/* 列标题 */
@@ -83,9 +126,10 @@ static void redraw_all(void) {
da_draw_string(&g_ac, "Type", col_type_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
da_fill_rect(&g_ac, margin, top_y + CHAR_H, (i64)g_ac.fb_w - margin * 2, 1, DA_BORDER);
/* 文件列表 */
/* 滚动范围调整 */
if (scroll_off < 0) scroll_off = 0;
if (scroll_off > file_count - visible_rows && file_count > visible_rows) scroll_off = file_count - visible_rows;
if (file_count > visible_rows && scroll_off > file_count - visible_rows)
scroll_off = file_count - visible_rows;
if (scroll_off < 0) scroll_off = 0;
for (int i = 0; i < visible_rows; i++) {
@@ -93,28 +137,23 @@ static void redraw_all(void) {
if (idx >= file_count) break;
int y = top_y + CHAR_H + 6 + i * row_h;
/* 选中高亮 */
if (idx == selected) {
da_fill_rect(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_BG_TERTIARY);
da_rect_outline(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_ACCENT, 4);
}
/* 图标 */
u32 icon_color = files[idx].is_dir ? DA_WARNING : DA_ACCENT;
da_fill_rounded_rect(&g_ac, margin + 4, y, 24, 16, icon_color, 3);
/* 文件名 */
u32 name_color = (idx == selected) ? DA_ACCENT_LIGHT : DA_TEXT_PRIMARY;
da_draw_string(&g_ac, files[idx].name, col_name_x, y, name_color,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
/* 大小 */
if (!files[idx].is_dir) {
char sz[16]; int p = 0;
u32 s = files[idx].size;
if (s == 0) { sz[p++] = '0'; }
else { char tmp[12]; int tl = 0; while(s>0){tmp[tl++]='0'+(s%10);s/=10;} for(int j=tl-1;j>=0;j--)sz[p++]=tmp[j]; }
sz[p++] = ' '; sz[p++] = 'B'; sz[p] = 0;
char sz[16];
fm_u32_dec(sz, files[idx].size);
int sl = 0; while (sz[sl]) sl++;
sz[sl++] = ' '; sz[sl++] = 'B'; sz[sl] = 0;
da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
} else {
@@ -122,7 +161,6 @@ static void redraw_all(void) {
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
}
/* 类型 */
const char *type = files[idx].is_dir ? "DIR" : "FILE";
da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
@@ -134,18 +172,171 @@ static void redraw_all(void) {
}
/* 状态栏 */
char status[64]; int p = 0;
const char *p1 = "/ "; while (*p1) status[p++] = *p1++;
status[p++] = '0' + file_count / 10; status[p++] = '0' + file_count % 10;
const char *p2 = " items"; while (*p2) status[p++] = *p2++;
char status[80]; int p = 0;
char nc[12]; fm_u32_dec(nc, (u32)file_count);
const char *s1 = "items: "; while (*s1) status[p++] = *s1++;
for (int i = 0; nc[i]; i++) status[p++] = nc[i];
const char *s2 = " | Enter:open Backspace:up Esc:exit";
while (*s2) status[p++] = *s2++;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
/* 鼠标光标 */
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
/* ---- 文件查看器绘制 ---- */
static void redraw_view(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
/* 标题栏显示文件名 */
char title[32]; int p = 0;
const char *t = "View: ";
while (*t && p < 30) title[p++] = *t++;
if (selected >= 0 && selected < file_count) {
const char *fn = files[selected].name;
while (*fn && p < 30) title[p++] = *fn++;
}
title[p] = 0;
da_draw_titlebar(&g_ac, title, (i64)g_ac.fb_w);
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 2;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
/* 按行显示文本内容 */
int y = top_y;
u32 pos = 0;
int line = 0;
/* 跳过 view_scroll 行 */
int skip = view_scroll;
while (pos < view_size && skip > 0) {
if (view_buf[pos] == '\n') { skip--; }
pos++;
}
while (pos < view_size && line < visible_rows) {
/* 提取一行 */
int line_start = pos;
while (pos < view_size && view_buf[pos] != '\n' && pos - line_start < 80) pos++;
int line_len = pos - line_start;
/* 绘制字符 */
for (int i = 0; i < line_len && i < 80; i++) {
char c = (char)view_buf[line_start + i];
if (c == '\r') continue;
if (c < ' ' || c > '~') c = '.';
char buf[2]; buf[0] = c; buf[1] = 0;
da_draw_string(&g_ac, buf, margin + i * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY, CHAR_STEP);
}
y += row_h;
line++;
if (pos < view_size && view_buf[pos] == '\n') pos++;
/* 处理 \r\n */
if (pos < view_size && view_buf[pos] == '\r') pos++;
}
/* 状态栏 */
char status[80]; p = 0;
char sz[12]; fm_u32_dec(sz, view_size);
const char *s1 = "size: "; while (*s1) status[p++] = *s1++;
for (int i = 0; sz[i]; i++) status[p++] = sz[i];
const char *s2 = "B | Up/Down:scroll Esc:back";
while (*s2) status[p++] = *s2++;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_cursor_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
}
static void redraw_all(void) {
if (view_mode) redraw_view();
else redraw_list();
}
/* ---- 打开文件/目录 ---- */
static void open_selected(void) {
if (selected < 0 || selected >= file_count) return;
fm_entry *f = &files[selected];
/* 特殊处理 ".." 条目 */
if (path_depth > 0 && selected == 0 && f->name[0] == '.' && f->name[1] == '.' && f->name[2] == 0) {
go_up();
return;
}
if (f->is_dir) {
/* 进入子目录 */
if (path_depth < MAX_DEPTH - 1) {
path_clus[path_depth] = f->clus;
for (int i = 0; i < 13; i++) path_name[path_depth][i] = f->name[i];
path_name[path_depth][12] = 0;
path_depth++;
load_current_dir();
}
return;
}
/* 文件:读取内容进查看器 */
if (f->size == 0) {
view_size = 0;
} else {
u32 rd = f->size;
if (rd > VIEW_BUF) rd = VIEW_BUF;
if (f32_read_file_by_clus(f->clus, f->size, view_buf, VIEW_BUF) != 0) {
da_slog("fileman", "read file failed");
return;
}
view_size = rd;
}
view_mode = 1;
view_scroll = 0;
}
/* ---- 返回上级目录 ---- */
static void go_up(void) {
if (path_depth > 0) {
path_depth--;
load_current_dir();
}
}
/* 查找并进入 SYSTEM/user 目录 */
static void find_user_dir(void) {
/* 先查找 SYSTEM 目录 */
file_count = 0;
f32_list_dir(0, fm_collect_cb, 0);
u32 system_clus = 0;
for (int i = 0; i < file_count; i++) {
if (files[i].is_dir && files[i].name[0] == 'S' && files[i].name[1] == 'Y' &&
files[i].name[2] == 'S' && files[i].name[3] == 'T' &&
files[i].name[4] == 'E' && files[i].name[5] == 'M') {
system_clus = files[i].clus;
break;
}
}
if (!system_clus) return; /* SYSTEM 目录不存在,保持根目录 */
/* 在 SYSTEM 中查找 user 目录 */
file_count = 0;
f32_list_dir(system_clus, fm_collect_cb, 0);
for (int i = 0; i < file_count; i++) {
if (files[i].is_dir && files[i].name[0] == 'U' && files[i].name[1] == 'S' &&
files[i].name[2] == 'E' && files[i].name[3] == 'R') {
/* 找到了 SYSTEM/user,设置路径栈 */
path_clus[0] = system_clus;
path_name[0][0] = 'S'; path_name[0][1] = 'Y'; path_name[0][2] = 'S';
path_name[0][3] = 'T'; path_name[0][4] = 'E'; path_name[0][5] = 'M';
path_name[0][6] = 0;
path_clus[1] = files[i].clus;
path_name[1][0] = 'U'; path_name[1][1] = 'S'; path_name[1][2] = 'E';
path_name[1][3] = 'R'; path_name[1][4] = 0;
path_depth = 2;
return;
}
}
}
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
@@ -160,7 +351,13 @@ void dsk_entry(const da_boot_context *ctx) {
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init();
load_root_dir();
/* 初始化 fat32_io:注入 block_read/block_write */
f32_init((f32_block_read_fn)g_ac.block_read, (f32_block_write_fn)g_ac.block_write);
path_depth = 0;
/* 尝试进入 SYSTEM/user 目录 */
find_user_dir();
load_current_dir();
redraw_all();
int e0 = 0, shift = 0; (void)shift;
@@ -171,9 +368,14 @@ void dsk_entry(const da_boot_context *ctx) {
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
need_redraw = 1;
if (g_cursor.btn) {
/* 点击选择文件 */
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
/* 检查关闭按钮点击(右上角:w-28, 4, 24, 20) */
if (g_cursor.mx >= (i64)g_ac.fb_w - 28 && g_cursor.mx < (i64)g_ac.fb_w - 4 &&
g_cursor.my >= 4 && g_cursor.my < 24) {
da_slog("fileman", "close button clicked");
return;
}
if (!view_mode) {
int top_y = DA_TITLEBAR_H + 4 + CHAR_H + 4;
int row_h = CHAR_H + 6;
int click_y = g_cursor.my - top_y - CHAR_H - 6;
if (click_y >= 0) {
@@ -185,6 +387,7 @@ void dsk_entry(const da_boot_context *ctx) {
}
}
}
}
/* 键盘轮询 */
u8 st = inb(0x64);
@@ -196,11 +399,36 @@ void dsk_entry(const da_boot_context *ctx) {
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
if (sc & 0x80) { e0 = 0; goto skip; }
if (sc == 0x01) { da_slog("fileman", "exit"); return; }
if (sc == 0x01) { /* Esc */
if (view_mode) {
view_mode = 0;
need_redraw = 1;
} else {
da_slog("fileman", "exit");
return;
}
goto skip;
}
if (view_mode) {
/* 查看器模式:Up/Down 滚动 */
if (e0) {
if (sc == 0x48) { if (view_scroll > 0) view_scroll--; need_redraw = 1; }
else if (sc == 0x50) { view_scroll++; need_redraw = 1; }
e0 = 0;
goto skip;
}
/* PageUp/PageDown */
if (sc == 0x49) { view_scroll -= 10; if (view_scroll < 0) view_scroll = 0; need_redraw = 1; }
else if (sc == 0x51) { view_scroll += 10; need_redraw = 1; }
goto skip;
}
/* 列表模式 */
if (e0) {
if (sc == 0x48) { /* Up */
if (selected > 0) selected--;
if (selected < scroll_off) scroll_off = selected;
need_redraw = 1;
} else if (sc == 0x50) { /* Down */
if (selected < file_count - 1) selected++;
@@ -211,12 +439,17 @@ void dsk_entry(const da_boot_context *ctx) {
}
if (sc == 0x1C) { /* Enter - 打开 */
if (selected >= 0 && selected < file_count) {
da_slog("fileman", "open file (stub)");
}
open_selected();
need_redraw = 1;
} else if (sc == 0x0E) { /* Backspace - 返回上级 */
da_slog("fileman", "back (root only)");
go_up();
need_redraw = 1;
} else if (sc == 0x49) { /* PageUp */
selected -= 10; if (selected < 0) selected = 0;
if (selected < scroll_off) scroll_off = selected;
need_redraw = 1;
} else if (sc == 0x51) { /* PageDown */
selected += 10; if (selected >= file_count) selected = file_count - 1;
need_redraw = 1;
}
}
Binary file not shown.
+630
View File
@@ -0,0 +1,630 @@
#!/usr/bin/env python3
"""
gen_pe.py — Deshab PE 兼容层测试 PE 程序生成器(无需 MinGW/MSVC)
用途: 当宿主机无 MinGW/MSVC 时,用纯 Python 构造最小 PE32+ 和 PE32 二进制,
验证 pe_loader 解析与 pe_shim/x86emu32 执行路径。
构造的 PE 特征:
- PE32+ (64位): machine=0x8664, entry=main, image_base=0x140000000
- PE32 (32位): machine=0x014c, entry=main, image_base=0x00400000
- 单 section (.text), 4KB 对齐
- IAT 仅 2 项: msvcrt.dll!printf + kernel32.dll!ExitProcess
- main 函数: 调用 printf("Hello from PE32+/PE32!\\r\\n") → 调用 ExitProcess(0)
输出:
hello64.exe - PE32+ 二进制 (供 HELLO64 EXE 测试用)
hello32.exe - PE32 二进制 (供 HELLO32 EXE 测试用)
用法:
python gen_pe.py
# 生成 hello64.exe / hello32.exe 到当前目录
# 之后复制到 SYSTEM/system/deshab64/tools/ 并重新运行 build.bat
"""
import struct
import sys
import os
# ============================================================================
# PE32+ (64位) 构造
# ============================================================================
IMAGE_DOS_SIGNATURE = 0x5A4D # 'MZ'
IMAGE_NT_SIGNATURE = 0x00004550 # 'PE\0\0'
IMAGE_FILE_MACHINE_I386 = 0x014c
IMAGE_FILE_MACHINE_AMD64 = 0x8664
IMAGE_FILE_MACHINE_I386_CHARACTERISTICS = 0x0102 # EXECUTABLE_IMAGE | 32BIT_MACHINE
IMAGE_FILE_MACHINE_AMD64_CHARACTERISTICS = 0x0022 # EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
IMAGE_SCN_CNT_CODE = 0x20
IMAGE_SCN_MEM_EXECUTE = 0x20000000
IMAGE_SCN_MEM_READ = 0x40000000
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
def build_pe32_plus():
"""构造 PE32+ (64位) 测试程序。"""
image_base = 0x140000000
section_align = 0x1000
file_align = 0x200
# DOS header (64 bytes) + e_lfanew
dos_header = bytearray(64)
struct.pack_into('<H', dos_header, 0, IMAGE_DOS_SIGNATURE) # e_magic
dos_header[2:60] = b'\x00' * 58
struct.pack_into('<I', dos_header, 60, 64) # e_lfanew = 64 (PE header at offset 64)
# PE signature
pe_sig = struct.pack('<I', IMAGE_NT_SIGNATURE)
# COFF file header (20 bytes)
num_sections = 1
time_stamp = 0
sym_table_ptr = 0
num_symbols = 0
optional_header_size = 240 # PE32+ optional header size
characteristics = IMAGE_FILE_MACHINE_AMD64_CHARACTERISTICS
coff_header = struct.pack('<HHIIIHH',
IMAGE_FILE_MACHINE_AMD64,
num_sections,
time_stamp,
sym_table_ptr,
num_symbols,
optional_header_size,
characteristics)
# PE32+ Optional header (240 bytes)
# 布局:
# Magic (2) = 0x20b (PE32+)
# MajorLinkerVersion (1) = 0
# MinorLinkerVersion (1) = 0
# SizeOfCode (4) = .text 大小
# SizeOfInitializedData (4) = 0
# SizeOfUninitializedData (4) = 0
# AddressOfEntryPoint (4) = main RVA
# BaseOfCode (4) = 0x1000
# ImageBase (8) = 0x140000000
# SectionAlignment (4) = 0x1000
# FileAlignment (4) = 0x200
# MajorOperatingSystemVersion (2) = 6
# MinorOperatingSystemVersion (2) = 0
# MajorImageVersion (2) = 0
# MinorImageVersion (2) = 0
# MajorSubsystemVersion (2) = 6
# MinorSubsystemVersion (2) = 0
# Win32VersionValue (4) = 0
# SizeOfImage (4) = 0x3000
# SizeOfHeaders (4) = 0x200
# CheckSum (4) = 0
# Subsystem (2) = IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
# DllCharacteristics (2) = 0
# SizeOfStackReserve (8) = 0x100000
# SizeOfStackCommit (8) = 0x1000
# SizeOfHeapReserve (8) = 0x100000
# SizeOfHeapCommit (8) = 0x1000
# LoaderFlags (4) = 0
# NumberOfRvaAndSizes (4) = 16
# DataDirectory[16] (8*16 = 128 bytes)
# .text 段机器码(x86_64):
# main:
# lea rcx, [rip + msg] ; 48 8D 0D XX XX XX XX (printf arg1 = msg)
# call [rip + printf_iat] ; FF 15 XX XX XX XX (call IAT[printf])
# xor ecx, ecx ; 31 C9 (ExitProcess arg1 = 0)
# call [rip + exitproc_iat] ; FF 15 XX XX XX XX (call IAT[ExitProcess])
# ret ; C3
# msg: "Hello from PE32+!\r\n\0"
msg = b"Hello from PE32+!\r\n\0"
# 占位用,后面填正确偏移
code = bytearray()
code += b'\x48\x8D\x0D' + b'\x00\x00\x00\x00' # lea rcx, [rip+0] (placeholder)
code += b'\xFF\x15' + b'\x00\x00\x00\x00' # call [rip+0] (placeholder)
code += b'\x31\xC9' # xor ecx, ecx
code += b'\xFF\x15' + b'\x00\x00\x00\x00' # call [rip+0] (placeholder)
code += b'\xC3' # ret
code += msg
code_rva = 0x1000 # .text RVA
main_rva = code_rva
printf_iat_rva = code_rva + 0x200 # IAT 在 .text 后(同一 section 内,简化)
exitproc_iat_rva = printf_iat_rva + 8
# 修正 lea rcx, [rip + msg]: msg 在 code[19] 之后(lea 7字节 + call 6 + xor 2 + call 6 + ret 1 = 22)
msg_offset_in_code = 22
lea_rip_offset = msg_offset_in_code - len(code) + len(code) # 偏移=22-0=22(lea 后下一指令的 RIP)
# lea rcx, [rip + disp32]: disp32 = msg - (next_instr_rip)
# next_instr_rip = main_rva + 7
# msg = main_rva + 22
# disp32 = 22 - 7 = 15
struct.pack_into('<i', code, 3, 15)
# 修正 call [rip + printf_iat]: printf IAT 在 printf_iat_rva, call 后下一指令 RIP = main_rva + 13
# disp32 = printf_iat_rva - (main_rva + 13) = 0x200 - 13 = 499
struct.pack_into('<i', code, 10, 0x200 - 13)
# 修正 call [rip + exitproc]: ExitProcess IAT 在 exitproc_iat_rva, call 后下一指令 RIP = main_rva + 21
# disp32 = exitproc_iat_rva - (main_rva + 21) = 0x208 - 21 = 491
struct.pack_into('<i', code, 17, 0x208 - 21)
# IAT 8字节对齐填充
while len(code) < 0x200:
code += b'\x00'
# IAT 内容(指向 import 函数名 RVA)
iat_data = bytearray()
iat_data += struct.pack('<Q', 0x300) # printf function name RVA (hint+name 表)
iat_data += struct.pack('<Q', 0x320) # ExitProcess function name RVA
code += iat_data
# Import descriptor 数据
import_desc_rva = 0x400 # 在 IAT 后
while len(code) < 0x300:
code += b'\x00'
# Hint/Name 表
# "msvcrt.dll\0" + printf name "printf\0"
# "kernel32.dll\0" + ExitProcess name "ExitProcess\0"
printf_name_rva = 0x300
exitproc_name_rva = 0x320
printf_hint_name = struct.pack('<H', 0) + b'printf\0' # hint=0
exitproc_hint_name = struct.pack('<H', 0) + b'ExitProcess\0'
while len(code) < printf_name_rva - code_rva:
code += b'\x00'
code += printf_hint_name
while len(code) < exitproc_name_rva - code_rva:
code += b'\x00'
code += exitproc_hint_name
# DLL 名(放在 import descriptor 之后)
msvcrt_dll_rva = 0x350
kernel32_dll_rva = 0x360
while len(code) < msvcrt_dll_rva - code_rva:
code += b'\x00'
code += b'msvcrt.dll\0'
while len(code) < kernel32_dll_rva - code_rva:
code += b'\x00'
code += b'kernel32.dll\0'
# Import descriptor (5 * 4 + 4 = 24 bytes for original thunk 这里简化)
while len(code) < import_desc_rva - code_rva:
code += b'\x00'
import_desc = struct.pack('<IIIII',
printf_iat_rva, # OriginalFirstThunk
0, # TimeDateStamp
0, # ForwarderChain
msvcrt_dll_rva, # Name
printf_iat_rva) # FirstThunk
code += import_desc
import_desc2 = struct.pack('<IIIII',
exitproc_iat_rva,
0, 0,
kernel32_dll_rva,
exitproc_iat_rva)
code += import_desc2
# NULL 终止 import desc 数组
code += b'\x00' * 20
# 对齐到 file_align
while len(code) % file_align != 0:
code += b'\x00'
size_of_code = len(code)
size_of_image = code_rva + size_of_code
# 修正 size_of_image 到 section_align
while size_of_image % section_align != 0:
size_of_image += 1
opt_header = bytearray(240)
struct.pack_into('<H', opt_header, 0, 0x20b) # Magic = PE32+
opt_header[2] = 0 # MajorLinkerVersion
opt_header[3] = 0 # MinorLinkerVersion
struct.pack_into('<I', opt_header, 4, size_of_code) # SizeOfCode
struct.pack_into('<I', opt_header, 8, 0) # SizeOfInitializedData
struct.pack_into('<I', opt_header, 12, 0) # SizeOfUninitializedData
struct.pack_into('<I', opt_header, 16, main_rva) # AddressOfEntryPoint
struct.pack_into('<I', opt_header, 20, code_rva) # BaseOfCode
struct.pack_into('<Q', opt_header, 24, image_base) # ImageBase
struct.pack_into('<I', opt_header, 32, section_align)
struct.pack_into('<I', opt_header, 36, file_align)
struct.pack_into('<H', opt_header, 40, 6) # MajorOperatingSystemVersion
struct.pack_into('<H', opt_header, 42, 0)
struct.pack_into('<H', opt_header, 44, 0) # MajorImageVersion
struct.pack_into('<H', opt_header, 46, 0)
struct.pack_into('<H', opt_header, 48, 6) # MajorSubsystemVersion
struct.pack_into('<H', opt_header, 50, 0)
struct.pack_into('<I', opt_header, 52, 0) # Win32VersionValue
struct.pack_into('<I', opt_header, 56, size_of_image)
struct.pack_into('<I', opt_header, 60, 0x200) # SizeOfHeaders
struct.pack_into('<I', opt_header, 64, 0) # CheckSum
struct.pack_into('<H', opt_header, 68, IMAGE_SUBSYSTEM_WINDOWS_CUI) # Subsystem
struct.pack_into('<H', opt_header, 70, 0) # DllCharacteristics
struct.pack_into('<Q', opt_header, 72, 0x100000) # SizeOfStackReserve
struct.pack_into('<Q', opt_header, 80, 0x1000) # SizeOfStackCommit
struct.pack_into('<Q', opt_header, 88, 0x100000) # SizeOfHeapReserve
struct.pack_into('<Q', opt_header, 96, 0x1000) # SizeOfHeapCommit
struct.pack_into('<I', opt_header, 104, 0) # LoaderFlags
struct.pack_into('<I', opt_header, 108, 16) # NumberOfRvaAndSizes
# DataDirectory[16] = 16 entries * 8 bytes = 128 bytes
data_dir_offset = 112
# DataDirectory[1] = Import (RVA, Size)
struct.pack_into('<II', opt_header, data_dir_offset + 8, import_desc_rva, 48)
# Section header (.text)
section_header = bytearray(40)
section_header[0:8] = b'.text\0\0\0' # Name
struct.pack_into('<I', section_header, 8, size_of_code) # VirtualSize
struct.pack_into('<I', section_header, 12, code_rva) # VirtualAddress
struct.pack_into('<I', section_header, 16, size_of_code) # SizeOfRawData
struct.pack_into('<I', section_header, 20, file_align) # PointerToRawData
struct.pack_into('<I', section_header, 24, 0) # PointerToRelocations
struct.pack_into('<I', section_header, 28, 0) # PointerToLinenumbers
struct.pack_into('<H', section_header, 32, 0) # NumberOfRelocations
struct.pack_into('<H', section_header, 34, 0) # NumberOfLinenumbers
struct.pack_into('<I', section_header, 36,
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ)
# 组装文件
headers = bytearray()
headers += dos_header
headers += pe_sig
headers += coff_header
headers += opt_header
headers += section_header
# 头部对齐到 file_align
while len(headers) % file_align != 0:
headers += b'\x00'
return bytes(headers) + bytes(code)
def build_pe32():
"""构造 PE32 (32位) 测试程序(x86emu32 解释器执行)。
与 build_pe32_plus 的区别:
- Machine = 0x014c (i386), Characteristics = 0x0102
- Optional Header magic = 0x10b, 大小 224 字节
- 有 BaseOfData 字段;ImageBase 为 4 字节
- Stack/Heap Reserve/Commit 为 4 字节
- IAT thunk 为 4 字节(struct '<I')
- 机器码为 x86-32,使用 call [abs] 绝对寻址(FF 25 disp32)
"""
image_base = 0x00400000
section_align = 0x1000
file_align = 0x200
# DOS header (64 bytes) + e_lfanew
dos_header = bytearray(64)
struct.pack_into('<H', dos_header, 0, IMAGE_DOS_SIGNATURE)
dos_header[2:60] = b'\x00' * 58
struct.pack_into('<I', dos_header, 60, 64) # e_lfanew
pe_sig = struct.pack('<I', IMAGE_NT_SIGNATURE)
# COFF file header (20 bytes) — PE32 optional header = 224 字节
num_sections = 1
optional_header_size = 224
characteristics = IMAGE_FILE_MACHINE_I386_CHARACTERISTICS # 0x0102
coff_header = struct.pack('<HHIIIHH',
IMAGE_FILE_MACHINE_I386,
num_sections,
0, 0, 0,
optional_header_size,
characteristics)
# .text 段机器码(x86-32):
# main:
# push msg_addr ; 68 XX XX XX XX (5) printf 参数
# call [printf_iat_abs] ; FF 25 XX XX XX XX (6) 通过 IAT 调用
# add esp, 4 ; 83 C4 04 (3) cdecl 清栈
# push 0 ; 6A 00 (2) ExitProcess 参数
# call [exit_iat_abs] ; FF 25 XX XX XX XX (6) 通过 IAT 调用
# ret ; C3 (1)
# msg: "Hello from PE32!\r\n\0"
msg = b"Hello from PE32!\r\n\0"
code_rva = 0x1000
main_rva = code_rva
printf_iat_rva = code_rva + 0x200 # 0x1200
exitproc_iat_rva = printf_iat_rva + 4 # 0x1204 (4-byte thunk)
printf_name_rva = 0x1300
exitproc_name_rva = 0x1320
msvcrt_dll_rva = 0x1350
kernel32_dll_rva = 0x1360
import_desc_rva = 0x1400
msg_offset = 5 + 6 + 3 + 2 + 6 + 1 # = 23
msg_addr = image_base + code_rva + msg_offset # 0x401017
printf_iat_abs = image_base + printf_iat_rva # 0x401200
exitproc_iat_abs = image_base + exitproc_iat_rva # 0x401204
code = bytearray()
code += b'\x68' + struct.pack('<I', msg_addr) # push msg_addr
code += b'\xFF\x25' + struct.pack('<I', printf_iat_abs) # call [printf_iat]
code += b'\x83\xC4\x04' # add esp, 4
code += b'\x6A\x00' # push 0
code += b'\xFF\x25' + struct.pack('<I', exitproc_iat_abs) # call [exitproc_iat]
code += b'\xC3' # ret
code += msg
# 对齐到 IAT (0x200)
while len(code) < 0x200:
code += b'\x00'
# IAT (4-byte thunks,初始值 = hint/name RVA)
code += struct.pack('<I', printf_name_rva)
code += struct.pack('<I', exitproc_name_rva)
# 对齐到 hint/name 表 (0x300)
while len(code) < printf_name_rva - code_rva:
code += b'\x00'
code += struct.pack('<H', 0) + b'printf\0' # hint=0
while len(code) < exitproc_name_rva - code_rva:
code += b'\x00'
code += struct.pack('<H', 0) + b'ExitProcess\0'
# DLL 名
while len(code) < msvcrt_dll_rva - code_rva:
code += b'\x00'
code += b'msvcrt.dll\0'
while len(code) < kernel32_dll_rva - code_rva:
code += b'\x00'
code += b'kernel32.dll\0'
# Import descriptors
while len(code) < import_desc_rva - code_rva:
code += b'\x00'
code += struct.pack('<IIIII',
printf_iat_rva, # OriginalFirstThunk (INT)
0, 0,
msvcrt_dll_rva, # Name
printf_iat_rva) # FirstThunk (IAT)
code += struct.pack('<IIIII',
exitproc_iat_rva, # OriginalFirstThunk
0, 0,
kernel32_dll_rva, # Name
exitproc_iat_rva) # FirstThunk
code += b'\x00' * 20 # NULL 终止
# 对齐到 file_align
while len(code) % file_align != 0:
code += b'\x00'
size_of_code = len(code)
size_of_image = code_rva + size_of_code
while size_of_image % section_align != 0:
size_of_image += 1
# PE32 Optional Header (224 字节)
opt_header = bytearray(224)
struct.pack_into('<H', opt_header, 0, 0x10b) # Magic = PE32
opt_header[2] = 0 # MajorLinkerVersion
opt_header[3] = 0 # MinorLinkerVersion
struct.pack_into('<I', opt_header, 4, size_of_code) # SizeOfCode
struct.pack_into('<I', opt_header, 8, 0) # SizeOfInitializedData
struct.pack_into('<I', opt_header, 12, 0) # SizeOfUninitializedData
struct.pack_into('<I', opt_header, 16, main_rva) # AddressOfEntryPoint
struct.pack_into('<I', opt_header, 20, code_rva) # BaseOfCode
struct.pack_into('<I', opt_header, 24, 0) # BaseOfData (PE32 only)
struct.pack_into('<I', opt_header, 28, image_base) # ImageBase (32-bit)
struct.pack_into('<I', opt_header, 32, section_align)
struct.pack_into('<I', opt_header, 36, file_align)
struct.pack_into('<H', opt_header, 40, 6) # MajorOperatingSystemVersion
struct.pack_into('<H', opt_header, 42, 0)
struct.pack_into('<H', opt_header, 44, 0) # MajorImageVersion
struct.pack_into('<H', opt_header, 46, 0)
struct.pack_into('<H', opt_header, 48, 6) # MajorSubsystemVersion
struct.pack_into('<H', opt_header, 50, 0)
struct.pack_into('<I', opt_header, 52, 0) # Win32VersionValue
struct.pack_into('<I', opt_header, 56, size_of_image)
struct.pack_into('<I', opt_header, 60, 0x200) # SizeOfHeaders
struct.pack_into('<I', opt_header, 64, 0) # CheckSum
struct.pack_into('<H', opt_header, 68, IMAGE_SUBSYSTEM_WINDOWS_CUI) # Subsystem
struct.pack_into('<H', opt_header, 70, 0) # DllCharacteristics
struct.pack_into('<I', opt_header, 72, 0x100000) # SizeOfStackReserve (4字节)
struct.pack_into('<I', opt_header, 76, 0x1000) # SizeOfStackCommit
struct.pack_into('<I', opt_header, 80, 0x100000) # SizeOfHeapReserve
struct.pack_into('<I', opt_header, 84, 0x1000) # SizeOfHeapCommit
struct.pack_into('<I', opt_header, 88, 0) # LoaderFlags
struct.pack_into('<I', opt_header, 92, 16) # NumberOfRvaAndSizes
# DataDirectory[1] = Import (RVA, Size)
data_dir_offset = 96
struct.pack_into('<II', opt_header, data_dir_offset + 8, import_desc_rva, 60)
# Section header (.text)
section_header = bytearray(40)
section_header[0:8] = b'.text\0\0\0'
struct.pack_into('<I', section_header, 8, size_of_code) # VirtualSize
struct.pack_into('<I', section_header, 12, code_rva) # VirtualAddress
struct.pack_into('<I', section_header, 16, size_of_code) # SizeOfRawData
struct.pack_into('<I', section_header, 20, file_align) # PointerToRawData
struct.pack_into('<I', section_header, 24, 0)
struct.pack_into('<I', section_header, 28, 0)
struct.pack_into('<H', section_header, 32, 0)
struct.pack_into('<H', section_header, 34, 0)
struct.pack_into('<I', section_header, 36,
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ)
# 组装文件
headers = bytearray()
headers += dos_header
headers += pe_sig
headers += coff_header
headers += opt_header
headers += section_header
while len(headers) % file_align != 0:
headers += b'\x00'
return bytes(headers) + bytes(code)
def build_cmd_exe():
"""构造 cmd.exe — PE32+ (64位) 命令行测试程序。
与 build_pe32_plus() 类似,仅修改消息文本为 "Hello from cmd.exe!"
"""
image_base = 0x140000000
section_align = 0x1000
file_align = 0x200
dos_header = bytearray(64)
struct.pack_into('<H', dos_header, 0, IMAGE_DOS_SIGNATURE)
dos_header[2:60] = b'\x00' * 58
struct.pack_into('<I', dos_header, 60, 64)
pe_sig = struct.pack('<I', IMAGE_NT_SIGNATURE)
num_sections = 1
optional_header_size = 240
characteristics = IMAGE_FILE_MACHINE_AMD64_CHARACTERISTICS
coff_header = struct.pack('<HHIIIHH',
IMAGE_FILE_MACHINE_AMD64,
num_sections,
0, 0, 0,
optional_header_size,
characteristics)
msg = b"Hello from cmd.exe!\r\n\0"
code = bytearray()
code += b'\x48\x8D\x0D' + b'\x00\x00\x00\x00' # lea rcx, [rip+0]
code += b'\xFF\x15' + b'\x00\x00\x00\x00' # call [rip+0]
code += b'\x31\xC9' # xor ecx, ecx
code += b'\xFF\x15' + b'\x00\x00\x00\x00' # call [rip+0]
code += b'\xC3' # ret
code += msg
code_rva = 0x1000
main_rva = code_rva
printf_iat_rva = code_rva + 0x200
exitproc_iat_rva = printf_iat_rva + 8
msg_offset_in_code = 22
struct.pack_into('<i', code, 3, 15)
struct.pack_into('<i', code, 10, 0x200 - 13)
struct.pack_into('<i', code, 17, 0x208 - 21)
while len(code) < 0x200:
code += b'\x00'
code += struct.pack('<Q', 0x300)
code += struct.pack('<Q', 0x320)
import_desc_rva = 0x400
while len(code) < 0x300:
code += b'\x00'
printf_name_rva = 0x300
exitproc_name_rva = 0x320
printf_hint_name = struct.pack('<H', 0) + b'printf\0'
exitproc_hint_name = struct.pack('<H', 0) + b'ExitProcess\0'
while len(code) < printf_name_rva - code_rva:
code += b'\x00'
code += printf_hint_name
while len(code) < exitproc_name_rva - code_rva:
code += b'\x00'
code += exitproc_hint_name
msvcrt_dll_rva = 0x350
kernel32_dll_rva = 0x360
while len(code) < msvcrt_dll_rva - code_rva:
code += b'\x00'
code += b'msvcrt.dll\0'
while len(code) < kernel32_dll_rva - code_rva:
code += b'\x00'
code += b'kernel32.dll\0'
while len(code) < import_desc_rva - code_rva:
code += b'\x00'
code += struct.pack('<IIIII', printf_iat_rva, 0, 0, msvcrt_dll_rva, printf_iat_rva)
code += struct.pack('<IIIII', exitproc_iat_rva, 0, 0, kernel32_dll_rva, exitproc_iat_rva)
code += b'\x00' * 20
while len(code) % file_align != 0:
code += b'\x00'
size_of_code = len(code)
size_of_image = code_rva + size_of_code
while size_of_image % section_align != 0:
size_of_image += 1
opt_header = bytearray(240)
struct.pack_into('<H', opt_header, 0, 0x20b)
opt_header[2] = 0
opt_header[3] = 0
struct.pack_into('<I', opt_header, 4, size_of_code)
struct.pack_into('<I', opt_header, 8, 0)
struct.pack_into('<I', opt_header, 12, 0)
struct.pack_into('<I', opt_header, 16, main_rva)
struct.pack_into('<I', opt_header, 20, code_rva)
struct.pack_into('<Q', opt_header, 24, image_base)
struct.pack_into('<I', opt_header, 32, section_align)
struct.pack_into('<I', opt_header, 36, file_align)
struct.pack_into('<H', opt_header, 40, 6)
struct.pack_into('<H', opt_header, 42, 0)
struct.pack_into('<H', opt_header, 44, 0)
struct.pack_into('<H', opt_header, 46, 0)
struct.pack_into('<H', opt_header, 48, 6)
struct.pack_into('<H', opt_header, 50, 0)
struct.pack_into('<I', opt_header, 52, 0)
struct.pack_into('<I', opt_header, 56, size_of_image)
struct.pack_into('<I', opt_header, 60, 0x200)
struct.pack_into('<I', opt_header, 64, 0)
struct.pack_into('<H', opt_header, 68, IMAGE_SUBSYSTEM_WINDOWS_CUI)
struct.pack_into('<H', opt_header, 70, 0)
struct.pack_into('<Q', opt_header, 72, 0x100000)
struct.pack_into('<Q', opt_header, 80, 0x1000)
struct.pack_into('<Q', opt_header, 88, 0x100000)
struct.pack_into('<Q', opt_header, 96, 0x1000)
struct.pack_into('<I', opt_header, 104, 0)
struct.pack_into('<I', opt_header, 108, 16)
data_dir_offset = 112
struct.pack_into('<II', opt_header, data_dir_offset + 8, import_desc_rva, 48)
section_header = bytearray(40)
section_header[0:8] = b'.text\0\0\0'
struct.pack_into('<I', section_header, 8, size_of_code)
struct.pack_into('<I', section_header, 12, code_rva)
struct.pack_into('<I', section_header, 16, size_of_code)
struct.pack_into('<I', section_header, 20, file_align)
struct.pack_into('<I', section_header, 24, 0)
struct.pack_into('<I', section_header, 28, 0)
struct.pack_into('<H', section_header, 32, 0)
struct.pack_into('<H', section_header, 34, 0)
struct.pack_into('<I', section_header, 36,
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ)
headers = bytearray()
headers += dos_header
headers += pe_sig
headers += coff_header
headers += opt_header
headers += section_header
while len(headers) % file_align != 0:
headers += b'\x00'
return bytes(headers) + bytes(code)
def main():
out_dir = os.path.dirname(os.path.abspath(__file__))
pe32_plus = build_pe32_plus()
out_path = os.path.join(out_dir, 'hello64.exe')
with open(out_path, 'wb') as f:
f.write(pe32_plus)
print(f"[gen] Generated PE32+: {out_path} ({len(pe32_plus)} bytes)")
pe32 = build_pe32()
out_path = os.path.join(out_dir, 'hello32.exe')
with open(out_path, 'wb') as f:
f.write(pe32)
print(f"[gen] Generated PE32: {out_path} ({len(pe32)} bytes)")
cmd_exe = build_cmd_exe()
out_path = os.path.join(out_dir, 'cmd.exe')
with open(out_path, 'wb') as f:
f.write(cmd_exe)
print(f"[gen] Generated cmd.exe: {out_path} ({len(cmd_exe)} bytes)")
print("[gen] 完成。复制到 SYSTEM/system/deshab64/tools/ 并重新运行 build.bat")
if __name__ == '__main__':
main()
+27
View File
@@ -0,0 +1,27 @@
/* hello32.c — PE32 (32-bit) 测试程序
*
* 用途: 验证 Deshab PE 兼容层 x86emu32 解释器对 PE32 的加载与执行
*
* 调用的 shim API (pe_shim.c 已实现):
* - msvcrt.dll!printf
* - kernel32.dll!ExitProcess
*
* 编译 (MinGW-w64 32位):
* i686-w64-mingw32-gcc -o hello32.exe hello32.c -nostartfiles \
* -Wl,--entry,_main -Wl,--image-base,0x00400000
*
* 编译 (MSVC 32位):
* cl /Fe:hello32.exe hello32.c /link /ENTRY:_main /BASE:0x00400000
*
* 或直接使用 gen_pe.py 生成等价 PE32 二进制
*/
#include <windows.h>
#include <stdio.h>
int __cdecl main(void) {
printf("Hello from PE32!\r\n");
printf("Deshab x86emu32 interpreter works.\r\n");
ExitProcess(0);
return 0;
}
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
/* hello64.c — PE32+ (64-bit) 测试程序
*
* 用途: 验证 Deshab PE 兼容层 pe_service 对 PE32+ 的加载与原生 Ring0 执行
*
* 调用的 shim API (pe_shim.c 已实现):
* - msvcrt.dll!printf
* - kernel32.dll!ExitProcess
*
* 编译 (MinGW-w64):
* x86_64-w64-mingw32-gcc -o hello64.exe hello64.c -nostartfiles \
* -Wl,--entry,main -Wl,--image-base,0x140000000
*
* 编译 (MSVC):
* cl /Fe:hello64.exe hello64.c /link /ENTRY:main /BASE:0x140000000
*
* 或直接使用 gen_pe.py 生成等价 PE32+ 二进制
*/
#include <windows.h>
#include <stdio.h>
int main(void) {
printf("Hello from PE32+!\r\n");
printf("Deshab PE compat layer works.\r\n");
ExitProcess(0);
return 0;
}
Binary file not shown.
+174
View File
@@ -0,0 +1,174 @@
# UTSM ↔ Linux IPC 骨架 (Phase 1.3)
## 概述
Phase 1.3 实现了 UTSM 监控核与 Linux 主导核之间的双向 IPC 通信骨架,包含三个通信通道中的两个:
1. **共享内存环形缓冲区**(异步消息) — ✅ 已实现
2. **VMCALL hypercall**(同步请求/响应) — ✅ 已实现
3. **IPI 通知**(中断注入) — ⏳ Phase 1.4+
## 文件结构
### UTSM 侧(监控核)
```
CODE/UTSM/
├── include/utsm/
│ ├── ipc_shm.h — IPC 共享内存 API 声明
│ └── hypercall.h — VMCALL 处理器 API 声明
├── vmm/
│ ├── ipc_shm.c — 共享内存分配、初始化、EPT 映射、ring buffer 操作
│ ├── hypercall.c — VMCALL VM-Exit 处理(PING/HELLO/SHM_INFO/CONSOLE_WRITE)
│ ├── vmexit.c — VM-Exit 分发器(增加 VMCALL case)
│ ├── vmexit_asm.S — VM-Exit 汇编入口(重写:保存/恢复 guest GPR + 汇编 vmresume)
│ ├── vmm.c — VMM 核心(g_guest_regs 全局变量 + vmm_vmexit_entry 简化)
│ └── ept.c — EPT 页表(增加 ept_gpa_to_hpa() 翻译函数)
```
### 共享协议
```
CODE/utsm-ipc/
└── ipc_proto.h — 双内核共享的 IPC 协议头文件
```
### Linux 侧(主导核)
```
CODE/linux/
├── configs/
│ └── utsm_x86_64_defconfig — 增加 CONFIG_UTSM_HCALL=y
├── patches/
│ ├── utsm_hcall.c — Linux 内核驱动:VMCALL + /dev/utsm + SHM 映射
│ ├── utsm_Kconfig — Kconfig 配置
│ └── utsm_Makefile — 模块 Makefile
└── build.sh — 更新:自动安装驱动到 Linux 源码树
```
## 通信协议
### 共享内存布局(GPA 0x04000000,1MB)
```
0x04000000 utsm_ipc_shm_header (64 bytes)
├── magic = "UTSMHC\0\0"
├── version = 1
├── utsm_ready (UTSM 设置)
└── linux_ready (Linux 设置)
0x04000040 utsm_ipc_ring utsm_to_linux (UTSM 写,Linux 读)
└── 64 条消息,每条 256 字节
0x04001040 utsm_ipc_ring linux_to_utsm (Linux 写,UTSM 读)
└── 64 条消息,每条 256 字节
0x04002040 payload pool (~1MB,用于大数据传输)
```
### VMCALL Hypercall ABI
```
Linux guest 执行 VMCALL 指令:
RAX = UTSM_HCALL_MAGIC | op (0x5554534D48430000 | op)
RDI = arg0
RSI = arg1
RDX = arg2
UTSM 处理 VMCALL VM-Exit:
1. 从 g_guest_regs 读取 RAX/RDI/RSI/RDX
2. 验证 magic,提取 op
3. 分发到对应处理器
4. 返回值写入 g_guest_regs.rax
5. GUEST_RIP 前进过 VMCALL 指令
6. vmresume(GPR = 修改后的 guest 值)
```
### 已实现的 Hypercall 操作
| 操作码 | 名称 | 功能 |
|--------|------|------|
| 0x0001 | PING | 返回 "PONG" (0x504F4E47),用于探测 UTSM |
| 0x0002 | HELLO | 握手,UTSM 回复 HELLO_ACK 消息到 ring |
| 0x0010 | SHM_INFO | 返回共享内存 GPA + size |
| 0x0020 | CAP_VALIDATE | 能力校验(stub) |
| 0x0030 | UTRW_READ | 封缄内存读(stub) |
| 0x0031 | UTRW_WRITE | 封缄内存写(stub) |
| 0x0040 | DRR_CHECKPOINT | DRR checkpoint 触发(stub) |
| 0x0050 | CONSOLE_WRITE | 将 Linux 字符串输出到 UTSM 串口 |
| 0x0051 | CONSOLE_READ | 从 UTSM 串口读取(stub) |
## 关键设计决策
### 1. VM-Exit 汇编重写
VMCS 不保存 guest GPR(RAX-R15)。原汇编只保存 host caller-saved 寄存器,无法让 C 处理器读写 guest GPR。
重写后:
- VM-Exit 时保存全部 16 个 guest GPR 到 `g_guest_regs` 结构
- 调用 C 分发器(不再从 C 调用 vmresume)
- 恢复全部 16 个 guest GPR(C 可能已修改 rax = 返回值)
- 汇编中执行 vmresume(GPR = 修改后的 guest 值)
### 2. EPT GPA→HPA 翻译
Linux guest 使用 identity-mapped 页表(GVA=GPA),hypercall 传递的指针是 GPA。UTSM 需要:
- `ept_gpa_to_hpa(gpa)` — 4 级 EPT walk,支持 4KB/2MB/1GB 页
- 翻译后加 HHDM 偏移得到 host 虚拟地址
### 3. GPA 布局修复
原 `LINUX_GUEST_INITRD_GPA = 0x04000000` 与 `UTSM_IPC_SHM_GPA = 0x04000000` 冲突。
修复后:
```
0x00400000-0x004FFFFF 固定区域 (boot_params/cmdline/GDT/PGT/stack)
0x01000000-0x02FFFFFF Linux kernel
0x03000000-0x03FFFFFF initrd
0x04000000-0x040FFFFF IPC shared memory (1MB) ← e820 reserved
0x05000000-0x08FFFFFF general RAM (64MB)
```
## 验证方法
### 1. UTSM 侧编译验证(已完成)
```powershell
cd d:\Code\Deshab\CODE\UTSM
mingw32-make
# → utsm.elf 生成,0 warnings from new code
```
### 2. Linux 内核构建(需 Linux 环境)
```bash
cd CODE/linux
./build.sh
# → SYSTEM/boot/linux-bzImage
# → SYSTEM/boot/linux-initrd.img
```
### 3. QEMU 端到端测试(需 VMX 支持)
1. 取消 `SYSTEM/limine/limine.conf` 中 Linux 模块的注释
2. 运行 `ISO/run_qemu.bat`(需 KVM 或硬件 VMX)
3. 预期串口输出:
- `[UTSM] IPC shm init ok`
- `[LINUX] loader init ok`
- `[LINUX] vmlaunch`
- Linux 启动日志
- `[utsm] UTSM monitor detected (PONG=0x504F4E47)`
- `[utsm] /dev/utsm registered (IPC ready)`
- `[HCALL] HELLO from Linux guest`
4. Linux 用户态测试:
```bash
echo "hello UTSM" > /dev/utsm # → UTSM 串口显示 [LINUX] hello UTSM
cat /dev/utsm # → 读取 UTSM 发来的消息
```
## 后续阶段
- **Phase 1.4**: virtio-console 双向通信(`/dev/hvc0` ↔ UTSM 串口)
- **Phase 1.5**: virtio-fs 文件共享(Linux 挂载 `/utsm`)
- IPI 中断注入(替代当前的轮询方式)
- UTRW/CAP/DRR hypercall 完整实现
+266
View File
@@ -0,0 +1,266 @@
#ifndef UTSM_IPC_PROTO_H
#define UTSM_IPC_PROTO_H
/*
* UTSM ↔ Linux IPC Protocol — shared between both kernels.
*
* This header is included by:
* - UTSM kernel (CODE/UTSM/vmm/ipc_shm.c, hypercall.c)
* - Linux kernel module (CODE/linux/patches/utsm-ipc.patch → drivers/utsm/)
*
* Communication channels:
* 1. Shared memory ring buffer (async messages, bidirectional)
* 2. VMCALL hypercall (sync request/response)
* 3. IPI notification (UTSM → Linux via guest IRQ injection)
*
* Shared memory layout (at GPA 0x04000000, 1MB total):
* 0x04000000: utsm_ipc_shm_header (64 bytes)
* 0x04000040: utsm_ipc_ring utsm_to_linux (UTSM writes, Linux reads)
* 0x04001040: utsm_ipc_ring linux_to_utsm (Linux writes, UTSM reads)
* 0x04002040: payload pool (for large data transfers)
*/
#include <stdint.h>
/* ===== Constants ===== */
#define UTSM_IPC_MAGIC 0x5554534D48430000ULL /* "UTSMHC\00\00" */
#define UTSM_IPC_VERSION 1
#define UTSM_IPC_SHM_GPA 0x04000000ULL
#define UTSM_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
#define UTSM_IPC_QUEUE_DEPTH 64 /* messages per ring */
#define UTSM_IPC_MSG_DATA_SIZE 240 /* inline data per message */
#define UTSM_IPC_PAYLOAD_SIZE (UTSM_IPC_SHM_SIZE - 2 * 1024 - 64) /* ~1MB */
/* ===== Hypercall operations ===== */
enum utsm_hcall_op {
UTSM_HCALL_PING = 0x0001, /* Simple ping (returns PONG) */
UTSM_HCALL_HELLO = 0x0002, /* Hello handshake */
UTSM_HCALL_SHM_INFO = 0x0010, /* Query shared memory GPA + size */
UTSM_HCALL_CAP_VALIDATE = 0x0020, /* Capability validation */
UTSM_HCALL_UTRW_READ = 0x0030, /* Sealed memory read (UTRW) */
UTSM_HCALL_UTRW_WRITE = 0x0031, /* Sealed memory write (UTRW) */
UTSM_HCALL_DRR_CHECKPOINT= 0x0040, /* Trigger DRR checkpoint */
UTSM_HCALL_CONSOLE_WRITE = 0x0050, /* Write to UTSM serial console */
UTSM_HCALL_CONSOLE_READ = 0x0051, /* Read from UTSM serial console */
};
/* Hypercall return codes */
#define UTSM_HCALL_OK 0
#define UTSM_HCALL_INVAL (-1)
#define UTSM_HCALL_PERM (-2)
#define UTSM_HCALL_NOMEM (-3)
#define UTSM_HCALL_NOSYS (-4)
/* ===== Message types ===== */
enum utsm_ipc_msg_type {
UTSM_MSG_NONE = 0,
UTSM_MSG_HELLO = 1, /* Initial handshake */
UTSM_MSG_HELLO_ACK= 2, /* Handshake acknowledgment */
UTSM_MSG_PING = 3, /* Ping request */
UTSM_MSG_PONG = 4, /* Pong response */
UTSM_MSG_DATA = 5, /* Generic data message */
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) */
struct utsm_ipc_msg {
uint32_t type; /* enum utsm_ipc_msg_type */
uint32_t seq; /* Sequence number (monotonic) */
uint32_t data_len; /* Actual bytes in data[] */
uint32_t flags; /* Reserved for future use */
uint8_t data[UTSM_IPC_MSG_DATA_SIZE]; /* Inline payload */
} __attribute__((packed));
/* Ring buffer: single-producer, single-consumer queue.
*
* Producer writes at tail, then increments tail (with memory barrier).
* Consumer reads at head, then increments head (with memory barrier).
*
* Empty when head == tail.
* Full when (tail + 1) % capacity == head. */
struct utsm_ipc_ring {
volatile uint32_t head; /* Consumer updates (Linux or UTSM) */
volatile uint32_t tail; /* Producer updates (UTSM or Linux) */
uint32_t capacity; /* Max messages (UTSM_IPC_QUEUE_DEPTH) */
uint32_t reserved;
struct utsm_ipc_msg messages[UTSM_IPC_QUEUE_DEPTH];
} __attribute__((packed));
/* Shared memory header */
struct utsm_ipc_shm_header {
uint64_t magic; /* UTSM_IPC_MAGIC */
uint32_t version; /* UTSM_IPC_VERSION */
uint32_t header_size; /* sizeof(struct utsm_ipc_shm_header) */
uint64_t shm_phys; /* Physical address of SHM region */
uint64_t shm_size; /* Total SHM size */
volatile uint32_t utsm_ready; /* UTSM sets to 1 when initialized */
volatile uint32_t linux_ready; /* Linux sets to 1 when initialized */
uint8_t reserved[24];
} __attribute__((packed));
/* Full shared memory region */
struct utsm_ipc_shm {
struct utsm_ipc_shm_header header;
struct utsm_ipc_ring utsm_to_linux; /* UTSM writes, Linux reads */
struct utsm_ipc_ring linux_to_utsm; /* Linux writes, UTSM reads */
uint8_t payload_pool[UTSM_IPC_PAYLOAD_SIZE];
} __attribute__((packed));
/* ===== Ring buffer operations (inline, usable by both kernels) ===== */
/* Memory barriers — adapted for each kernel's environment.
* UTSM: use compiler barriers (__asm__ volatile("" ::: "memory"))
* Linux: use smp_store_release / smp_load_acquire
*
* For simplicity, we use full memory barriers here. */
static inline void utsm_ipc_mb(void) {
__asm__ volatile("mfence" ::: "memory");
}
/* Initialize a ring buffer */
static inline void utsm_ipc_ring_init(struct utsm_ipc_ring *ring) {
ring->head = 0;
ring->tail = 0;
ring->capacity = UTSM_IPC_QUEUE_DEPTH;
ring->reserved = 0;
utsm_ipc_mb();
}
/* Try to enqueue a message. Returns 0 on success, -1 if full. */
static inline int utsm_ipc_ring_push(struct utsm_ipc_ring *ring,
const struct utsm_ipc_msg *msg) {
uint32_t tail = ring->tail;
uint32_t next_tail = (tail + 1) % ring->capacity;
if (next_tail == ring->head) {
return -1; /* Full */
}
ring->messages[tail] = *msg;
utsm_ipc_mb(); /* Ensure message is visible before updating tail */
ring->tail = next_tail;
return 0;
}
/* Try to dequeue a message. Returns 0 on success, -1 if empty. */
static inline int utsm_ipc_ring_pop(struct utsm_ipc_ring *ring,
struct utsm_ipc_msg *msg) {
uint32_t head = ring->head;
if (head == ring->tail) {
return -1; /* Empty */
}
*msg = ring->messages[head];
utsm_ipc_mb(); /* Ensure we've read the message before updating head */
ring->head = (head + 1) % ring->capacity;
return 0;
}
/* Check if ring is empty */
static inline int utsm_ipc_ring_empty(const struct utsm_ipc_ring *ring) {
return ring->head == ring->tail;
}
/* Check if ring is full */
static inline int utsm_ipc_ring_full(const struct utsm_ipc_ring *ring) {
uint32_t next_tail = (ring->tail + 1) % ring->capacity;
return next_tail == ring->head;
}
/* ===== Hypercall ABI (Linux → UTSM) =====
*
* Linux calls VMCALL with:
* RAX = UTSM_IPC_MAGIC | op
* RDI = arg0
* RSI = arg1
* RDX = arg2
*
* UTSM handles the VMCALL VM-Exit and returns:
* RAX = return value (UTSM_HCALL_OK or error)
*
* The magic in RAX distinguishes UTSM hypercalls from other VMCALL users.
*/
#define UTSM_HCALL_MAGIC 0x5554534D48430000ULL /* "UTSMHC\00\00" */
static inline long utsm_hcall(uint64_t op, uint64_t a0, uint64_t a1, uint64_t a2) {
long ret;
__asm__ volatile("vmcall"
: "=a"(ret)
: "a"(UTSM_HCALL_MAGIC | op), "D"(a0), "S"(a1), "d"(a2)
: "memory");
return ret;
}
/* Convenience hypercall wrappers */
static inline long utsm_hcall_ping(void) {
return utsm_hcall(UTSM_HCALL_PING, 0, 0, 0);
}
static inline long utsm_hcall_hello(void) {
return utsm_hcall(UTSM_HCALL_HELLO, 0, 0, 0);
}
static inline long utsm_hcall_shm_info(uint64_t *gpa_out, uint64_t *size_out) {
uint64_t gpa = 0, size = 0;
long ret = utsm_hcall(UTSM_HCALL_SHM_INFO, (uint64_t)&gpa, (uint64_t)&size, 0);
if (ret == 0) {
if (gpa_out) *gpa_out = gpa;
if (size_out) *size_out = size;
}
return ret;
}
static inline long utsm_hcall_console_write(const char *buf, uint64_t len) {
return utsm_hcall(UTSM_HCALL_CONSOLE_WRITE, (uint64_t)buf, len, 0);
}
#endif /* UTSM_IPC_PROTO_H */
+25 -4
View File
@@ -1,5 +1,5 @@
@echo off
setlocal
@echo off
setlocal enabledelayedexpansion
set "SCRIPT_DIR=%~dp0"
set "IMG=%SCRIPT_DIR%deshab.img"
@@ -41,15 +41,35 @@ if not defined OVMF (
exit /b 1
)
REM ===== Accelerator selection =====
REM QEMU_ACCEL env var can override: "whpx" / "tcg"
REM Default: try WHPX first (for VMX/Linux compat), fallback to TCG if unavailable
if not defined QEMU_ACCEL set "QEMU_ACCEL=whpx"
REM Detect whether Linux compat layer is enabled (bzImage present in IMG)
set "LINUX_ENABLED=0"
findstr /C:"module_path: boot():/boot/linux-bzImage" "%SCRIPT_DIR%..\SYSTEM\limine\limine.conf" >nul 2>&1
if not errorlevel 1 (
set "LINUX_ENABLED=1"
)
echo [qemu] Using: %QEMU%
echo [qemu] UEFI: %OVMF%
echo [qemu] Image: %IMG%
echo [qemu] Accel: %QEMU_ACCEL%
if "%LINUX_ENABLED%"=="1" (
echo [qemu] Linux compat: ENABLED (requires WHPX or real hardware for VMX)
echo [qemu] If VMX unavailable, UTSM will auto-skip Linux and continue to DSK
) else (
echo [qemu] Linux compat: disabled (build via CODE/linux/build.sh to enable)
)
if exist "%SATA_IMG%" (
echo [qemu] SATA: %SATA_IMG%
"%QEMU%" ^
-accel %QEMU_ACCEL% ^
-machine q35 ^
-m 512M ^
-cpu max ^
-cpu qemu64 ^
-serial stdio ^
-drive if=pflash,format=raw,readonly=on,file="%OVMF%" ^
-drive format=raw,file="%IMG%",if=virtio ^
@@ -61,9 +81,10 @@ if exist "%SATA_IMG%" (
) else (
echo [qemu] SATA image not found, booting without block device
"%QEMU%" ^
-accel %QEMU_ACCEL% ^
-machine q35 ^
-m 512M ^
-cpu max ^
-cpu qemu64 ^
-serial stdio ^
-drive if=pflash,format=raw,readonly=on,file="%OVMF%" ^
-drive format=raw,file="%IMG%",if=virtio ^
+39
View File
@@ -0,0 +1,39 @@
# Deshab Bug 记录
> 所有测试与开发过程中发现的 bug 统一登记于此。每个 bug 按 BUG-YYYYMMDD-NNN 编号。
> 测试用例文档位于 RE/test-cases/。
## 严重程度定义
- P0 阻断: 影响主链路启动或核心功能完全不可用(必须立即修复)
- P1 严重: 功能可降级但关键路径受损(本轮内修复)
- P2 一般: 非关键路径异常或体验问题(下一里程碑前修复)
- P3 提示: 日志/小问题,不影响功能(记录备查)
## Bug 条目格式
```
### BUG-YYYYMMDD-NNN [P级] [模块] 简述
- 现象:
- 复现步骤:
- 串口/日志证据:
- 根因分析:
- 修复方案:
- 状态: [OPEN/FIXING/FIXED/CLOSED]
- 修复提交: <commit hash 或 "待提交">
- 验证: <验证方法与结果>
```
---
## Bug 列表
<!-- 在此追加 bug 条目,最新放在顶部 -->
P2: 软件Files的窗口点击叉无法关闭
P3: 在第一页已经有了终端,所以在第四页的桌面上不用显示终端和CMD
P2: 在开发者模式中的shell输入命令并回车时文字消失没有反应
P3: 启动时的打字机效果再快3倍
P2: 桌面中第一页的shell中输入命令回车后,上一条命令输入的文字会跑到下一行
P2: 桌面中第一页的Files显示的不是SYSTEM/user中的文件夹内容
P2: Files中没有显示文件夹,直接显示了文件夹下的文件
P2: Files没有提供“..”来供用户返回上一级的文件夹
+38
View File
@@ -0,0 +1,38 @@
# Deshab 测试用例矩阵
本目录组织 Deshab-OS 的系统化测试用例文档。
## 测试矩阵总览
| 文档 | 范围 | 优先级 |
|---|---|---|
| [boot-regression.md](boot-regression.md) | 启动链路回归(首启/正常/dev_mode/无盘/无NIC) | P0 |
| [linux-compat.md](linux-compat.md) | UTSM+Linux 双内核兼容层端到端 | P0 |
| [pe-compat.md](pe-compat.md) | PE/EXE 兼容层(PE32+ native + PE32 x86emu32) | P1 |
| [driver-matrix.md](driver-matrix.md) | 14 DKM 驱动功能矩阵 | P1 |
| [userapps-matrix.md](userapps-matrix.md) | 用户态程序矩阵(shell/cmd/desktop/tools) | P1 |
| [network-e2e.md](network-e2e.md) | 网络端到端(DHCP/ARP/DNS/ping/curl) | P1 |
| [performance-baseline.md](performance-baseline.md) | 性能基线(TSC 测量) | P2 |
| [x86emu32-coverage.md](x86emu32-coverage.md) | x86emu32 指令覆盖率 | P2 |
## 测试场景状态标记
- ✅ PASS: 全部测试项通过
- 🔶 PARTIAL: 部分测试项通过,存在已知限制
- ❌ FAIL: 关键测试项失败,需修复
- ⬜ PENDING: 未执行
- ⏭ SKIP: 因环境/依赖不可用而跳过
## 自动化执行
```powershell
# 运行全部自动化测试场景
.\ISO\run_tests.bat
# 输出
# ISO/logs/test-report-<timestamp>.md
```
## Bug 跟踪
所有测试发现的 bug 登记到 [../Bug记录.txt](../Bug记录.txt),并在对应测试文档中引用 bug 编号。
+96
View File
@@ -0,0 +1,96 @@
# 启动链路回归测试矩阵
> 对应计划 Phase C2:验证不同启动场景下系统行为正确
## 状态: ⬜ PENDING
## 测试场景
| # | 场景 | firstInit.txt | 配置 | 期望串口锚点 | 状态 | Bug# |
|---|---|---|---|---|---|---|
| B1 | 首次启动 | `0\n0` | 空 user.conf | `[DSK] firstInit=0` → `mouseInit` → `FirstInit` → 完成向导 → 写盘 | ⬜ | |
| B2 | 正常启动 | `1\n0` | 已有 user.conf | `[DSK] firstInit=1` → `login` → desktop | ⬜ | |
| B3 | dev_mode | `1\n1` | dev_mode=1 | `[DSK] dev_mode` → shell run_dev_tests 全绿 → Esc → 正常流程 | ⬜ | |
| B4 | 无 SATA 盘 | 任意 | 移除 sata_fat32_dsk.img | DSK fallback 到 Limine module | ⬜ | |
| B5 | 无 NIC | 任意 | 移除 `-netdev` | e1000 not found → netman 警告 → 继续启动 | ⬜ | |
| B6 | Linux 兼容层启用 | 任意 | +linux-bzImage +linux-initrd | UTSM VMM + Linux park → DSK 正常接管 | ⬜ | |
## 串口日志锚点(标准启动)
### 首次启动(B1)
```
[UTSM] boot
[UTSM] SELFTEST PASS
[UTSM] loading DSK
[DSK] boot
[DSK] context ok
[DSK] firstInit=0
[DSK] loading mouseInit.elf
[DSK] loading FirstInit.elf
[FirstInit] ...
[DSK] writing firstInit=1
[DSK] loading shell.elf
```
### 正常启动(B2)
```
[UTSM] boot
[UTSM] SELFTEST PASS
[UTSM] loading DSK
[DSK] boot
[DSK] firstInit=1
[DSK] loading login.elf
[DSK] loading desktop.elf
```
### dev_mode(B3)
```
[UTSM] boot
[UTSM] SELFTEST PASS
[DSK] dev_mode=1
[DSK] loading shell.elf
[shell] run_dev_tests:
[shell] ls: OK
[shell] echo>: OK
[shell] cat: OK
[shell] cp: OK
[shell] mv: OK
[shell] rm: OK
[shell] dev_tests done
... (Esc 返回后正常流程)
```
## 自动化脚本
[ISO/run_qemu_scenario.bat](../../ISO/run_qemu_scenario.bat) 接收场景名参数:
```powershell
.\ISO\run_qemu_scenario.bat firstboot
.\ISO\run_qemu_scenario.bat normal
.\ISO\run_qemu_scenario.bat devmode
.\ISO\run_qemu_scenario.bat nosata
.\ISO\run_qemu_scenario.bat nonic
.\ISO\run_qemu_scenario.bat linux
```
每个场景输出独立日志到 `ISO/logs/<scenario>-<timestamp>.log`。
## 配置文件准备
### B1 首次启动
- `SYSTEM/system/user/use/firstInit.txt`: `0\n0`
- 备份 `user.conf` 后清空内容(仅保留 `DESHAB_USERCONF_V1` 头)
### B3 dev_mode
- `firstInit.txt`: `1\n1`
- 保持现有 user.conf
### B4 无 SATA
- 临时重命名 `.build_tmp/sata_fat32_dsk.img` → `.build_tmp/sata_fat32_dsk.img.bak`
- run_qemu.bat 自动检测,缺失时跳过 `-device ide-hd`
+70
View File
@@ -0,0 +1,70 @@
# 14 DKM 驱动功能矩阵
> 对应计划 Phase C3:每个驱动的加载、探测、功能验证点
## 状态: ⬜ PENDING
## 驱动清单
| # | 驱动 | stage | class | required | QEMU 参数 | 期望串口日志 | 功能验证点 | 状态 | Bug# |
|---|---|---|---|---|---|---|---|---|---|
| 1 | timer | 0 | timer | ✅ | 默认 | `[DKM:timer]` | 100ms busy-wait 精度 | ⬜ | |
| 2 | apic | 0 | interrupt | ✅ | 默认 | `[DKM:apic] MADT found` | LAPIC MMIO 只读 | ⬜ | |
| 3 | acpi | 0 | platform | ❌ | 默认 | `[DKM:acpi]` | RSDP/XSDT 枚举 | ⬜ | |
| 4 | pci | 0 | bus | ✅ | 默认 | `[DKM:pci]` | PCI config space 扫描 | ⬜ | |
| 5 | console_fb | 1 | console | ❌ | 默认 | `[DKM:console_fb]` | 淡蓝色背景清屏 | ⬜ | |
| 6 | ahci | 1 | storage | ❌ | +SATA 测试盘 | `[DKM:ahci] ahci0 registered` | LBA0 读取 | ⬜ | |
| 7 | nvme | 1 | storage | ❌ | `-device nvme` | `[DKM:nvme] 1b36:0010 found` | BAR0 读取(高位 MMIO 未解决则 SKIP) | ⏭ | |
| 8 | bootfs | 1 | fs | ✅ | 默认 | `[DKM:bootfs]` | Limine boot module FS | ⬜ | |
| 9 | vfs | 2 | fs-core | ✅ | 默认 | `[DKM:vfs]` | 挂载 bootfs | ⬜ | |
| 10 | fat32 | 2 | fs | ❌ | +SATA 测试盘 | `[DKM:fat32]` | BPB/FAT/目录项解析 | ⬜ | |
| 11 | devfs | 2 | fs | ❌ | 默认 | `[DKM:devfs]` | /dev/version, /dev/fb0 等 | ⬜ | |
| 12 | e1000 | 3 | net | ❌ | `-device e1000` | `tx=ready rx=ready` | DHCP + ping | ⬜ | |
| 13 | virtio_net | 3 | net | ❌ | `-device virtio-net-pci` | `[DKM:virtio_net] 1af4:1001 found` | capability 枚举 | ⬜ | |
| 14 | ath9k | 3 | net | ❌ | (QEMU 不支持 WiFi) | `no Atheros WiFi device found` | 跳过(QEMU 限制) | ⏭ | |
| 15 | ps2kbd | 3 | input | ❌ | 默认 | scan code 读取 | 输入回显 | ⬜ | |
## stage 加载顺序
```
stage 0 (platform): timer → apic → acpi → pci
stage 1 (boot): console_fb → ahci → nvme → bootfs
stage 2 (fs): vfs → fat32 → devfs
stage 3 (optional): e1000 → virtio_net → ath9k → ps2kbd
```
## 测试设备 QEMU 参数
### AHCI + FAT32 测试盘
```
-drive id=sata0,format=raw,file=.build_tmp/sata_fat32_dsk.img,if=none ^
-device ide-hd,drive=sata0,bus=ide.0 ^
```
### NVMe
```
-device nvme,drive=nvme0,serial=UTSMTEST ^
-drive id=nvme0,format=raw,file=.build_tmp/nvme_test.img,if=none ^
```
### e1000
```
-netdev user,id=net0 ^
-device e1000,netdev=net0,mac=52:54:00:12:34:56 ^
```
### virtio-net
```
-netdev user,id=net0 ^
-device virtio-net-pci,netdev=net0 ^
```
## 已知限制
- NVMe BAR0 在 QEMU 中常位于 4G 以上,当前 HHDM 不能安全访问,需 Phase 0 高位 MMIO 映射解决
- ath9k 在 QEMU 中无对应设备模拟,永远走 "not found" 路径
- 多个 NIC 同时挂载会改变 PCI 拓扑,可能影响 e1000 探测位置
+77
View File
@@ -0,0 +1,77 @@
# Linux 兼容层测试矩阵
> 对应计划 Phase A:UTSM+Linux 双内核 park-and-resume 端到端验证
> 前置条件:Linux 6.6 LTS bzImage 已构建(SYSTEM/boot/linux-bzImage + linux-initrd.img)
## 状态: ⬜ PENDING
## 测试环境
- QEMU 加速: WHPX (`-accel whpx -cpu max`),TCG 不支持 VMX
- Linux 模块: limine.conf 中 bzImage/initrd 模块已启用
- 串口: `-serial stdio`,过滤 `Linux version|utsm-linux|linux_compat|UTSM.*Linux|DSK|shell`
## 测试矩阵
| # | 测试项 | 串口/Shell 预期 | 状态 | 失败处理 | Bug# |
|---|---|---|---|---|---|
| A5.1 | UTSM VMM self-test 通过 | `[UTSM] VMM self-test PASS` | ⬜ | 检查 VMCS/EPT 初始化;BIOS 启用 VT-x | |
| A5.2 | bzImage 加载与解析 | `[UTSM] Linux bzImage loaded` | ⬜ | 检查 linux_loader_init header magic 校验 | |
| A5.3 | EPT 映射 + vmlaunch | `[UTSM] Linux guest launched` | ⬜ | 检查 EPT 表、VMCS guest 状态 | |
| A5.4 | Linux 内核启动 | `Linux version 6.6.x` 串口日志 | ⬜ | 检查 cmdline、boot_params、E820 表 | |
| A5.5 | init 脚本运行 | `[utsm-linux] init started` | ⬜ | 检查 initramfs 结构 | |
| A5.6 | exec daemon 启动 + park | `[utsm-linux] starting utsm_exec_daemon...` → `linux_compat_init() ready` | ⬜ | 检查 daemon 编译、IPC 共享内存初始化 | |
| A5.7 | DSK 正常接管 | `[DSK] boot` → `[DSK] SELFTEST PASS` | ⬜ | 验证 Linux park 不阻塞 DSK | |
| A5.8 | shell `linux`(无参)状态查询 | `Linux 兼容层已就绪 (双内核 park-and-resume)` | ⬜ | 检查 reserved[5] 传递链 | |
| A5.9 | shell `linux ls` | 输出 initramfs 根目录内容 | ⬜ | 检查 IPC exec 协议、vmresume 路径 | |
| A5.10 | shell `linux uname -a` | 输出 `Linux ... 6.6.x ...` | ⬜ | 同上 | |
| A5.11 | shell `linux cat /etc/hostname` | 输出 hostname | ⬜ | 同上 | |
| A5.12 | 错误路径:`linux nosuchprog` | 输出 exec 失败,shell 不崩溃 | ⬜ | 检查 EXEC_EXIT 消息处理 | |
## 关键约束验证
| 约束 | 验证方法 | 状态 |
|---|---|---|
| Linux guest cmdline 含 `nohlt idle=poll` | 检查 [linux_loader.c](../../CODE/UTSM/vmm/linux_loader.c) cmdline 构造 | ⬜ |
| IPC 共享内存 GPA=0x04000000, 1MB | e820 中标记 RESERVED,Linux 不分配 | ⬜ |
| Linux guest RAM 起始 0x05000000 | 检查 EPT 映射范围 | ⬜ |
| exec daemon 先发 EXEC_READY 再 HLT | daemon 源码 [utsm_exec_daemon.c](../../CODE/linux/initramfs/bin/utsm_exec_daemon.c) | ⬜ |
| UTSM hypercall ABI: RAX=magic\|op | 检查 [hypercall.c](../../CODE/UTSM/vmm/hypercall.c) 处理 | ⬜ |
## 已知限制
- QEMU TCG 不支持 VMX,必须 WHPX 或真机
- WHPX 可能不暴露 nested VMX 给 guest(需实测)
- 若 CPU 不支持 VT-x,整个 Linux 兼容层降级为"不可用",不影响其他系统功能
## 手动测试命令
进入 shell 后执行:
```
deshab:/ $ linux
deshab:/ $ linux ls
deshab:/ $ linux uname -a
deshab:/ $ linux cat /etc/hostname
deshab:/ $ linux nosuchprog
```
## 串口日志锚点
启动后期望按顺序出现:
```
[UTSM] VMM self-test PASS
[UTSM] Linux bzImage loaded
[UTSM] Linux guest launched
Linux version 6.6.x ...
[utsm-linux] init started
[utsm-linux] kernel: 6.6.x
[utsm-linux] starting utsm_exec_daemon...
[utsm-linux] daemon PID=...
[utsm-linux] init: daemon running, entering idle hold
[UTSM] linux_compat service ready
[DSK] boot
[DSK] SELFTEST PASS
... (进入 shell 提示符)
```
+62
View File
@@ -0,0 +1,62 @@
# 网络端到端测试矩阵
> 对应计划 Phase C4 网络部分:e1000 + net_stack + ping/curl/browser
## 状态: ✅ 已验证([deshab_system_roadmap_plan.md](../../.trae/documents/deshab_system_roadmap_plan.md) Phase 1)
> 本矩阵用于回归验证,确保不退化
## 测试环境
- QEMU 网络: `-netdev user,id=net0 -device e1000,netdev=net0`
- slirp 默认: guest 10.0.2.15, 网关 10.0.2.2, DNS 10.0.2.3
- 主机 http 服务: `python -m http.server 8000`
## 测试矩阵
| # | 测试项 | 命令 | 预期 | 状态 | Bug# |
|---|---|---|---|---|---|
| N1 | e1000 驱动加载 | (自动) | `tx=ready rx=ready` | ✅ | |
| N2 | netman 注册 | (自动) | netman 打印设备枚举 | ✅ | |
| N3 | DHCP 获取 IP | (自动) | netman DHCP final rc=0 | ✅ | |
| N4 | ARP 解析网关 | (自动) | netman ARP final rc=0 | ✅ | |
| N5 | DNS 查询 | (自动) | netman DNS final rc=0 | ✅ | |
| N6 | shell ping 网关 | `ping 10.0.2.2` | 4/4 reply, RTT<10ms | ✅ | |
| N7 | shell ping 外网 | `ping 8.8.8.8` | (依赖主机联网) | 🔶 | |
| N8 | shell curl 本地 | `curl http://10.0.2.2:8000/` | HTTP 200 + 目录 HTML | ✅ | |
| N9 | shell curl 外网 | `curl http://example.com/` | HTTP 200 (依赖主机联网) | 🔶 | |
| N10 | curl.elf 工具 | `run CURL.ELF` | 独立工具执行 curl | ✅ | |
| N11 | ping.elf 工具 | `run PING.ELF` | 独立工具执行 ping | ✅ | |
| N12 | browser.elf 工具 | `run BROWSER.ELF` | URL 输入 + HTTP GET | ✅ | |
## 回归验证
每次启动 dev_mode 时,run_dev_tests 应包含:
```
[shell] dev_tests:
...
ping 10.0.2.2: OK (4/4 reply, RTT=Xms)
...
```
若 ping 失败:
1. 检查 e1000 是否加载(`-device e1000` 参数)
2. 检查 netman 是否成功 DHCP
3. 检查 ARP 是否解析网关
4. 串口日志 grep `e1000|netman|DHCP|ARP|DNS|network`
## 性能基线
参见 [performance-baseline.md](performance-baseline.md):
- ping RTT < 10ms
- curl 64KB 页面接收无明显停顿
- DHCP 总耗时 < 5s
## 已知限制
- HTTPS/TLS 不支持(curl 仅 http)
- TCP 仅最小 client 实现(无拥塞控制/乱序队列)
- QEMU slirp 不支持真实 Wi-Fi 模式,ath9k 驱动在 QEMU 中无设备
+72
View File
@@ -0,0 +1,72 @@
# PE/EXE 兼容层测试矩阵
> 对应计划 Phase B:PE32+ (64位 native) 与 PE32 (32位 x86emu32) 兼容层验证
> 入口:cmd.elf 通过 `ctx->reserved[4]` 取 pe_service
## 状态: ⬜ PENDING
## 测试程序
| 文件 | 类型 | 来源 | FAT32 8.3 名 | 路径 |
|---|---|---|---|---|
| hello64.exe | PE32+ (64位) | MinGW/MSVC 编译或 Python 生成 | `HELLO64 EXE` | SYSTEM/system/deshab64/tools/ |
| hello32.exe | PE32 (32位) | MinGW 交叉编译或 Python 生成 | `HELLO32 EXE` | SYSTEM/system/deshab64/tools/ |
## 测试矩阵
| # | 测试项 | 命令 | 预期输出 | 状态 | Bug# |
|---|---|---|---|---|---|
| B2.1 | PE32+ 加载执行 | `pe HELLO64.EXE` | `Hello from PE32+!` | ⬜ | |
| B2.2 | PE32 加载执行(解释器) | `pe HELLO32.EXE` | `Hello from PE32!` | ⬜ | |
| B2.3 | PE 头解析 | `peinfo HELLO64.EXE` | `machine=0x8664 entry=0x... image_base=0x...` | ⬜ | |
| B2.4 | 文件不存在错误路径 | `pe NONEXIST.EXE` | `Error: file not found` | ⬜ | |
| B2.5 | 无参用法提示 | `pe` | 显示用法 | ⬜ | |
| B2.6 | ExitProcess 返回 | (执行后) | cmd 接管,longjmp 跳回 pe_service_run | ⬜ | |
| B2.7 | PE32 头解析 | `peinfo HELLO32.EXE` | `machine=0x14c entry=0x... image_base=0x00400000` | ⬜ | |
## 关键 ABI 验证
| ABI | 验证方法 | 状态 |
|---|---|---|
| `ctx->reserved[4]` 持 pe_service* | [cmd/main.c:825](../../CODE/cmd/main.c#L825) | ⬜ |
| `PE_SERVICE_MAGIC` 校验 | [cmd/main.c:830](../../CODE/cmd/main.c#L830) | ⬜ |
| `g_pe_svc` 缓存 | [cmd/main.c:108](../../CODE/cmd/main.c#L108) | ⬜ |
| PE32+ IAT 填 ms_abi shim | [pe_loader.c](../../CODE/UTSM/pe/pe_loader.c) | ⬜ |
| PE32 IAT 填合成地址 0x00010000\|idx | [pe_loader.c](../../CODE/UTSM/pe/pe_loader.c) | ⬜ |
| x86emu32 拦截 CALL 合成地址 | [x86emu32.c](../../CODE/UTSM/pe/x86emu32.c) | ⬜ |
## 测试程序源码
### hello64.c (PE32+)
```c
#include <windows.h>
int main(void) {
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "Hello from PE32+!\r\n", 19, NULL, NULL);
return 0;
}
```
编译:`x86_64-w64-mingw32-gcc -o hello64.exe hello64.c` 或 `cl /Fe:hello64.exe hello64.c`
### hello32.c (PE32)
```c
#include <windows.h>
int main(void) {
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "Hello from PE32!\r\n", 18, NULL, NULL);
return 0;
}
```
编译:`i686-w64-mingw32-gcc -o hello32.exe hello32.c -Wl,--image-base=0x00400000`
### Fallback: Python 生成器
若无 MinGW/MSVC,使用 [CODE/tools/pe-samples/gen_pe.py](../../CODE/tools/pe-samples/gen_pe.py) 生成最小 PE 程序。
## 已知限制
- pe_shim 实现的 Win32 API 子集有限,测试程序仅使用 WriteFile + ExitProcess
- x86emu32 指令覆盖率参见 [x86emu32-coverage.md](x86emu32-coverage.md)
- TLS / IFUNC / lazy binding 不支持(DKM ABI 约束)
+88
View File
@@ -0,0 +1,88 @@
# 性能基线测量
> 对应计划 Phase C5:基于 TSC 的关键性能指标测量
## 状态: ⬜ PENDING
## 测量方法
所有指标基于 TSC(Time Stamp Counter):
```c
static inline u64 rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
```
TSC 频率需先校准(PIT 100ms busy-wait 测量):
```c
u64 tsc_freq; // Hz
u64 calibrate_tsc(void) {
u64 start = rdtsc();
pit_busy_wait(100); // 100ms
u64 end = rdtsc();
return (end - start) * 10; // ×10 得到 Hz
}
```
时间换算:`ms = (tsc_diff * 1000) / tsc_freq`
## 基线指标
| # | 指标 | 测量位置 | 基线目标 | 实测 | 状态 |
|---|---|---|---|---|---|
| P1 | UTSM 启动到 DSK SELFTEST | UTSM entry → DSK selftest pass | < 500ms (QEMU) | | ⬜ |
| P2 | DSK 加载 shell.elf 时间 | FAT32 read + PIE load 包围 | < 200ms | | ⬜ |
| P3 | DSK 加载 desktop.elf 时间 | 同上 | < 200ms | | ⬜ |
| P4 | shell `ping 10.0.2.2` RTT | echo request → reply | < 10ms (user-net) | | ⬜ |
| P5 | shell `linux ls` 完整 RTT | IPC exec round-trip | < 100ms (park-and-resume) | | ⬜ |
| P6 | shell `run PING.ELF` 加载时间 | FAT32 read + PIE load | < 150ms | | ⬜ |
| P7 | desktop 窗口拖动帧率 | 帧计数/TSC | 30+ FPS | | ⬜ |
| P8 | `pe HELLO64.EXE` 加载+执行 | pe_service_run 包围 | < 50ms | | ⬜ |
| P9 | `pe HELLO32.EXE` 加载+执行 | x86emu32 解释 | < 200ms | | ⬜ |
| P10 | UTSM VMM self-test | vmm_init → vmm_self_test | < 50ms | | ⬜ |
| P11 | Linux guest 启动到 park | vmlaunch → daemon HLT | < 3s (QEMU WHPX) | | ⬜ |
| P12 | e1000 DHCP 完整耗时 | netman DHCP start → final rc | < 5s | | ⬜ |
## 自动化测量
在 dev_mode run_dev_tests 中追加性能测量代码:
```c
/* 在 run_dev_tests 开头校准 TSC */
u64 tsc_freq = calibrate_tsc();
logl("[shell] TSC freq: <Hz>");
/* 各项测试包围 TSC */
u64 t0 = rdtsc();
/* ... 操作 ... */
u64 t1 = rdtsc();
u64 ms = (t1 - t0) * 1000 / tsc_freq;
logl("[shell] perf: <name> = <ms>ms");
```
输出到串口日志,由 [ISO/run_tests.bat](../../ISO/run_tests.bat) 收集汇总。
## 性能回归检测
```powershell
# 比较两次测试结果
$prev = Get-Content ISO/logs/perf-baseline-prev.json | ConvertFrom-Json
$curr = Get-Content ISO/logs/perf-baseline-curr.json | ConvertFrom-Json
foreach ($m in $curr.metrics) {
$p = $prev.metrics | Where-Object name -eq $m.name
if ($p -and $m.ms -gt $p.ms * 1.5) {
Write-Host "[REGRESSION] $($m.name): $($p.ms)ms -> $($m.ms)ms (>50%)"
}
}
```
## 已知限制
- QEMU 性能数据不等于真机,仅作回归参考
- WHPX vs TCG 性能差异巨大,必须固定加速方案
- 多次测量取中位数,避免单次抖动
+76
View File
@@ -0,0 +1,76 @@
# 用户态程序功能矩阵
> 对应计划 Phase C4:FirstInit/login/desktop/shell/cmd + 5 个工具应用
## 状态: ⬜ PENDING
## 程序矩阵
| # | 程序 | ELF 路径 | 入口 | 关键命令/功能 | 验证点 | 状态 | Bug# |
|---|---|---|---|---|---|---|---|
| 1 | FirstInit | `system/user/use/FirstInit.elf` | 首启触发 | 计算机名/用户名/密码输入、空字段红字、网络配置 | SHA256 密码、user.conf 写入缓冲 | ⬜ | |
| 2 | login | `system/user/use/login.elf` | 次启触发 | 用户选择、密码输入、错误重试 | USER.CONF 解密、密码匹配 | ⬜ | |
| 3 | desktop | `system/deshab64/desktop.elf` | login 后 | 双击图标、窗口拖动、任务栏、内建 bash | 6 个工具启动、鼠标光标 | ⬜ | |
| 4 | shell | `system/deshab64/shell.elf` | desktop Terminal | ls/cat/cp/mv/rm/echo>/ping/curl/run/linux | dev_mode 自动测试全绿 | ⬜ | |
| 5 | cmd | `system/deshab64/cmd.elf` | desktop CMD | dir/type/copy/del + `pe <NAME.EXE>` | PE 兼容层调用 | ⬜ | |
| 6 | netman | `system/deshab64/network/netman.elf` | DSK 调度 | 读 conf.conf、应用网络配置 | DHCP/静态 IP | ⬜ | |
| 7 | mouseInit | `system/deshab64/mouse/mouseInit.elf` | DSK 调度 | 安全 stub | 不阻塞启动 | ⬜ | |
| 8 | editor | `system/deshab64/tools/editor.elf` | desktop 双击 | 文件打开/编辑/保存 | FAT32 写盘 | ⬜ | |
| 9 | fileman | `system/deshab64/tools/fileman.elf` | desktop 双击 | 浏览/打开/复制 | 目录遍历 | ⬜ | |
| 10 | browser | `system/deshab64/tools/browser.elf` | desktop 双击 | URL 输入/HTTP GET | net_stack 协议栈 | ⬜ | |
| 11 | curl | `system/deshab64/tools/curl.elf` | desktop 双击 | `curl http://...` | HTTP GET | ⬜ | |
| 12 | ping | `system/deshab64/tools/ping.elf` | desktop 双击 | `ping <ip>` | ICMP echo | ⬜ | |
## 启动链路
```
首启: utsm → DSK → mouseInit → netman → FirstInit → desktop
次启: utsm → DSK → login → desktop
dev_mode: utsm → DSK → shell(dev_tests) → Esc → login → desktop
desktop → 双击 Terminal → shell.elf
desktop → 双击 CMD → cmd.elf
shell → run FILEMAN.ELF → fileman.elf → Esc 返回 shell
shell → run BROWSER.ELF → browser.elf
shell → run EDITOR.ELF → editor.elf
shell → run PING.ELF → ping.elf
shell → run CURL.ELF → curl.elf
```
## 详细测试用例
### shell 命令测试
| 命令 | 参数 | 预期 | 状态 |
|---|---|---|---|
| `ls` | (无) | 列出根目录文件(DESHAB.ELF, SHELL.ELF 等) | ⬜ |
| `pwd` | (无) | `/` | ⬜ |
| `cat` | `README.TXT` | 显示文件内容 | ⬜ |
| `cp` | `a b` | 复制成功 | ⬜ |
| `mv` | `a b` | 移动成功 | ⬜ |
| `rm` | `a` | 删除成功 | ⬜ |
| `echo>` | `text > file` | 写入文件 | ⬜ |
| `ping` | `10.0.2.2` | 4/4 reply | ⬜ |
| `curl` | `http://10.0.2.2:8000/` | HTTP 200 + body | ⬜ |
| `run` | `PING.ELF` | 加载并执行 ping | ⬜ |
| `linux` | (无) | 显示兼容层状态 | ⬜ |
| `linux` | `ls` | 输出 initramfs 内容 | ⬜ |
| `help` | (无) | 显示命令列表 | ⬜ |
| `ver`/`version` | (无) | 显示版本 | ⬜ |
### desktop UI 测试
| 操作 | 预期 | 状态 |
|---|---|---|
| 双击 Terminal 图标 | 打开 shell 窗口 | ⬜ |
| 双击 CMD 图标 | 打开 cmd 窗口 | ⬜ |
| 双击 Editor 图标 | 打开 editor | ⬜ |
| 双击 File Manager 图标 | 打开 fileman | ⬜ |
| 双击 Browser 图标 | 打开 browser | ⬜ |
| 鼠标移动 | 光标跟随,无闪烁 | ⬜ |
| 窗口拖动 | 流畅,无撕裂 | ⬜ |
| 任务栏点击 | 窗口切换 | ⬜ |
## 已知问题
- fileman.elf 之前触发过 QEMU 退出(访问未支持 boot context 字段),需观察是否复现
- 真实 TTF 字体未运行时光栅化,使用预渲染位图(已知设计选择,非 bug)
+86
View File
@@ -0,0 +1,86 @@
# x86emu32 指令覆盖率测试
> 对应计划 Phase B3:枚举 x86emu32.c 已实现指令,验证覆盖率
## 状态: ⬜ PENDING
## 已实现指令清单
> 待根据 [x86emu32.c](../../CODE/UTSM/pe/x86emu32.c) 实际代码枚举填充
### 数据传送
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| MOV r32, imm32 | B8+rd | `mov eax, 0x12345678` | eax=0x12345678 | ⬜ |
| MOV r32, r/m32 | 8B /r | `mov eax, ebx` | eax=ebx | ⬜ |
| MOV r/m32, r32 | 89 /r | `mov [esi], eax` | [esi]=eax | ⬜ |
| PUSH r32 | 50+rd | `push eax` | [esp]=eax, esp-=4 | ⬜ |
| POP r32 | 58+rd | `pop eax` | eax=[esp], esp+=4 | ⬜ |
### 算术运算
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| ADD r32, r/m32 | 03 /r | `add eax, ebx` | eax=eax+ebx | ⬜ |
| SUB r32, r/m32 | 2B /r | `sub eax, ebx` | eax=eax-ebx | ⬜ |
| INC r32 | FF /0 | `inc eax` | eax=eax+1 | ⬜ |
| DEC r32 | FF /1 | `dec eax` | eax=eax-1 | ⬜ |
| MUL r/m32 | F7 /4 | `mul ebx` | edx:eax=eax*ebx | ⬜ |
| DIV r/m32 | F7 /6 | `div ebx` | eax=edx:eax/ebx | ⬜ |
### 逻辑运算
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| AND r32, r/m32 | 23 /r | `and eax, ebx` | eax=eax&ebx | ⬜ |
| OR r32, r/m32 | 0B /r | `or eax, ebx` | eax=eax\|ebx | ⬜ |
| XOR r32, r/m32 | 33 /r | `xor eax, ebx` | eax=eax^ebx | ⬜ |
| NOT r/m32 | F7 /2 | `not eax` | eax=~eax | ⬜ |
### 控制流
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| JMP rel32 | E9 cw/cd | `jmp label` | rip=label | ⬜ |
| JMP r/m32 | FF /4 | `jmp eax` | rip=eax | ⬜ |
| CALL rel32 | E8 cd | `call func` | push rip, rip=func | ⬜ |
| RET | C3 | `ret` | pop rip | ⬜ |
| Jcc rel32 | 0F 8x cd | `jz label` | 条件跳转 | ⬜ |
### 字符串操作
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| MOVSB | A4 | `movsb` | [edi]=[esi], inc/dec | ⬜ |
| STOSB | AA | `stosb` | [edi]=al, inc/dec | ⬜ |
| REP MOVSB | F3 A4 | `rep movsb` | 重复 ecx 次 | ⬜ |
| REP STOSB | F3 AA | `rep stosb` | 重复 ecx 次 | ⬜ |
### 堆栈与控制
| 指令 | 操作码 | 测试程序 | 预期 | 状态 |
|---|---|---|---|---|
| ENTER imm16, imm8 | C8 iw ib | `enter 8, 0` | 建立栈帧 | ⬜ |
| LEAVE | C9 | `leave` | 释放栈帧 | ⬜ |
| NOP | 90 | `nop` | 无操作 | ⬜ |
| INT3 | CC | `int3` | 触发断点 | ⬜ |
## 测试 PE 程序
每个指令类别构造一个最小 PE32 测试程序,在 [CODE/tools/pe-samples/](../../CODE/tools/pe-samples/) 下:
- `test_arith.exe`:算术运算
- `test_logic.exe`:逻辑运算
- `test_control.exe`:控制流
- `test_string.exe`:字符串操作
每个测试程序通过 WriteFile 输出结果到 cmd.elf,与预期对比。
## 覆盖率统计
```
已测试指令数 / 已实现指令数 = 覆盖率
```
目标:核心指令(MOV/ADD/SUB/CMP/JMP/CALL/RET/PUSH/POP)覆盖率 100%。
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+21
View File
@@ -42,3 +42,24 @@ verbose: yes
module_cmdline: dsk:main
module_path: boot():/driver/test.fat32
module_cmdline: dkm:test_fat32
# 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 <prog>` command
# invokes linux_compat_service->exec() → vmresume → daemon runs prog
#
# Linux 兼容层模块(需先通过 WSL 运行 CODE/linux/build.sh 构建产物)
# build.sh 成功后会自动取消下面 4 行注释;若未构建 Linux 内核请保持注释状态
# module_path: boot():/boot/linux-bzImage
# module_cmdline: linux:bzImage
# module_path: boot():/boot/linux-initrd.img
# module_cmdline: linux:initrd
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More