This commit is contained in:
xyou
2026-07-28 12:00:02 +08:00
parent efa626ed17
commit 17a577f2ed
51 changed files with 8446 additions and 787 deletions
+4 -4
View File
@@ -5,12 +5,12 @@ OBJCOPY := llvm-objcopy
ROOT := ../.. ROOT := ../..
OUT := $(ROOT)/SYSTEM/boot/utsm.elf 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 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 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 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) 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)
S_SOURCES := $(wildcard arch/x86_64/*.S) S_SOURCES := $(wildcard arch/x86_64/*.S vmm/*.S)
OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o) OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o)
.PHONY: all clean .PHONY: all clean
+262
View File
@@ -706,4 +706,266 @@ static inline du_u32 du_theme_accent(du_theme t) {
return (t == DU_THEME_DARK) ? DS_DARK_ACCENT : DS_LIGHT_ACCENT; 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 0xFF1A3A4Au /* 不透明近似 */
#define DP_KATE_PANEL_BORDER 0x5900E5FFu /* --panel-border: rgba(0,229,255,0.35) */
#define DP_KATE_PANEL_BORDER_SOLID 0xFF005A6Au /* 不透明近似 */
/* --- 辅助色:霓虹绿 (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 特效函数
* =================================================================== */
/* 绘制扫描线纹理( repeating-linear-gradient 0deg, 3px 周期)
* 在指定矩形区域内叠加半透明青色水平细线 */
static inline void du_kate_scanlines(du_context *ctx, du_i64 x, du_i64 y,
du_i64 w, du_i64 h) {
for (du_i64 ry = 0; ry < h; ry++) {
du_i64 yy = y + ry;
if (yy < 0 || (du_u64)yy >= ctx->height) continue; /* 每 3 像素一条线 */
du_u32 *line = (du_u32 *)((du_u8 *)ctx->fb + (du_u64)yy * ctx->pitch);
for (du_i64 rx = 0; rx < w; rx++) {
du_i64 xx = x + rx;
if (xx < 0 || (du_u64)xx >= ctx->width) continue;
/* 叠加极淡青色(alpha ~0.03 ≈ 8/255)*/
du_u32 cur = line[(du_u64)xx];
du_u32 b = (cur >> 16) & 0xFF;
du_u32 g = (cur >> 8) & 0xFF;
du_u32 r = cur & 0xFF;
b = (b + 2 < 255) ? b + 2 : 255;
g = (g + 4 < 255) ? g + 4 : 255;
r = (r + 2 < 255) ? r + 2 : 255;
line[(du_u64)xx] = 0xFF000000u | (b << 16) | (g << 8) | r;
}
}
}
/* 绘制带光晕的矩形边框(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 */ #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 */
+170
View File
@@ -0,0 +1,170 @@
#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 */
/* ===== 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). */
#define LINUX_DEFAULT_CMDLINE \
"console=ttyS0,115200 earlyprintk=serial nokaslr " \
"no_timer_check loglevel=7"
#endif
+4
View File
@@ -9,6 +9,10 @@
#include <utsm/segment.h> #include <utsm/segment.h>
#include <utsm/capability.h> #include <utsm/capability.h>
#include <utsm/utrw.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); void utsm_init(void);
int utsm_selftest_run(void); int utsm_selftest_run(void);
+63
View File
@@ -0,0 +1,63 @@
#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;
/* ===== 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
+268
View File
@@ -0,0 +1,268 @@
#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)
/* ===== Primary processor-based controls ===== */
#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_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_APIC_ACCESS 44
/* ===== 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);
#endif
+44
View File
@@ -7,6 +7,7 @@
#include <utsm/dsk.h> #include <utsm/dsk.h>
#include <utsm/block.h> #include <utsm/block.h>
#include <utsm/net.h> #include <utsm/net.h>
#include <utsm/ipc_shm.h>
void arch_halt_forever(void); void arch_halt_forever(void);
@@ -43,6 +44,49 @@ void kernel_main(void) {
int result = utsm_selftest_run(); int result = utsm_selftest_run();
if (result == 0) { if (result == 0) {
log_info("[UTSM] SELFTEST PASS"); 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 terminated");
} 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) { if (dsk_load_and_jump() != 0) {
log_error("[UTSM] DSK jump failed"); log_error("[UTSM] DSK jump failed");
} }
Binary file not shown.
+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);
}
+343
View File
@@ -0,0 +1,343 @@
/* 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/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);
/* Globals shared with vmexit_asm.S (must match vmm.c) */
extern volatile u64 g_saved_host_rsp;
extern volatile u64 g_saved_return_rip;
extern volatile int g_guest_terminated;
extern volatile u64 g_last_exit_reason;
/* Host stack for VM-Exit (reuse the one from vmm.c) */
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) {
u64 pin = vmx_adjust_control(PIN_EXT_INTERRUPT_EXITING | PIN_NMI_EXITING,
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");
/* Reset VM-Exit state */
g_guest_terminated = 0;
g_last_exit_reason = 0xFFFFFFFFULL;
/* Configure VMCS */
linux_vmcs_setup_guest_state(gi);
linux_vmcs_setup_controls(vmm_get_eptp());
linux_vmcs_setup_host_state();
/* 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);
return -3;
}
/* Guest terminated */
log_info("[LINUX] guest terminated");
log_hex64("[LINUX] last exit reason=", g_last_exit_reason);
log_hex64("[LINUX] vmexit count=", vmexit_get_count());
return 0;
}
+608
View File
@@ -0,0 +1,608 @@
/* 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;
}
/* ===== 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;
/* For now, no initrd (Phase 1.3+ will add it) */
g_guest.initrd_gpa = 0;
g_guest.initrd_size = 0;
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;
}
+125
View File
@@ -0,0 +1,125 @@
#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>
/* VM-Exit 处理器:从 VMCS 读取 exit reason 与 guest 状态,分发处理。
*
* Phase 1.1:处理 HLT(终止 guest)、EPT violation(动态映射)、异常。
* Phase 1.3:增加 VMCALL hypercall 处理(读 guest GPR、分发、写返回值)。
*/
extern void vmx_vmresume_wrapper(void);
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:Phase 1.1 自检场景下表示 guest 完成测试,停止 guest。 */
static int handle_hlt(u64 rip, u64 instr_len, int *out_resume) {
*out_resume = 0; /* 不 resume,guest 终止 */
log_info("[VMEXIT] HLT - guest halted");
(void)rip; (void)instr_len;
return 0;
}
/* 处理 EPT violation:动态映射缺失的 GPA。
* Phase 1.1:1:1 映射缺失的页面,权限 RWX。 */
static int handle_ept_violation(u64 qualification, int *out_resume) {
u64 gpa = vmx_vmcs_read(VMCS_GUEST_PHYSICAL_ADDR);
u64 page = gpa & ~(EPT_PAGE_SIZE - 1);
log_hex64("[VMEXIT] EPT violation gpa=", gpa);
(void)qualification;
/* 1:1 映射该页(假设 HPA=GPA) */
if (ept_identity_map(page, EPT_PAGE_SIZE, EPT_RWX) != 0) {
log_error("[VMEXIT] failed to map EPT page");
*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, &resume);
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:
/* 简化:让 guest 通过 CPUID,前进 RIP。Phase 1.2+ 完整模拟。 */
log_info("[VMEXIT] CPUID - skip");
vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len);
resume = 1;
break;
case EXIT_IO_INSTRUCTION:
/* 简化:跳过 IO 指令。Phase 1.4 virtio-console 完整处理。 */
vmx_vmcs_write(VMCS_GUEST_RIP, rip + instr_len);
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;
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)
+523
View File
@@ -0,0 +1,523 @@
#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;
/* 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(); }
+3 -1
View File
@@ -17,8 +17,10 @@ SECTIONS
*(.data*) *(.data*)
*(.data.rel.ro*) *(.data.rel.ro*)
*(COMMON) *(COMMON)
*(.bss*)
} :data } :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
.bss : ALIGN(4K) {
*(.bss*)
} :data
} }
+2236 -584
View File
@@ -1,20 +1,38 @@
/* Deshab Desktop Manager — 图形桌面环境 /* Deshab Desktop Manager — Winux-Kate 结构完整复刻
* *
* 功能:窗口管理、任务栏、桌面图标、事件路由、应用加载 * 布局结构与 D:\Code\Winux-Kate 完全一致:
* 风格:Sealed Arc 视觉设计语言 * app-shell
* 输入:PS/2 键盘 + PS/2 鼠标 * ├── topbar (34px):品牌 + 工作区切换 + 页面切换 + PG 时钟
* ├── page-host
* │ ├── 1·DASHBOARD:TERM-01 / TERM-02 / EDITOR / FILES 四面板 + StatusBar
* │ ├── 2·IDE:标签栏 + 编辑宿主
* │ ├── 3·IM:栏 + 分屏槽(未检测卡片)
* │ ├── 4·DESKTOP:图标网格 + 窗口 + 任务栏
* │ └── 5+/CUSTOM:外部 .elf 全屏宿主(双击启动)
* └── task-view overlay(工作区卡片)
*
* 新 UI 特性(Kate 之外):
* - RTC 实时时钟(秒级)+ 开机至今 UP 计时
* - VOL / BRIGHT 可拖滑块(BRIGHT 真实调暗全屏)
* - 开机打字机 Boot 屏
* - FILES 面板双击文件载入 EDITOR(FAT32 真实读写 + SAVE 回写)
* - 窗口单实例聚焦策略
*/ */
#include "../UTSM/include/utsm/dsk.h" #include "../UTSM/include/utsm/dsk.h"
/* block_read 函数类型(从 kernel_api + 0xA8 获取) */ /* block 设备函数类型(从 kernel_api + 0xA8 获取 block_api,read @ +0x10, write @ +0x18) */
typedef int (*desktop_block_read_fn)(void *ctx, unsigned long long lba, unsigned int count, void *buffer); 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) */ /* 先包含 ascii_bitmaps.c(定义 g_ascii),再包含 deshab_ui.h(引用 g_ascii) */
#include "../firstInit/ascii_bitmaps.c" #include "../firstInit/ascii_bitmaps.c"
#include "../UTSM/include/utsm/deshab_ui.h" #include "../UTSM/include/utsm/deshab_ui.h"
/* 用户态 FAT32 读写 */
#include "../tools/fat32_io.h"
typedef unsigned char u8; typedef unsigned char u8;
typedef signed char i8;
typedef unsigned short u16; typedef unsigned short u16;
typedef unsigned int u32; typedef unsigned int u32;
typedef unsigned long long u64; typedef unsigned long long u64;
@@ -32,24 +50,57 @@ static void sputc(char c) {
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } 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 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 void redraw_all(void);
static int ps2_mouse_poll(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 MAX_WINDOWS 8
#define TASKBAR_H 40 #define BORDER_W 1
#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 CURSOR_SIZE 24 #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 +115,7 @@ typedef enum {
typedef struct { typedef struct {
ev_type type; ev_type type;
int mx, my; /* 鼠标全局坐标 */ int mx, my;
int button; /* 0=左, 1=右 */ int button; /* 0=左, 1=右 */
u8 scancode; u8 scancode;
int shift, ctrl, alt; int shift, ctrl, alt;
@@ -81,18 +132,13 @@ typedef struct desktop_window {
int focused; int focused;
int minimized; int minimized;
char title[48]; char title[48];
int app_id; /* 关联应用 ID */ int app_id;
void *app_state; /* 应用私有状态 */ void *app_state;
int closing; /* 关闭中标记 */ int closing;
int dirty; /* 需要重绘 */ int dirty;
int ws; /* 所属工作区 */
} desktop_window; } desktop_window;
/* ============================================================
* 应用描述符
* ============================================================ */
/* 前向声明 */
struct desktop_window;
typedef struct desktop_window desktop_win; typedef struct desktop_window desktop_win;
typedef struct app_ctx { typedef struct app_ctx {
@@ -101,6 +147,7 @@ typedef struct app_ctx {
int client_x, client_y, client_w, client_h; int client_x, client_y, client_w, client_h;
int should_exit; int should_exit;
desktop_block_read_fn block_read; desktop_block_read_fn block_read;
desktop_block_write_fn block_write;
const void *kernel_api; const void *kernel_api;
} app_ctx; } app_ctx;
@@ -117,8 +164,39 @@ typedef struct {
app_event_fn on_event; app_event_fn on_event;
app_draw_fn on_draw; app_draw_fn on_draw;
app_destroy_fn on_destroy; app_destroy_fn on_destroy;
const char *elf_name;
} app_descriptor; } 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;
/* ============================================================ /* ============================================================
* 全局状态 * 全局状态
* ============================================================ */ * ============================================================ */
@@ -127,10 +205,13 @@ static du_context g_fb;
static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch; static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch;
static const void *g_kernel_api; static const void *g_kernel_api;
static desktop_block_read_fn g_block_read; 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_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 u8 g_mouse_buf[3];
static int g_mouse_idx = 0; static int g_mouse_idx = 0;
static int g_mouse_has_pkt = 0; static int g_mouse_has_pkt = 0;
@@ -160,17 +241,75 @@ static int g_drag_off_x, g_drag_off_y;
static app_descriptor g_apps[MAX_APPS]; static app_descriptor g_apps[MAX_APPS];
static int g_app_count = 0; 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 desktop_icon g_icons[MAX_ICONS];
static int g_icon_count = 0; static int g_icon_count = 0;
/* ---- Kate 外壳状态 ---- */
static int g_booted = 0;
static int g_page = 1; /* 1..4 内置, 5..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_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_im_toggle_rect[4]; /* IM bar 切换视图按钮 */
static int g_cp_host[4]; /* 自定义页宿主 */
/* ============================================================ /* ============================================================
* 箭头光标形状(24×24,1=前景 0=透明) * 箭头光标形状(24×24,1=前景 0=透明)
* ============================================================ */ * ============================================================ */
@@ -203,70 +342,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 draw_icon_terminal(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700; static void rtc_read(int *yy, int *mo, int *dd, int *hh, int *mi, int *ss) {
u32 fg = DP_SEAL_300; outb(0x70, 0x00); *ss = bcd2bin(inb(0x71));
u32 border = DP_SEAL_500; outb(0x70, 0x02); *mi = bcd2bin(inb(0x71));
/* 圆角矩形背景 */ outb(0x70, 0x04); *hh = bcd2bin(inb(0x71));
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6); outb(0x70, 0x07); *dd = bcd2bin(inb(0x71));
du_rect_outline(ctx, cx, cy, 32, 32, border, 6); outb(0x70, 0x08); *mo = bcd2bin(inb(0x71));
/* >_ 符号 */ outb(0x70, 0x09); *yy = bcd2bin(inb(0x71));
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);
} }
/* 绘制编辑器图标(32×32) */ static int rtc_day_sec(void) {
static void draw_icon_editor(du_context *ctx, int cx, int cy) { int y, mo, d, h, mi, s;
u32 bg = DP_ABYSS_700; rtc_read(&y, &mo, &d, &h, &mi, &s);
u32 fg = DP_SEAL_500; return h * 3600 + mi * 60 + s;
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); /* 格式化 HH:MM:SS */
/* 文本行 */ static void fmt_hms(char *out, int sec) {
for (int i = 0; i < 5; i++) { int h = sec / 3600, m = (sec / 60) % 60, s = sec % 60;
int lw = (i == 0) ? 20 : (i == 3 ? 12 : 16); out[0] = '0' + h / 10; out[1] = '0' + h % 10; out[2] = ':';
du_fill_rect(ctx, cx+6, cy+6+i*5, lw, 2, fg); 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);
}
}
} }
} }
/* 绘制文件夹图标(32×32) */ static void draw_icon_terminal(du_context *ctx, int cx, int cy) {
static void draw_icon_folder(du_context *ctx, int cx, int cy) { du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
u32 bg = DP_ARC_500; du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
u32 fg = DP_ARC_300; du_fill_rect(ctx, cx + 8, cy + 12, 2, 2, KS_ACCENT2);
u32 border = DP_ARC_700; du_fill_rect(ctx, cx + 10, cy + 14, 2, 2, KS_ACCENT2);
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6); du_fill_rect(ctx, cx + 12, cy + 16, 2, 2, KS_ACCENT2);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6); 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+4, cy+6, 12, 3, fg); du_fill_rect(ctx, cx + 16, cy + 22, 8, 2, KS_ACCENT);
/* 文件夹主体 */ }
du_fill_rect(ctx, cx+4, cy+12, 24, 2, border);
du_fill_rect(ctx, cx+4, cy+14, 24, 12, fg); static void draw_icon_editor(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 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);
}
}
static void draw_icon_folder(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_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) { static void draw_icon_calc(du_context *ctx, int cx, int cy) {
u32 bg = DP_ABYSS_700; du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_TERTIARY);
u32 fg = DP_WARNING; du_rect_outline(ctx, cx, cy, 40, 40, KS_ACCENT, 2);
u32 border = DP_WARNING; du_fill_rect(ctx, cx + 6, cy + 6, 28, 8, KS_BG_PRIMARY);
du_fill_rounded_rect(ctx, cx, cy, 32, 32, bg, 6); du_rect_outline(ctx, cx + 6, cy + 6, 28, 8, KS_ACCENT2, 1);
du_rect_outline(ctx, cx, cy, 32, 32, border, 6); du_fill_rect(ctx, cx + 28, cy + 8, 4, 4, KS_ACCENT2);
/* 屏幕 */
du_fill_rect(ctx, cx+4, cy+4, 24, 6, DP_ABYSS_900);
du_fill_rect(ctx, cx+22, cy+5, 4, 4, fg);
/* 按键网格 */
for (int r = 0; r < 3; r++) { for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) { 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,15 +505,13 @@ static void cursor_draw(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) { for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) { for (int c = 0; c < CURSOR_SIZE; c++) {
if (cursor_shape[r][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 块拆开,每块结束后轮询一次鼠标。
* 若鼠标有新数据包,立即擦除旧光标、在新位置保存背景并重绘光标。 */
static void anim_delay_poll_mouse(u32 ms) { static void anim_delay_poll_mouse(u32 ms) {
for (u32 i = 0; i < ms; i++) { for (u32 i = 0; i < ms; i++) {
for (volatile u32 j = 0; j < 50000; j++) { for (volatile u32 j = 0; j < 50000; j++) {
@@ -340,10 +537,10 @@ static desktop_window *win_find(int id) {
} }
static desktop_window *win_find_at(int mx, int my) { static desktop_window *win_find_at(int mx, int my) {
/* 从后往前搜索(后面的窗口在上面) */
for (int i = g_win_count - 1; i >= 0; i--) { for (int i = g_win_count - 1; i >= 0; i--) {
desktop_window *w = &g_windows[i]; desktop_window *w = &g_windows[i];
if (!w->visible || w->minimized) continue; 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) { if (mx >= w->x && mx < w->x + w->w && my >= w->y && my < w->y + w->h) {
return w; return w;
} }
@@ -364,6 +561,15 @@ static void win_bring_to_front(desktop_window *w) {
g_windows[g_win_count - 1] = tmp; 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) { 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; if (g_win_count >= MAX_WINDOWS) return 0;
desktop_window *win = &g_windows[g_win_count++]; desktop_window *win = &g_windows[g_win_count++];
@@ -377,10 +583,9 @@ static desktop_window *win_create(int app_id, const char *title, int x, int y, i
win->app_state = 0; win->app_state = 0;
win->closing = 0; win->closing = 0;
win->dirty = 1; win->dirty = 1;
/* 拷贝标题 */ win->ws = g_ws_cur;
for (int i = 0; i < 47 && title[i]; i++) win->title[i] = title[i]; for (int i = 0; i < 47 && title[i]; i++) win->title[i] = title[i];
win->title[47] = 0; win->title[47] = 0;
/* 取消其他窗口焦点 */
for (int i = 0; i < g_win_count - 1; i++) g_windows[i].focused = 0; for (int i = 0; i < g_win_count - 1; i++) g_windows[i].focused = 0;
g_focused_win = id; g_focused_win = id;
return win; return win;
@@ -393,10 +598,12 @@ static void win_destroy(desktop_window *w) {
} }
if (idx < 0) return; if (idx < 0) return;
/* Window close animation - shrinking rectangle */ /* 窗口关闭动画 — 收缩矩形 */
{ {
int wx = w->x, wy = w->y, ww = w->w, wh = w->h; int wx = w->x, wy = w->y, ww = w->w, wh = w->h;
int was_cur = (w->ws == g_ws_cur);
w->visible = 0; w->visible = 0;
if (was_cur) {
for (int step = 5; step >= 0; step--) { for (int step = 5; step >= 0; step--) {
int scale = (step + 1) * 100 / 6; int scale = (step + 1) * 100 / 6;
int aw = ww * scale / 100; int aw = ww * scale / 100;
@@ -405,188 +612,89 @@ static void win_destroy(desktop_window *w) {
int ay = wy + (wh - ah) / 2; int ay = wy + (wh - ah) / 2;
cursor_restore_bg(); cursor_restore_bg();
redraw_all(); redraw_all();
/* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */
cursor_restore_bg(); cursor_restore_bg();
du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_ACCENT, DU_RADIUS_SM); du_rect_outline(&g_fb, ax, ay, aw, ah, KS_ACCENT, DU_RADIUS_SM);
cursor_save_bg(g_mouse_x, g_mouse_y); cursor_save_bg(g_mouse_x, g_mouse_y);
cursor_draw(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y);
anim_delay_poll_mouse(16); 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) { 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); 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]; for (int i = idx; i < g_win_count - 1; i++) g_windows[i] = g_windows[i+1];
g_win_count--; g_win_count--;
if (g_focused_win == w->id) { if (g_focused_win == w->id) {
g_focused_win = g_win_count > 0 ? g_windows[g_win_count-1].id : -1; g_focused_win = -1;
if (g_focused_win >= 0) win_find(g_focused_win)->focused = 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) { static int win_hit_titlebar(desktop_window *w, int mx, int my) {
return mx >= w->x && mx < w->x + w->w && 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) { static int win_hit_close_btn(desktop_window *w, int mx, int my) {
int bx = w->x + w->w - 28; int bx = w->x + w->w - CLOSE_BTN_OFFSET;
int by = w->y + 4; int by = w->y + (KATE_TITLEBAR_H - CLOSE_BTN_SIZE) / 2;
return mx >= bx && mx < bx + 24 && my >= by && my < by + 20; 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) { static void draw_window_frame(desktop_window *w) {
if (!w->visible || w->minimized) return; if (!w->visible || w->minimized) return;
u32 titlebar_bg = w->focused ? DP_ABYSS_600 : DP_ABYSS_800; u32 border = w->focused ? KS_BORDER_FOCUS : KS_BORDER;
u32 border_col = w->focused ? DS_DARK_BORDER_FOCUS : DS_DARK_BORDER;
/* 发光效果(焦点窗口) */
if (w->focused) { if (w->focused) {
du_rect_glow(&g_fb, w->x-2, w->y-2, w->w+4, w->h+4, du_kate_glow_border(&g_fb, w->x, w->y, w->w, w->h, KS_ACCENT);
DS_DARK_BORDER_FOCUS, 10, 3);
} }
/* 窗口主体背景 */ du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h, KS_BG_SECONDARY, 2);
du_fill_rounded_rect(&g_fb, w->x, w->y, w->w, w->h,
DS_DARK_BG_SECONDARY, DU_RADIUS_LG);
/* 标题栏 */ du_fill_rect_gradient(&g_fb, w->x + 1, w->y + 1, w->w - 2, KATE_TITLEBAR_H - 1,
du_fill_rect(&g_fb, w->x + BORDER_W, w->y + BORDER_W, 0xFF0A3050u, KS_BG_SECONDARY);
w->w - 2*BORDER_W, TITLEBAR_H - BORDER_W, titlebar_bg); 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, du_draw_string(&g_fb, w->title,
w->x + (i64)DU_SPACE_SM, w->x + 14,
w->y + (TITLEBAR_H - (i64)DU_ASCII_CELL_H) / 2, w->y + (KATE_TITLEBAR_H - (i64)DU_ASCII_LINE_H) / 2 + 1,
DS_DARK_TEXT_PRIMARY, titlebar_bg, DU_ASCII_STEP); KS_ACCENT, 0xFF0A3050u, DU_ASCII_STEP);
/* 关闭按钮 */ int bx = w->x + w->w - CLOSE_BTN_OFFSET;
int bx = w->x + w->w - 28; int by = w->y + (KATE_TITLEBAR_H - CLOSE_BTN_SIZE) / 2;
int by = w->y + 4; du_rect_outline(&g_fb, bx, by, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE, KS_DANGER, 1);
du_fill_rounded_rect(&g_fb, bx, by, 24, 20, DP_ERROR, DU_RADIUS_SM); du_draw_string(&g_fb, "X", bx + 3, by - 1,
du_draw_string(&g_fb, "X", bx + 8, by + 1, DP_NEUTRAL_900, DP_ERROR, DU_ASCII_STEP); KS_DANGER, KS_BG_SECONDARY, DU_ASCII_STEP);
/* 边框 */ du_rect_outline(&g_fb, w->x, w->y, w->w, w->h, border, 2);
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);
}
} }
/* ============================================================ /* ============================================================
* PS/2 鼠标处理 * PS/2 鼠标处理
* ============================================================ */ * ============================================================ */
/* PS/2 控制器等待 — 输入缓冲区空(可写) */
static int ps2_wait_write(void) { static int ps2_wait_write(void) {
for (int t = 0; t < 200000; t++) { for (int t = 0; t < 200000; t++) {
if (!(inb(0x64) & 0x02)) return 0; if (!(inb(0x64) & 0x02)) return 0;
@@ -595,7 +703,6 @@ static int ps2_wait_write(void) {
return -1; return -1;
} }
/* PS/2 控制器等待 — 输出缓冲区满(可读) */
static int ps2_wait_read(void) { static int ps2_wait_read(void) {
for (int t = 0; t < 200000; t++) { for (int t = 0; t < 200000; t++) {
if (inb(0x64) & 0x01) return 0; if (inb(0x64) & 0x01) return 0;
@@ -604,7 +711,6 @@ static int ps2_wait_read(void) {
return -1; return -1;
} }
/* 排干输出缓冲区中所有残留数据(带超时) */
static void ps2_drain(void) { static void ps2_drain(void) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
if (!(inb(0x64) & 0x01)) break; if (!(inb(0x64) & 0x01)) break;
@@ -613,56 +719,45 @@ static void ps2_drain(void) {
} }
static void ps2_mouse_init(void) { static void ps2_mouse_init(void) {
/* 1. 刷新固件/上一阶段留下的残留数据 */
ps2_drain(); ps2_drain();
/* 2. 启用 AUX(鼠标)端口 */
ps2_wait_write(); ps2_wait_write();
outb(0x64, 0xA8); outb(0x64, 0xA8);
/* 3. 读取控制器配置字节 */
ps2_wait_write(); ps2_wait_write();
outb(0x64, 0x20); outb(0x64, 0x20);
ps2_wait_read(); ps2_wait_read();
u8 cfg = inb(0x60); u8 cfg = inb(0x60);
/* 4. 设置配置:bit5=0(启用 AUX 时钟), bit1=1(启用 AUX IRQ12), bit6=1(Set2→Set1 转换) */ cfg &= ~0x20;
cfg &= ~0x20; /* enable AUX clock */ cfg |= 0x02;
cfg |= 0x02; /* enable AUX IRQ12 */ cfg |= 0x40;
cfg |= 0x40; /* enable Set2→Set1 translation */
/* 5. 写回控制器配置 */
ps2_wait_write(); ps2_wait_write();
outb(0x64, 0x60); outb(0x64, 0x60);
ps2_wait_write(); ps2_wait_write();
outb(0x60, cfg); outb(0x60, cfg);
/* 6. 复位鼠标 (0xFF) — 响应为 ACK(0xFA) + 自检(0xAA) + 设备ID(0x00),共 3 字节 */
ps2_wait_write(); ps2_wait_write();
outb(0x64, 0xD4); outb(0x64, 0xD4);
ps2_wait_write(); ps2_wait_write();
outb(0x60, 0xFF); outb(0x60, 0xFF);
/* 排干所有复位响应字节(最多 8 字节,带超时) */
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if (ps2_wait_read() != 0) break; if (ps2_wait_read() != 0) break;
inb(0x60); inb(0x60);
} }
/* 7. 启用数据流模式 (0xF4) — 响应为 ACK(0xFA) */
ps2_wait_write(); ps2_wait_write();
outb(0x64, 0xD4); outb(0x64, 0xD4);
ps2_wait_write(); ps2_wait_write();
outb(0x60, 0xF4); outb(0x60, 0xF4);
/* 排干 ACK */
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (ps2_wait_read() != 0) break; if (ps2_wait_read() != 0) break;
inb(0x60); inb(0x60);
} }
/* 8. 最终刷新:丢弃任何杂散字节,确保数据包从干净状态开始 */
ps2_drain(); ps2_drain();
/* 重置鼠标数据包解析状态 */
g_mouse_idx = 0; g_mouse_idx = 0;
g_mouse_has_pkt = 0; g_mouse_has_pkt = 0;
@@ -672,20 +767,18 @@ static void ps2_mouse_init(void) {
static int ps2_mouse_poll(void) { static int ps2_mouse_poll(void) {
u8 st = inb(0x64); u8 st = inb(0x64);
if (!(st & 1)) return 0; if (!(st & 1)) return 0;
if (!(st & 0x20)) return 0; /* 不是鼠标数据(键盘数据留给键盘处理) */ if (!(st & 0x20)) return 0;
u8 data = inb(0x60); u8 data = inb(0x60);
g_mouse_buf[g_mouse_idx++] = data; g_mouse_buf[g_mouse_idx++] = data;
if (g_mouse_idx < 3) return 0; if (g_mouse_idx < 3) return 0;
g_mouse_idx = 0; g_mouse_idx = 0;
/* 检查 bit3 同步 */
if (!(g_mouse_buf[0] & 0x08)) return 0; if (!(g_mouse_buf[0] & 0x08)) return 0;
int dx = (int)(i8)g_mouse_buf[1]; int dx = (int)(i8)g_mouse_buf[1];
int dy = (int)(i8)g_mouse_buf[2]; int dy = (int)(i8)g_mouse_buf[2];
/* (i8) 转换已处理符号位,无需再检查 sign bits */ dy = -dy;
dy = -dy; /* PS/2 Y 向上为正,屏幕 Y 向下为正,需翻转 */
g_mouse_x += dx; g_mouse_x += dx;
g_mouse_y += dy; g_mouse_y += dy;
@@ -695,8 +788,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; 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; int old_btn = g_mouse_btn;
g_mouse_btn = (g_mouse_buf[0] & 0x01) ? 1 : 0; g_mouse_btn = g_mouse_buf[0] & 0x03;
/* Only signal redraw if something visual changed */ 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; if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0;
g_mouse_has_pkt = 1; g_mouse_has_pkt = 1;
return 1; return 1;
@@ -724,7 +819,7 @@ static char scan_to_ascii(u8 sc, int shift) {
} }
/* ============================================================ /* ============================================================
* 内置应用:终端 (Bash) * 终端 (Bash) — 面板/窗口共用核心
* ============================================================ */ * ============================================================ */
#define TERM_MAX_COLS 80 #define TERM_MAX_COLS 80
@@ -743,42 +838,24 @@ typedef struct {
static const char *BASH_PROMPT = "deshab# "; static const char *BASH_PROMPT = "deshab# ";
static void *bash_on_create(app_ctx *ctx) { static void bash_reset(bash_state *s, int w, int h) {
bash_state *s = (bash_state *)0x4000000; /* 固定地址分配,避免堆依赖 */
for (int i = 0; i < TERM_MAX_CHARS; i++) { 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->cols = (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->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->cols > TERM_MAX_COLS) s->cols = TERM_MAX_COLS;
if (s->rows > TERM_MAX_ROWS) s->rows = TERM_MAX_ROWS; 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->cur_col = 0; s->cur_row = 0;
s->input_len = 0; s->input_cursor = 0; s->input_len = 0; s->input_cursor = 0;
s->prompt_len = 0; s->prompt_len = kstrlen(BASH_PROMPT);
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;
} }
static void bash_putc(bash_state *s, char c, u32 color) { 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->cur_col = 0; s->cur_row++; return; }
if (s->cur_row >= s->rows) { if (s->cur_row >= s->rows) {
/* 滚动 */
for (int r = 1; r < s->rows; r++) { for (int r = 1; r < s->rows; r++) {
for (int c2 = 0; c2 < s->cols; c2++) { for (int c2 = 0; c2 < s->cols; c2++) {
s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2]; s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2];
@@ -802,66 +879,63 @@ static void bash_puts(bash_state *s, const char *str, u32 color) {
static void bash_execute(bash_state *s, const char *cmd) { static void bash_execute(bash_state *s, const char *cmd) {
while (*cmd == ' ') cmd++; while (*cmd == ' ') cmd++;
bash_puts(s, BASH_PROMPT, DS_DARK_PROMPT); bash_puts(s, BASH_PROMPT, KS_ACCENT2);
bash_puts(s, cmd, DS_DARK_TEXT_PRIMARY); bash_puts(s, cmd, KS_TEXT_PRIMARY);
bash_putc(s, '\n', 0); bash_putc(s, '\n', 0);
if (*cmd == 0) return; if (*cmd == 0) return;
if (cmd[0]=='h'&&cmd[1]=='e'&&cmd[2]=='l'&&cmd[3]=='p') { 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, " help clear echo version\n", KS_ACCENT2);
bash_puts(s, " uname date about reboot\n", DP_SUCCESS); bash_puts(s, " uname about reboot halt\n", KS_ACCENT2);
bash_puts(s, " halt pwd whoami id\n", DP_SUCCESS); 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') { } 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] = ' '; } for (int i = 0; i < TERM_MAX_CHARS; i++) { s->ch[i] = ' '; }
s->cur_col = 0; s->cur_row = 0; s->cur_col = 0; s->cur_row = 0;
} else if (cmd[0]=='v'&&cmd[1]=='e'&&cmd[2]=='r') { } 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') { } 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') { } 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, "Deshab OS - SAS-R0 Kernel\n", KS_ACCENT);
bash_puts(s, "Sealed Arc UI Design\n", DP_ARC_300); bash_puts(s, "Winux-Kate UI Structure\n", KS_ACCENT2);
} else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') { } else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') {
const char *arg = cmd + 4; const char *arg = cmd + 4;
while (*arg == ' ') arg++; while (*arg == ' ') arg++;
bash_puts(s, arg, DS_DARK_TEXT_PRIMARY); bash_puts(s, arg, KS_TEXT_PRIMARY);
bash_putc(s, '\n', 0); bash_putc(s, '\n', 0);
} else if (cmd[0]=='p'&&cmd[1]=='w'&&cmd[2]=='d') { } 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') { } 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') { } 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') { } 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); outb(0x64, 0xFE);
for(;;) __asm__("hlt"); for(;;) __asm__("hlt");
} else if (cmd[0]=='h'&&cmd[1]=='a'&&cmd[2]=='l'&&cmd[3]=='t') { } 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"); for(;;) __asm__("hlt");
} else { } else {
bash_puts(s, "unknown: ", DP_ERROR); bash_puts(s, "unknown: ", KS_DANGER);
bash_puts(s, cmd, DP_ERROR); bash_puts(s, cmd, KS_DANGER);
bash_putc(s, '\n', 0); bash_putc(s, '\n', 0);
} }
} }
static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) { static void bash_key(bash_state *s, u8 sc, int shift) {
bash_state *s = (bash_state *)state; if (sc == 0x1C) {
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
if (sc == 0x1C) { /* Enter */
s->input_buf[s->input_len] = 0; s->input_buf[s->input_len] = 0;
bash_execute(s, s->input_buf); bash_execute(s, s->input_buf);
s->input_len = 0; s->input_cursor = 0; s->input_len = 0; s->input_cursor = 0;
} else if (sc == 0x0E) { /* Backspace */ } else if (sc == 0x0E) {
if (s->input_cursor > 0) { if (s->input_cursor > 0) {
for (int i = s->input_cursor-1; i < s->input_len-1; i++) for (int i = s->input_cursor-1; i < s->input_len-1; i++)
s->input_buf[i] = s->input_buf[i+1]; s->input_buf[i] = s->input_buf[i+1];
s->input_len--; s->input_cursor--; s->input_len--; s->input_cursor--;
} }
} else { } 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) { if (c && c >= 32 && c <= 126 && s->input_len < 255) {
for (int i = s->input_len; i > s->input_cursor; i--) for (int i = s->input_len; i > s->input_cursor; i--)
s->input_buf[i] = s->input_buf[i-1]; s->input_buf[i] = s->input_buf[i-1];
@@ -869,55 +943,59 @@ static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
s->input_len++; s->input_len++;
} }
} }
ctx->win->dirty = 1;
} }
static void bash_on_draw(void *state, app_ctx *ctx) { /* 绘制终端到指定矩形区域(面板或窗口客户区) */
bash_state *s = (bash_state *)state; static void bash_draw_to(bash_state *s, int cx, int cy, int cw, int ch) {
int cx = ctx->client_x; du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_PRIMARY);
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
/* 背景 */
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_PRIMARY);
/* 渲染所有字符 */
for (int r = 0; r < s->rows; r++) { for (int r = 0; r < s->rows; r++) {
for (int c = 0; c < s->cols; c++) { for (int c = 0; c < s->cols; c++) {
int idx = r * TERM_MAX_COLS + c; int idx = r * TERM_MAX_COLS + c;
if (s->ch[idx] == ' ') continue; if (s->ch[idx] == ' ') continue;
du_draw_char(&g_fb, s->ch[idx], int px = cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP;
cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP, int py = cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H;
cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H, if (px + (int)DU_ASCII_CELL_W > cx + cw) continue;
s->fg[idx], DS_DARK_BG_PRIMARY); 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);
} }
} }
/* 绘制当前输入行 */
int input_y = cy + (int)DU_SPACE_SM + s->cur_row * (int)DU_ASCII_LINE_H; 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, du_draw_string(&g_fb, BASH_PROMPT,
cx + (int)DU_SPACE_SM, input_y, 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, du_draw_string(&g_fb, s->input_buf,
cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP, cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP,
input_y, 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; 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, 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; 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;
} }
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;
}
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);
}
static void bash_on_destroy(void *state) { (void)state; }
/* ============================================================ /* ============================================================
* 内置应用:文本编辑器 (Editor) * 文本编辑器 (Editor) — 面板/IDE/窗口共用核心
* ============================================================ */ * ============================================================ */
#define EDITOR_BUF_SIZE 65536 #define EDITOR_BUF_SIZE 65536
@@ -926,11 +1004,13 @@ typedef struct {
char text[EDITOR_BUF_SIZE]; char text[EDITOR_BUF_SIZE];
int text_len; int text_len;
int cursor_pos; int cursor_pos;
int scroll_y; /* 行偏移 */ int scroll_y;
int cursor_line; int cursor_line;
int cursor_col; int cursor_col;
int modified; int modified;
char filename[64]; int has_file; /* 关联了磁盘文件 */
char name11[11]; /* FAT32 8.3 名 */
char disp[13]; /* 显示名 */
} editor_state; } editor_state;
static void editor_recalc_cursor(editor_state *s) { static void editor_recalc_cursor(editor_state *s) {
@@ -950,37 +1030,20 @@ static int editor_line_start(editor_state *s, int line) {
return s->text_len; return s->text_len;
} }
static void *editor_on_create(app_ctx *ctx) { static void editor_reset(editor_state *s, const char *sample) {
editor_state *s = (editor_state *)0x5000000; /* 固定地址 */
s->text_len = 0; s->cursor_pos = 0; s->text_len = 0; s->cursor_pos = 0;
s->scroll_y = 0; s->modified = 0; s->scroll_y = 0; s->modified = 0;
s->filename[0] = 0; s->has_file = 0;
s->disp[0] = 0;
/* 默认内容 */
const char *sample = "# Welcome to Deshab Editor\n#\n\nStart typing here...\n";
while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) { while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) {
s->text[s->text_len++] = *sample++; s->text[s->text_len++] = *sample++;
} }
s->cursor_pos = s->text_len; s->cursor_pos = s->text_len;
editor_recalc_cursor(s); 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) { static void editor_key(editor_state *s, u8 sc, int shift) {
editor_state *s = (editor_state *)state; if (sc == 0x1C) {
if (ev->type != EV_KEY_DOWN) return;
u8 sc = ev->scancode;
int e0 = 0; /* TODO: 扩展键处理 */
if (sc == 0x1C) { /* Enter */
if (s->text_len < EDITOR_BUF_SIZE - 1) { if (s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--) for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1]; s->text[i] = s->text[i-1];
@@ -988,14 +1051,14 @@ static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
s->text_len++; s->cursor_pos++; s->text_len++; s->cursor_pos++;
s->modified = 1; s->modified = 1;
} }
} else if (sc == 0x0E) { /* Backspace */ } else if (sc == 0x0E) {
if (s->cursor_pos > 0) { if (s->cursor_pos > 0) {
for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++) for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++)
s->text[i] = s->text[i+1]; s->text[i] = s->text[i+1];
s->text_len--; s->cursor_pos--; s->text_len--; s->cursor_pos--;
s->modified = 1; s->modified = 1;
} }
} else if (sc == 0x48) { /* Up */ } else if (sc == 0x48) {
if (s->cursor_line > 0) { if (s->cursor_line > 0) {
int prev_start = editor_line_start(s, s->cursor_line - 1); int prev_start = editor_line_start(s, s->cursor_line - 1);
int col = s->cursor_col; int col = s->cursor_col;
@@ -1003,26 +1066,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++) for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++; s->cursor_pos++;
} }
} else if (sc == 0x50) { /* Down */ } else if (sc == 0x50) {
if (s->cursor_line < 999) {
int next_start = editor_line_start(s, s->cursor_line + 1); 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; int col = s->cursor_col;
s->cursor_pos = next_start; s->cursor_pos = next_start;
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++) for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
s->cursor_pos++; s->cursor_pos++;
} }
} else if (sc == 0x4B) { /* Left */ } else if (sc == 0x4B) {
if (s->cursor_pos > 0) s->cursor_pos--; 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++; 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); 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); s->cursor_pos = editor_line_start(s, s->cursor_line);
while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n') while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n')
s->cursor_pos++; s->cursor_pos++;
} else { } 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) { if (c && c >= 32 && c <= 126 && s->text_len < EDITOR_BUF_SIZE - 1) {
for (int i = s->text_len; i > s->cursor_pos; i--) for (int i = s->text_len; i > s->cursor_pos; i--)
s->text[i] = s->text[i-1]; s->text[i] = s->text[i-1];
@@ -1032,31 +1095,23 @@ static void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
} }
} }
editor_recalc_cursor(s); 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; static void editor_draw_to(editor_state *s, int cx, int cy, int cw, int ch) {
int cx = ctx->client_x;
int cy = ctx->client_y;
int cw = ctx->client_w;
int ch = ctx->client_h;
int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM; int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM;
int text_area_x = cx + line_num_w; int text_area_x = cx + line_num_w;
int text_area_w = cw - 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; 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, KS_BG_SECONDARY);
du_fill_rect(&g_fb, cx, cy, cw, ch, DS_DARK_BG_SECONDARY);
/* 自动滚动 */
if (s->cursor_line < s->scroll_y) s->scroll_y = s->cursor_line; 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; if (s->cursor_line >= s->scroll_y + visible_rows) s->scroll_y = s->cursor_line - visible_rows + 1;
/* 渲染可见行 */
int line = 0; int line = 0;
int pos = 0; int pos = 0;
for (int i = 0; i < s->text_len || line <= s->cursor_line; ) { for (int i = 0; i < s->text_len || line <= s->cursor_line; ) {
@@ -1065,26 +1120,23 @@ static void editor_on_draw(void *state, app_ctx *ctx) {
if (line >= s->scroll_y) { if (line >= s->scroll_y) {
int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H; int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H;
/* 行号 */
char ln[8]; char ln[8];
ln[0] = '0' + ((line+1)/100)%10; ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10; ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10; ln[2] = '0' + (line+1)%10;
ln[3] = 0; ln[3] = 0;
du_draw_string(&g_fb, ln, cx + 2, screen_y, 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, 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; int col = 0;
while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) { while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) {
du_draw_char(&g_fb, s->text[pos], du_draw_char(&g_fb, s->text[pos],
text_area_x + col * (int)DU_ASCII_STEP, text_area_x + col * (int)DU_ASCII_STEP,
screen_y, screen_y,
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_SECONDARY); KS_TEXT_PRIMARY, KS_BG_SECONDARY);
col++; pos++; col++; pos++;
} }
if (pos < s->text_len && s->text[pos] == '\n') pos++; if (pos < s->text_len && s->text[pos] == '\n') pos++;
@@ -1096,19 +1148,19 @@ static void editor_on_draw(void *state, app_ctx *ctx) {
if (pos >= s->text_len && line > s->cursor_line) break; 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) { 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_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; 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; 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]; char status[64];
int sp = 0; int sp = 0;
/* 行:列 */
const char *p1 = "L:"; while(*p1) status[sp++] = *p1++; 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%10;
status[sp++] = '0' + (s->cursor_line+1)%10; status[sp++] = '0' + (s->cursor_line+1)%10;
@@ -1116,15 +1168,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%10;
status[sp++] = '0' + (s->cursor_col+1)%10; status[sp++] = '0' + (s->cursor_col+1)%10;
if (s->modified) { const char *m = " *"; while(*m) status[sp++] = *m++; } 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; status[sp] = 0;
du_draw_string(&g_fb, status, cx + 4, status_y + 1, 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; } static void editor_on_destroy(void *state) { (void)state; }
/* ============================================================ /* ============================================================
* 内置应用:计算器 (Calculator) * 计算器 (Calculator)
* ============================================================ */ * ============================================================ */
typedef struct { typedef struct {
@@ -1132,7 +1206,7 @@ typedef struct {
int display_len; int display_len;
i64 accumulator; i64 accumulator;
i64 current; i64 current;
int op; /* 0=none, 1=+, 2=-, 3=*, 4=/ */ int op;
int new_number; int new_number;
} calc_state; } calc_state;
@@ -1150,7 +1224,7 @@ static void calc_update_display(calc_state *s) {
} }
static void *calc_on_create(app_ctx *ctx) { 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->accumulator = 0; s->current = 0;
s->op = 0; s->new_number = 1; s->op = 0; s->new_number = 1;
calc_update_display(s); calc_update_display(s);
@@ -1192,20 +1266,17 @@ static void calc_on_draw(void *state, app_ctx *ctx) {
int cw = ctx->client_w; int cw = ctx->client_w;
int ch = ctx->client_h; 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; int disp_h = 40;
du_fill_rounded_rect(&g_fb, cx + 8, cy + 8, cw - 16, disp_h, 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, 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; 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, 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[] = { const char *btn_labels[] = {
"C", "+/-", "%", "/", "C", "+/-", "%", "/",
"7", "8", "9", "*", "7", "8", "9", "*",
@@ -1221,15 +1292,17 @@ static void calc_on_draw(void *state, app_ctx *ctx) {
int bx = cx + 8 + col * (btn_w + 3); int bx = cx + 8 + col * (btn_w + 3);
int by = btn_y + row * (btn_h + 3); int by = btn_y + row * (btn_h + 3);
if (by + btn_h > cy + ch - 4) break; if (by + btn_h > cy + ch - 4) break;
u32 bg = (i < 4) ? DP_ABYSS_600 : u32 bg = (i < 4) ? KS_ACCENT_DIM :
(i % 4 == 3) ? DS_DARK_ACCENT : (i % 4 == 3) ? KS_ACCENT :
(i == 18) ? DP_SUCCESS : (i == 18) ? KS_ACCENT2 :
DP_ABYSS_700; 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_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], du_draw_string(&g_fb, btn_labels[i],
bx + (btn_w - 1*(int)DU_ASCII_STEP)/2, bx + (btn_w - 1*(int)DU_ASCII_STEP)/2,
by + (btn_h - (int)DU_ASCII_CELL_H)/2, by + (btn_h - (int)DU_ASCII_CELL_H)/2,
DS_DARK_TEXT_PRIMARY, bg, DU_ASCII_STEP); fg, bg, DU_ASCII_STEP);
} }
} }
@@ -1240,84 +1313,204 @@ static void calc_on_destroy(void *state) { (void)state; }
* ============================================================ */ * ============================================================ */
static void register_apps(void) { static void register_apps(void) {
/* 0: Terminal */
g_apps[0] = (app_descriptor){ g_apps[0] = (app_descriptor){
"bash", "Terminal", 640, 440, "bash", "Terminal", 640, 440,
bash_on_create, bash_on_event, bash_on_draw, bash_on_destroy bash_on_create, bash_on_event, bash_on_draw, bash_on_destroy, 0
}; };
g_app_count++; g_app_count++;
/* 1: Editor */
g_apps[1] = (app_descriptor){ g_apps[1] = (app_descriptor){
"editor", "Editor", 600, 450, "editor", "Editor", 600, 450,
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy, 0
}; };
g_app_count++; g_app_count++;
/* 2: File Manager */
g_apps[2] = (app_descriptor){ g_apps[2] = (app_descriptor){
"fileman", "Files", 500, 400, "fileman", "Files", 500, 400,
0, 0, 0, 0 /* placeholder */ 0, 0, 0, 0, "FILEMAN ELF"
}; };
g_app_count++; g_app_count++;
/* 3: Calculator */
g_apps[3] = (app_descriptor){ g_apps[3] = (app_descriptor){
"calc", "Calculator", 280, 400, "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[4] = (app_descriptor){
"browser", "Browser", 700, 500,
0, 0, 0, 0, "BROWSER ELF"
}; };
g_app_count++; g_app_count++;
} }
/* Winux-Kate .desktop-grid:canvas padding 24px,列宽 104px,gap 18px,
* 左→右排列,超出画布宽度则换行(align-content: start) */
static void setup_desktop_icons(void) { static void setup_desktop_icons(void) {
int start_x = (int)g_fb_w - DESKTOP_ICON_W - ICON_GUTTER_X; int pad = KATE_ICON_PAD; /* 24 */
int y = ICON_GUTTER_Y; 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++) { 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;
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].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].y = y;
g_icons[g_icon_count].label = g_apps[i].display_name; g_icons[g_icon_count].label = g_apps[i].display_name;
g_icons[g_icon_count].selected = 0;
g_icon_count++; 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) { static void launch_app(int app_id) {
if (app_id < 0 || app_id >= g_app_count) return; if (app_id < 0 || app_id >= g_app_count) return;
app_descriptor *app = &g_apps[app_id]; app_descriptor *app = &g_apps[app_id];
if (app->elf_name) {
launch_external_elf(app->elf_name);
return;
}
if (!app->on_create) return; if (!app->on_create) return;
/* 计算窗口位置(居中偏移) */ /* 单实例:已有同类窗口则聚焦并迁移到当前工作区 */
int wx = 80 + g_win_count * 30; for (int i = 0; i < g_win_count; i++) {
int wy = 40 + g_win_count * 30; if (g_windows[i].app_id == app_id && g_windows[i].visible) {
if (wx + (int)app->default_w > (int)g_fb_w - 20) wx = 80; g_windows[i].ws = g_ws_cur;
if (wy + (int)app->default_h > (int)g_fb_h - TASKBAR_H - 20) wy = 40; 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, desktop_window *win = win_create(app_id, app->display_name,
wx, wy, (int)app->default_w, (int)app->default_h); wx, wy, (int)app->default_w, (int)app->default_h);
if (!win) return; if (!win) return;
/* 构建应用上下文 */
app_ctx ctx; app_ctx ctx;
ctx.fb = &g_fb; fill_app_ctx(&ctx, win);
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;
win->app_state = app->on_create(&ctx); win->app_state = app->on_create(&ctx);
win->dirty = 1; win->dirty = 1;
/* Window open animation - expanding rectangle */ /* 窗口打开动画 — 展开矩形 */
for (int step = 0; step < 6; step++) { for (int step = 0; step < 6; step++) {
int scale = (step + 1) * 100 / 6; int scale = (step + 1) * 100 / 6;
int aw = win->w * scale / 100; int aw = win->w * scale / 100;
@@ -1326,9 +1519,8 @@ static void launch_app(int app_id) {
int ay = win->y + (win->h - ah) / 2; int ay = win->y + (win->h - ah) / 2;
cursor_restore_bg(); cursor_restore_bg();
redraw_all(); redraw_all();
/* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */
cursor_restore_bg(); cursor_restore_bg();
du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_ACCENT, DU_RADIUS_SM); du_rect_outline(&g_fb, ax, ay, aw, ah, KS_ACCENT, DU_RADIUS_SM);
cursor_save_bg(g_mouse_x, g_mouse_y); cursor_save_bg(g_mouse_x, g_mouse_y);
cursor_draw(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y);
anim_delay_poll_mouse(16); anim_delay_poll_mouse(16);
@@ -1338,46 +1530,1104 @@ static void launch_app(int app_id) {
} }
/* ============================================================ /* ============================================================
* 全屏重绘 * FILES 面板(FAT32 真实读写)
* ============================================================ */ * ============================================================ */
static void redraw_all(void) { static int files_list_cb(const char *name, u32 size, u8 attr, void *ud) {
/* 渐变背景 */ (void)ud;
du_fill_bg_gradient(&g_fb, DP_ABYSS_900, DP_ABYSS_800); if (attr & 0x10) return 0; /* 跳过子目录 */
if (g_file_count >= MAX_FILES) return 1; /* 停止 */
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 / im-slot / 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);
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);
busy_delay(8000000);
}
du_divider_h(&g_fb, W / 2 - 120, ty + 30, 240, KS_ACCENT);
/* boot-subtitle(对应 Kate "由 Deaicup 工作室制作") */
draw_centered("DEAICUP STUDIO", W / 2, ty + 44, KS_ACCENT2, 0);
busy_delay(10000000);
/* 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);
busy_delay(500000);
}
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);
}
/* boot-hint + credit(对应 Kate "SYSTEM ONLINE" + "© 2026 Deaicup Studio") */
draw_centered("SYSTEM ONLINE", W / 2, H - 64, KS_ACCENT2, 0);
draw_centered("(C) 2026 DEAICUP STUDIO", W / 2, H - 40, KS_TEXT_DIM, 0);
busy_delay(40000000);
}
/* ============================================================
* 顶栏(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[4] = { "1.DASHBOARD", "2.IDE", "3.IM", "4.DESKTOP" };
for (int i = 0; i < 4; i++) {
int bw = kstrlen(pg_names[i]) * (int)DU_ASCII_STEP + 16;
int active = (g_page == i + 1);
int hover = g_mouse_x >= x && g_mouse_x < x + bw &&
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 + 8, 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 + 6;
}
/* 自定义页 */
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[4 + i][0] = x; g_pg_rect[4 + i][1] = 5;
g_pg_rect[4 + i][2] = bw; g_pg_rect[4 + i][3] = 24;
g_pg_rm_rect[4 + i][0] = x + bw - 20; g_pg_rm_rect[4 + i][1] = 5;
g_pg_rm_rect[4 + i][2] = 20; g_pg_rm_rect[4 + i][3] = 24;
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 . Ctrl+Shift+Tab CTX";
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 * 42 / 100;
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:/ROOT (", 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·IM ---- */
static int g_im_view = 0; /* 0=split(WECHAT/QQ) 1=full(WECOM),对应 Kate imView */
/* Kate .missing-card:h2 + p + a.btn 下载按钮 */
static void draw_missing_card(int sx, int sy, int sw, int sh,
const char *title, const char *desc,
const char *btn) {
int cw = 340, ch = 150;
int cx = sx + (sw - cw) / 2, cy = sy + (sh - ch) / 2;
du_kate_panel(&g_fb, cx, cy, cw, ch, 0);
draw_centered(title, sx + sw / 2, cy + 28, KS_ACCENT, 0);
draw_centered(desc, sx + sw / 2, cy + 56, KS_TEXT_DIM, 0);
/* .btn 下载按钮(视觉复刻,Deshab 无网络安装渠道) */
int bw = kstrlen(btn) * (int)DU_ASCII_STEP + 20;
int bx = sx + sw / 2 - bw / 2, by = cy + 88;
int hover = g_mouse_x >= bx && g_mouse_x < bx + bw &&
g_mouse_y >= by && g_mouse_y < by + 22;
if (hover) du_fill_rect(&g_fb, bx, by, bw, 22, KS_ACCENT_DIM);
du_rect_outline(&g_fb, bx, by, bw, 22, KS_BORDER, 1);
du_draw_string(&g_fb, btn, bx + 10, by + 3, KS_ACCENT, 0, DU_ASCII_STEP);
}
static void draw_im(void) {
int W = (int)g_fb_w;
int top = KATE_TOPBAR_H;
/* im-bar(36px):dot + 视图标签 + page-credit + 切换按钮(margin-left:auto) */
du_fill_rect(&g_fb, 0, top, W, 36, 0xFF051828u);
du_divider_h(&g_fb, 0, top + 36, W, KS_BORDER);
int ty = top + (36 - (int)DU_ASCII_LINE_H) / 2 + 1;
/* .dot(8×8 accent2 发光点) */
du_fill_rect(&g_fb, 12, top + 14, 8, 8, KS_ACCENT2);
du_draw_string(&g_fb,
g_im_view ? "WECOM FULLSCREEN" : "WECHAT / QQ SPLIT",
28, ty, KS_ACCENT, 0, DU_ASCII_STEP);
/* page-credit(对应 Kate "由 Deaicup 工作室制作") */
du_draw_string(&g_fb, "DEAICUP STUDIO",
28 + (g_im_view ? 16 : 17) * (int)DU_ASCII_STEP + 12,
ty + 1, KS_TEXT_DIM, 0, DU_ASCII_STEP);
/* 切换按钮(对应 Kate im_toggle,Ctrl+Shift+Tab) */
{
const char *tb = "CTRL+SHIFT+TAB SWITCH";
int bw = kstrlen(tb) * (int)DU_ASCII_STEP + 20;
int bx = W - 12 - bw;
g_im_toggle_rect[0] = bx; g_im_toggle_rect[1] = top + 7;
g_im_toggle_rect[2] = bw; g_im_toggle_rect[3] = 22;
int hover = g_mouse_x >= bx && g_mouse_x < bx + bw &&
g_mouse_y >= top + 7 && g_mouse_y < top + 29;
if (hover) du_fill_rect(&g_fb, bx, top + 7, bw, 22, KS_ACCENT_DIM);
du_rect_outline(&g_fb, bx, top + 7, bw, 22, KS_BORDER, 1);
du_draw_string(&g_fb, tb, bx + 10, ty, KS_ACCENT, 0, DU_ASCII_STEP);
}
if (g_im_view == 0) {
/* im-split 双槽(grid 1fr 1fr, gap/padding 8px) */
int pad = 8, gap = 8;
int hy = top + 36 + pad;
int hh = (int)g_fb_h - hy - pad;
int sw = (W - 2 * pad - gap) / 2;
for (int i = 0; i < 2; i++) {
int sx = pad + i * (sw + gap);
draw_dashed_rect(sx, hy, sw, hh, KS_BORDER);
}
draw_missing_card(pad, hy, sw, hh,
"WECHAT NOT DETECTED",
"IM MODULE NOT AVAILABLE IN DESHAB",
"DOWNLOAD WECHAT");
draw_missing_card(pad + sw + gap, hy, sw, hh,
"QQ NOT DETECTED",
"IM MODULE NOT AVAILABLE IN DESHAB",
"DOWNLOAD QQ");
} else {
/* im-full 单槽(margin 8px) */
int hx = 8, hy = top + 36 + 8;
int hw = W - 16, hh = (int)g_fb_h - hy - 8;
draw_dashed_rect(hx, hy, hw, hh, KS_BORDER);
draw_missing_card(hx, hy, hw, hh,
"WECOM NOT DETECTED",
"IM MODULE NOT AVAILABLE IN DESHAB",
"DOWNLOAD WECOM");
}
}
/* ---- 4·DESKTOP ---- */
static void draw_desktop_page(void) {
/* 图标网格 */
draw_desktop_icons(); draw_desktop_icons();
/* 窗口(从底到顶) */ /* 窗口(当前工作区,从底到顶) */
for (int i = 0; i < g_win_count; i++) { for (int i = 0; i < g_win_count; i++) {
desktop_window *w = &g_windows[i]; desktop_window *w = &g_windows[i];
if (w->minimized || !w->visible) continue; if (w->minimized || !w->visible) continue;
if (w->ws != g_ws_cur) continue;
/* 窗口装饰(先画框架作为窗口背景:主体、标题栏、边框) */
draw_window_frame(w); draw_window_frame(w);
/* 窗口客户区背景 */ if (w->app_id >= 0 && w->app_id < g_app_count &&
int cx = w->x + BORDER_W; g_apps[w->app_id].on_draw && w->app_state) {
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; app_ctx ctx;
ctx.fb = &g_fb; fill_app_ctx(&ctx, w);
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); 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) */
draw_taskbar(); 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) {
if (g_bright >= 100) return;
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, KS_BG_PRIMARY, KS_BG_TERTIARY);
/* 顶栏 */
draw_topbar();
/* 页面 */
if (g_page == 1) draw_dashboard();
else if (g_page == 2) draw_ide();
else if (g_page == 3) draw_im();
else if (g_page == 4) draw_desktop_page();
else {
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;
}
/* 任务视图 overlay */
if (g_taskview) draw_taskview();
/* 右键菜单(最顶) */
if (g_ctx_open) draw_ctx_menu();
/* 亮度调整 */
apply_brightness();
/* 鼠标光标 */ /* 鼠标光标 */
cursor_save_bg(g_mouse_x, g_mouse_y); cursor_save_bg(g_mouse_x, g_mouse_y);
@@ -1385,7 +2635,439 @@ 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 > 4 + 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 = 5 + 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 = 5 + 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 < 4 + g_cpage_count; i++) {
if (i >= 4 && rect_hit(g_pg_rm_rect[i], mx, my)) {
cpage_remove(i - 4);
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) {
/* IM 切换视图按钮(对应 Kate im_toggle) */
if (rect_hit(g_im_toggle_rect, mx, my)) { g_im_view ^= 1; return 1; }
return 0;
}
if (g_page == 4) return desktop_click(mx, my, dbl);
if (g_page >= 5) {
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 != 4) 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 上下文菜单(IM 页为切换视图,对应 Kate imView) */
if (g_ctrl && sc == 0x0F) {
if (g_shift) {
if (g_page == 3) { g_im_view ^= 1; return 1; }
/* Ctrl+Shift+Tab:对当前聚焦窗口打开"移到工作区"菜单 */
if (g_page == 4 && win_find(g_focused_win)) {
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 = 4 + 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 == 4) {
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"))) __attribute__((visibility("default")))
@@ -1395,167 +3077,134 @@ void dsk_entry(const dsk_boot_context *ctx) {
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
slog("bad context"); slog("bad context");
for(;;) __asm__("hlt"); for (;;) __asm__("hlt");
} }
g_boot_ctx = ctx;
g_fb_addr = ctx->framebuffer_address; g_fb_addr = ctx->framebuffer_address;
g_fb_w = ctx->framebuffer_width; g_fb_w = ctx->framebuffer_width;
g_fb_h = ctx->framebuffer_height; g_fb_h = ctx->framebuffer_height;
g_fb_pitch = ctx->framebuffer_pitch; g_fb_pitch = ctx->framebuffer_pitch;
g_kernel_api = (const void *)ctx->dkm_kernel_api; 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); du_context_init(&g_fb, g_fb_addr, g_fb_w, g_fb_h, g_fb_pitch);
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(); ps2_mouse_init();
/* 注册应用 */
register_apps(); register_apps();
setup_desktop_icons(); 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(); redraw_all();
slog("desktop ready"); slog("desktop ready");
/* 主事件循环 */ /* 主事件循环 */
for (;;) { while (!g_quit) {
int need_redraw = 0; int need_redraw = 0;
g_mouse_has_pkt = 0; int moved = 0;
/* 轮询 PS/2 鼠标 */ /* 鼠标:排空所有数据包 */
if (ps2_mouse_poll()) { while (ps2_mouse_poll()) moved = 1;
/* 拖拽处理 - 需要全屏重绘 */
if (g_dragging && g_drag_win >= 0) { 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); desktop_window *w = win_find(g_drag_win);
if (w) { if (w) {
w->x = g_mouse_x - g_drag_off_x; w->x = g_mouse_x - g_drag_off_x;
w->y = g_mouse_y - g_drag_off_y; 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; need_redraw = 1;
} }
/* 鼠标点击事件 - 需要全屏重绘 */ /* 滑块拖拽 */
if (g_mouse_btn) { 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; }
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 (win_hit_close_btn(hit, g_mouse_x, g_mouse_y)) { for (;;) {
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 键盘 */
{
u8 st = inb(0x64); u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) { if (!(st & 1) || (st & 0x20)) break;
u8 data = inb(0x60); u8 sc = inb(0x60);
u8 sc = data; if (handle_key(sc)) need_redraw = 1;
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; }
/* 发送键盘事件到焦点窗口 */ /* RTC 秒 tick:DASHBOARD 状态栏时钟/UP 计时刷新 */
if (g_focused_win >= 0) { {
desktop_window *fw = win_find(g_focused_win); int sec = rtc_day_sec();
if (fw && fw->app_id >= 0 && g_apps[fw->app_id].on_event && fw->app_state) { if (sec != g_last_sec) {
desktop_event dev; g_last_sec = sec;
dev.type = EV_KEY_DOWN; if (g_page == 1 && !g_taskview) need_redraw = 1;
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;
} }
} }
g_e0 = 0;
}
}
next:
if (need_redraw) { if (need_redraw) {
cursor_restore_bg(); cursor_restore_bg();
redraw_all(); redraw_all();
} else if (g_mouse_has_pkt) { } else if (moved) {
/* 鼠标移动但无需全屏重绘:只更新光标位置 */ /* 仅光标移动:局部更新 */
cursor_restore_bg(); cursor_restore_bg();
cursor_save_bg(g_mouse_x, g_mouse_y); cursor_save_bg(g_mouse_x, g_mouse_y);
cursor_draw(g_mouse_x, g_mouse_y); cursor_draw(g_mouse_x, g_mouse_y);
@@ -1563,4 +3212,7 @@ next:
__asm__("pause"); __asm__("pause");
} }
slog("exit");
/* 返回到 DSK */
} }
+3 -1
View File
@@ -17,8 +17,10 @@ SECTIONS
*(.data*) *(.data*)
*(.data.rel.ro*) *(.data.rel.ro*)
*(COMMON) *(COMMON)
*(.bss*)
} :data } :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
.bss : ALIGN(4K) {
*(.bss*)
} :data
} }
+2 -2
View File
@@ -66,7 +66,7 @@ static block_write_fn g_block_write;
static u64 g_tsc_per_ms = 0; static u64 g_tsc_per_ms = 0;
static u8 g_disk[131072]; static u8 g_disk[131072];
static u8 g_cluster[4096]; 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_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; } static int fat32_write_sectors(u32 lba, u32 count, const u8 *buf) { return g_block_write ? g_block_write(0,lba,count,buf) : -1; }
@@ -529,7 +529,7 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) {
if (!lc || min_vaddr == ~0ULL) return -4; if (!lc || min_vaddr == ~0ULL) return -4;
u64 isize = max_vaddr - min_vaddr; u64 isize = max_vaddr - min_vaddr;
isize = (isize + 0xFFF) & ~0xFFFULL; isize = (isize + 0xFFF) & ~0xFFFULL;
/* 加载缓冲:需容纳 shell.elf(~536KB,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */ /* 加载缓冲:需容纳 shell.elf(~862KB memsz,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */
static u8 ibuf[1048576]; static u8 ibuf[1048576];
u8 *image = ibuf; u8 *image = ibuf;
if (isize > 1048576) return -5; /* 镜像过大,防止越界 */ if (isize > 1048576) return -5; /* 镜像过大,防止越界 */
BIN
View File
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)
```
+134
View File
@@ -0,0 +1,134 @@
#!/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..."
git clone --depth 1 --branch v6.6 \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/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..."
# Create a minimal initramfs with busybox or a simple init
INITRAMFS_TMP="$SCRIPT_DIR/initramfs.tmp"
rm -rf "$INITRAMFS_TMP"
mkdir -p "$INITRAMFS_TMP"
# Copy initramfs contents
cp -a "$INITRAMFS_DIR"/* "$INITRAMFS_TMP"/
# 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"
echo "[utsm-linux] Console ready on /dev/hvc0 (virtio-console)"
exec /bin/sh
INITEOF
chmod +x "$INITRAMFS_TMP/init"
fi
# Pack initramfs
cd "$INITRAMFS_TMP"
find . | cpio -H newc -o | gzip -9 > "$OUTPUT_DIR/linux-initrd.img"
echo "[build] initrd: $OUTPUT_DIR/linux-initrd.img ($(du -h $OUTPUT_DIR/linux-initrd.img | cut -f1))"
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."
+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
+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
+369
View File
@@ -0,0 +1,369 @@
// 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>
/* Shared IPC protocol header (included via build.sh -I flag) */
#include <ipc_proto.h>
#define UTSM_DEV_NAME "utsm"
/* ===== 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;
}
static const struct file_operations utsm_fops = {
.owner = THIS_MODULE,
.write = utsm_dev_write,
.read = utsm_dev_read,
};
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 := ../.. ROOT := ../..
OUT := $(ROOT)/SYSTEM/system/user/use/login.elf 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 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 LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
@@ -14,9 +15,12 @@ OBJECTS := $(C_SOURCES:.c=.o)
all: $(OUT) all: $(OUT)
# 构建后用 patch_login_hash.ps1 计算 .text SHA256 并写入 .hashseg
$(OUT): $(OBJECTS) linker.ld $(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use" @if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS) $(LD) $(LDFLAGS) -o $@ $(OBJECTS)
@echo [login] patching .text SHA256 into .hashseg...
powershell -NoProfile -ExecutionPolicy Bypass -File $(PATCHER) $@
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
+16
View File
@@ -11,12 +11,28 @@ PHDRS
SECTIONS SECTIONS
{ {
. = 0x1000; . = 0x1000;
/* 防篡改:导出 .text 段范围供运行时自校验 */
__text_start = .;
.text : ALIGN(4K) { *(.text*) } :text .text : ALIGN(4K) { *(.text*) } :text
__text_end = .;
.rodata : ALIGN(4K) { *(.rodata*) } :rodata .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 : ALIGN(4K) {
*(.data*) *(.data*)
*(.data.rel.ro*) *(.data.rel.ro*)
*(COMMON) *(COMMON)
} :data
.bss : ALIGN(4K) {
*(.bss*) *(.bss*)
} :data } :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
+81
View File
@@ -4,12 +4,22 @@
* extracts username + password hash, prompts for password, * extracts username + password hash, prompts for password,
* verifies via SHA256 comparison, sets ctx->reserved[2]=1 on success. * 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). * Returns to DSK in all cases (success, skip via Esc, or fatal error).
* DSK loads desktop.elf after this returns. * DSK loads desktop.elf after this returns.
*/ */
#include "../UTSM/include/utsm/dsk.h" #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 signed char i8;
typedef unsigned char u8; typedef unsigned char u8;
typedef unsigned short u16; typedef unsigned short u16;
@@ -253,6 +263,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];} 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 ---- /* ---- conf parsing ----
* USER.CONF format (built by FirstInit's build_user_conf, but stored unencrypted * USER.CONF format (built by FirstInit's build_user_conf, but stored unencrypted
* on disk for login.elf to parse): * on disk for login.elf to parse):
@@ -404,6 +477,14 @@ __attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) { void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */ __asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[login] boot"); 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"); logl("[login] calibrating TSC");
tsc_calibrate_fi(); tsc_calibrate_fi();
+298 -59
View File
@@ -1,22 +1,170 @@
/* Deshab Browser — 全屏浏览器 (Stub) /* Deshab Browser — 全屏浏览器
* 地址栏 + 导航按钮 + 内容渲染区域。 * 接入 net_stack.h 共享协议栈,实现 HTTP GET 与简单内容显示。
* 当前显示 stub 提示,Esc 退出。 * 地址栏输入 URL(如 http://10.0.2.2:8000/ 或 10.0.2.2),
* Enter 发起请求,内容区显示 HTTP 响应(分离 header/body,移除 HTML 标签)。
* Esc 退出。
*/ */
#include "../../firstInit/ascii_bitmaps.c" #include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h" #include "../desktop_app.h"
#include "../net_stack.h"
#define CHAR_STEP 12 #define CHAR_STEP 12
#define CHAR_H 18 #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_app_context g_ac;
static da_cursor g_cursor; static da_cursor g_cursor;
static da_mouse g_mouse; 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); 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_h = 28;
int bar_x = 12; int bar_x = 12;
int bar_w = (int)g_ac.fb_w - 24; 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); u32 bar_bg = url_focused ? DA_BG_TERTIARY : DA_BG_SECONDARY;
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BORDER, 6); 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);
/* 导航按钮 */ /* URL 文本 + 光标 */
int btn_w = 32; int url_x = bar_x + 12;
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, url_buf, url_x, bar_y + 5, DA_TEXT_PRIMARY, bar_bg, CHAR_STEP);
da_draw_string(&g_ac, "<", bar_x + 14, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP); if (url_focused) {
da_fill_rounded_rect(&g_ac, bar_x + 4 + btn_w + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4); int cx = url_x + url_len * CHAR_STEP;
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_rect(&g_ac, cx, bar_y + 5, 2, CHAR_H, DA_ACCENT_LIGHT);
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 + (btn_w + 4) * 3 + 8; int hint_y = bar_y + bar_h + 4;
da_draw_string(&g_ac, "deshab://home", url_x, bar_y + 5, DA_TEXT_DIM, DA_BG_TERTIARY, CHAR_STEP); 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_y = hint_y + CHAR_H + 4;
int content_x = 16; int content_x = 12;
int content_w = (int)g_ac.fb_w - 32; 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); if (content_len > 0) {
da_rect_outline(&g_ac, content_x, content_y, content_w, 200, DA_BORDER, 8); /* 提取 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 visible_cols = (content_w - 16) / CHAR_STEP;
int icon_cy = content_y + 24; int visible_rows = (content_h - 8) / CHAR_H;
da_fill_rounded_rect(&g_ac, icon_cx, icon_cy, 64, 48, DA_BG_TERTIARY, 8); int y = content_y + 4;
/* 地球图标简化:圆圈 */ int pos = 0;
int cx = icon_cx + 32, cy = icon_cy + 24, r = 16; int line = 0;
for (int dy = -r; dy <= r; dy++) /* 跳过 scroll 行 */
for (int dx = -r; dx <= r; dx++) int skip = content_scroll;
if (dx*dx+dy*dy <= r*r && dx*dx+dy*dy > (r-1)*(r-1)) while (pos < plain_len && skip > 0) {
da_pixel(&g_ac, cx+dx, cy+dy, DA_ACCENT); if (plain[pos] == '\n') skip--;
/* 横线 */ pos++;
for (int dx = -r; dx <= r; dx++) }
if (dx*dx + 0 <= r*r) while (pos < plain_len && line < visible_rows) {
da_pixel(&g_ac, cx+dx, cy, DA_ACCENT); int ls = pos;
/* 竖线 */ while (pos < plain_len && plain[pos] != '\n' && pos - ls < visible_cols) pos++;
for (int dy = -r; dy <= r; dy++) int llen = pos - ls;
if (0 + dy*dy <= r*r) for (int i = 0; i < llen; i++) {
da_pixel(&g_ac, cx, cy+dy, DA_ACCENT); char c = (char)plain[ls + i];
if (c < ' ' || c > '~') c = '.';
/* 提示文字 */ char buf[2]; buf[0] = c; buf[1] = 0;
da_draw_string(&g_ac, "Deshab Browser", da_draw_string(&g_ac, buf, content_x + 8 + i * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
content_x + content_w / 2 - 7 * CHAR_STEP, icon_cy + 60, }
DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP); y += CHAR_H;
da_draw_string(&g_ac, "HTTP + HTML rendering required", line++;
content_x + content_w / 2 - 15 * CHAR_STEP, icon_cy + 82, if (pos < plain_len && plain[pos] == '\n') pos++;
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP); }
da_draw_string(&g_ac, "Press Esc to exit", } else if (!fetching) {
content_x + content_w / 2 - 9 * CHAR_STEP, icon_cy + 104, const char *empty = "No content. Enter URL and press Enter to fetch.";
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP); 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_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR); 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"))) __attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) { void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli"); __asm__ volatile("cli");
@@ -100,25 +279,85 @@ void dsk_entry(const da_boot_context *ctx) {
da_init(&g_ac, ctx); da_init(&g_ac, ctx);
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h); da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init(); da_mouse_init();
/* 初始化网络协议栈 */
br_ns_init();
br_set_status("Ready");
redraw_all(); redraw_all();
int e0 = 0, shift = 0;
for (;;) { for (;;) {
int need_redraw = 0; 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; 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); u8 st = inb(0x64);
if ((st & 1) && !(st & 0x20)) { if ((st & 1) && !(st & 0x20)) {
u8 data = inb(0x60); 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"); da_slog("browser", "exit");
return; 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) { if (need_redraw) {
da_cursor_restore(&g_ac, &g_cursor); da_cursor_restore(&g_ac, &g_cursor);
redraw_all(); redraw_all();
+17 -9
View File
@@ -40,8 +40,9 @@ typedef struct {
u64 kernel_stack_top; u64 reserved[8]; u64 kernel_stack_top; u64 reserved[8];
} da_boot_context; } da_boot_context;
/* block_read 函数类型 */ /* block 设备函数类型(与 shell/fat32_io.h 一致:u32 index 固定 0) */
typedef int (*da_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer); 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 提取的常用数据 */ /* 应用上下文 — 从 dsk_boot_context 提取的常用数据 */
typedef struct { typedef struct {
@@ -50,21 +51,26 @@ typedef struct {
u64 fb_h; /* 高度 */ u64 fb_h; /* 高度 */
u64 fb_pitch; /* 行宽(字节) */ u64 fb_pitch; /* 行宽(字节) */
da_block_read_fn block_read; da_block_read_fn block_read;
da_block_write_fn block_write;
const void *kernel_api; const void *kernel_api;
} da_app_context; } 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) { static inline void da_init(da_app_context *ac, const da_boot_context *ctx) {
ac->fb = (u32 *)ctx->framebuffer_address; ac->fb = (u32 *)ctx->framebuffer_address;
ac->fb_w = ctx->framebuffer_width; ac->fb_w = ctx->framebuffer_width;
ac->fb_h = ctx->framebuffer_height; ac->fb_h = ctx->framebuffer_height;
ac->fb_pitch = ctx->framebuffer_pitch; ac->fb_pitch = ctx->framebuffer_pitch;
ac->kernel_api = (const void *)ctx->dkm_kernel_api; 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; u64 api = ctx->dkm_kernel_api;
ac->block_read = (da_block_read_fn)*(u64 *)(api + 0xA8); u64 blk_api = *(u64 *)(api + 0xA8);
if (ac->block_read) if (blk_api) {
ac->block_read = (da_block_read_fn)*(u64 *)((u64)ac->block_read + 16); 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; if (m->idx < 3) return 0;
m->idx = 0; m->idx = 0;
if (!(m->buf[0] & 0x08)) return 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 dx = (int)(signed char)m->buf[1];
int dy = (int)(signed char)m->buf[2]; 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; dy = -dy;
c->mx += dx; c->my += dy; c->mx += dx; c->my += dy;
if (c->mx < 0) c->mx = 0; if (c->mx < 0) c->mx = 0;
+118 -14
View File
@@ -1,14 +1,18 @@
/* Deshab Editor — 全屏文本编辑器 /* Deshab Editor — 全屏文本编辑器
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。 * PS/2 键盘输入,行号 + 光标 + 垂直滚动。
* Ctrl+O 打开文件,Ctrl+S 保存(暂不支持),Esc 退出。 * Ctrl+O 打开文件(输入 8.3 名,从 FAT32 根目录读取)
* Ctrl+S 保存文件(输入 8.3 名,写入 FAT32 根目录)
* Esc 退出。
*/ */
#include "../../firstInit/ascii_bitmaps.c" #include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h" #include "../desktop_app.h"
#include "../fat32_io.h"
#define CHAR_STEP 12 #define CHAR_STEP 12
#define CHAR_H 18 #define CHAR_H 18
#define EDITOR_BUF_SIZE 65536 #define EDITOR_BUF_SIZE 65536
#define INPUT_BUF_SIZE 16
static da_app_context g_ac; static da_app_context g_ac;
@@ -22,6 +26,11 @@ static int modified = 0;
static char filename[64] = "untitled"; static char filename[64] = "untitled";
static int ctrl_down = 0; 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) { static int line_start(int line) {
int l = 0; int l = 0;
for (int i = 0; i < text_len; i++) { for (int i = 0; i < text_len; i++) {
@@ -47,6 +56,63 @@ static void init_default_text(void) {
recalc_cursor(); 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) { static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_SECONDARY); da_fill_bg(&g_ac, DA_BG_SECONDARY);
char title[80]; char title[80];
@@ -74,16 +140,13 @@ static void redraw_all(void) {
for (; pos <= text_len && line < scroll_y + visible_rows; ) { for (; pos <= text_len && line < scroll_y + visible_rows; ) {
if (line >= scroll_y) { if (line >= scroll_y) {
int screen_y = top_y + (line - scroll_y) * CHAR_H; int screen_y = top_y + (line - scroll_y) * CHAR_H;
/* 行号 */
char ln[8]; char ln[8];
ln[0] = '0' + ((line+1)/100)%10; ln[0] = '0' + ((line+1)/100)%10;
ln[1] = '0' + ((line+1)/10)%10; ln[1] = '0' + ((line+1)/10)%10;
ln[2] = '0' + (line+1)%10; ln[2] = '0' + (line+1)%10;
ln[3] = 0; ln[3] = 0;
da_draw_string(&g_ac, ln, margin_x, screen_y, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP); 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); da_fill_rect(&g_ac, margin_x + line_num_w - 2, screen_y, 1, CHAR_H, DA_BORDER);
/* 文本 */
int col = 0; int col = 0;
while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) { while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) {
da_draw_char(&g_ac, text_buf[pos], 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++; if (pos < text_len && text_buf[pos] == '\n') pos++;
line++; line++;
if (pos >= text_len) { if (pos >= text_len) {
/* 空行也画光标 */
if (line > cursor_line) break; 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 cy = top_y + (cursor_line - scroll_y) * CHAR_H;
int cx = text_x + cursor_col * CHAR_STEP; int cx = text_x + cursor_col * CHAR_STEP;
da_fill_rect(&g_ac, cx, cy, 2, CHAR_H, DA_ACCENT_LIGHT); 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++; const char *s1 = "L:"; while (*s1) status[p++] = *s1++;
status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10; status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10;
const char *s2 = " C:"; while (*s2) status[p++] = *s2++; const char *s2 = " C:"; while (*s2) status[p++] = *s2++;
status[p++] = '0' + (cursor_col+1)/10%10; status[p++] = '0' + (cursor_col+1)%10; 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++; } 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; status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h); 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); 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(); init_default_text();
redraw_all(); redraw_all();
@@ -148,6 +222,36 @@ void dsk_entry(const da_boot_context *ctx) {
if (sc == 0x9D) { ctrl_down = 0; continue; } if (sc == 0x9D) { ctrl_down = 0; continue; }
if (sc & 0x80) { e0 = 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 退出 */ /* Esc 退出 */
if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; } if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; }
@@ -212,16 +316,16 @@ void dsk_entry(const da_boot_context *ctx) {
modified = 1; modified = 1;
} }
} else { } else {
/* Ctrl+O: 打开文件(提示暂不支持) */ /* Ctrl+O: 打开文件 */
if (ctrl_down && (sc == 0x18)) { if (ctrl_down && (sc == 0x18)) {
/* Ctrl+O - stub */ enter_input_mode(1);
da_slog("editor", "Ctrl+O - open not yet"); redraw_all();
continue; continue;
} }
/* Ctrl+S: 保存(提示暂不支持) */ /* Ctrl+S: 保存文件 */
if (ctrl_down && (sc == 0x1F)) { if (ctrl_down && (sc == 0x1F)) {
da_slog("editor", "Ctrl+S - save not yet"); enter_input_mode(2);
modified = 0; redraw_all();
continue; continue;
} }
char c = da_scan_to_ascii(sc, shift); 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 */
+236 -71
View File
@@ -1,80 +1,107 @@
/* Deshab File Manager — 全屏文件管理器 /* Deshab File Manager — 全屏文件管理器
* 文件列表视图,鼠标点击选择,键盘导航。 * 接入 fat32_io.h 共享头,支持:
* FAT32 根目录遍历,Esc 退出。 * - 子目录遍历(路径栈,Enter 进入 / Backspace 返回)
* - FAT 链跟随(多簇文件/目录)
* - 文件查看(Enter 打开文本文件,只读浏览,Esc 返回列表)
* - 鼠标点击选择 + 键盘导航
* Esc 退出返回 DSK/desktop。
*/ */
#include "../../firstInit/ascii_bitmaps.c" #include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h" #include "../desktop_app.h"
#include "../fat32_io.h"
#define CHAR_STEP 12 #define CHAR_STEP 12
#define CHAR_H 18 #define CHAR_H 18
#define MAX_FILES 256 #define MAX_FILES 256
#define MAX_DEPTH 16
#define VIEW_BUF 24576 /* 文件查看器缓冲 24KB */
static da_app_context g_ac; static da_app_context g_ac;
static da_cursor g_cursor; static da_cursor g_cursor;
static da_mouse g_mouse; static da_mouse g_mouse;
typedef struct { typedef struct {
char name[13]; /* 8.3 */ char name[13];
int is_dir; u32 clus;
u32 size; 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 file_count = 0;
static int selected = 0; static int selected = 0;
static int scroll_off = 0; static int scroll_off = 0;
static void load_root_dir(void) { /* 路径栈:每层记录目录首簇(0=根)与显示名 */
file_count = 0; static u32 path_clus[MAX_DEPTH];
if (!g_ac.block_read) return; 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; static int view_mode = 0;
if (bpb[510] != 0x55 || bpb[511] != 0xAA) return; 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]; static int fm_collect_cb(const f32_entry *e, void *user_data) {
u32 fat_start = *(u16 *)(bpb + 14); (void)user_data;
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2; if (file_count >= MAX_FILES) return 1;
fm_entry *f = &files[file_count];
u8 dir_buf[8192]; for (int i = 0; i < 13; i++) f->name[i] = e->name[i];
u32 lba = data_start + (root_cluster - 2) * sectors_per_cluster; f->clus = e->clus;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) return; f->size = e->size;
f->is_dir = e->is_dir;
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];
}
f->name[p] = 0;
file_count++; file_count++;
} return 0;
} }
static void redraw_all(void) { 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);
}
/* ---- 路径显示 ---- */
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_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Files", (i64)g_ac.fb_w); 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 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 row_h = CHAR_H + 6;
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h; int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
int col_name_x = margin + 32; 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; int col_type_x = (int)g_ac.fb_w - 60;
/* 列标题 */ /* 列标题 */
@@ -83,9 +110,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_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); 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 < 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; if (scroll_off < 0) scroll_off = 0;
for (int i = 0; i < visible_rows; i++) { for (int i = 0; i < visible_rows; i++) {
@@ -93,28 +121,23 @@ static void redraw_all(void) {
if (idx >= file_count) break; if (idx >= file_count) break;
int y = top_y + CHAR_H + 6 + i * row_h; int y = top_y + CHAR_H + 6 + i * row_h;
/* 选中高亮 */
if (idx == selected) { if (idx == selected) {
da_fill_rect(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_BG_TERTIARY); 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); 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; 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); 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; 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, da_draw_string(&g_ac, files[idx].name, col_name_x, y, name_color,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP); (idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
/* 大小 */
if (!files[idx].is_dir) { if (!files[idx].is_dir) {
char sz[16]; int p = 0; char sz[16];
u32 s = files[idx].size; fm_u32_dec(sz, files[idx].size);
if (s == 0) { sz[p++] = '0'; } int sl = 0; while (sz[sl]) sl++;
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[sl++] = ' '; sz[sl++] = 'B'; sz[sl] = 0;
sz[p++] = ' '; sz[p++] = 'B'; sz[p] = 0;
da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM, da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP); (idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
} else { } else {
@@ -122,7 +145,6 @@ static void redraw_all(void) {
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP); (idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
} }
/* 类型 */
const char *type = files[idx].is_dir ? "DIR" : "FILE"; const char *type = files[idx].is_dir ? "DIR" : "FILE";
da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM, da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM,
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP); (idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
@@ -134,18 +156,129 @@ static void redraw_all(void) {
} }
/* 状态栏 */ /* 状态栏 */
char status[64]; int p = 0; char status[80]; int p = 0;
const char *p1 = "/ "; while (*p1) status[p++] = *p1++; char nc[12]; fm_u32_dec(nc, (u32)file_count);
status[p++] = '0' + file_count / 10; status[p++] = '0' + file_count % 10; const char *s1 = "items: "; while (*s1) status[p++] = *s1++;
const char *p2 = " items"; while (*p2) status[p++] = *p2++; 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; status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h); 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_save(&g_ac, &g_cursor);
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR); 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 (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();
}
}
__attribute__((visibility("default"))) __attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) { void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli"); __asm__ volatile("cli");
@@ -160,7 +293,11 @@ void dsk_entry(const da_boot_context *ctx) {
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h); da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
da_mouse_init(); 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;
load_current_dir();
redraw_all(); redraw_all();
int e0 = 0, shift = 0; (void)shift; int e0 = 0, shift = 0; (void)shift;
@@ -170,10 +307,8 @@ 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)) { if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
need_redraw = 1; need_redraw = 1;
if (g_cursor.btn) { if (g_cursor.btn && !view_mode) {
/* 点击选择文件 */ int top_y = DA_TITLEBAR_H + 4 + CHAR_H + 4;
int margin = 12;
int top_y = DA_TITLEBAR_H + 4;
int row_h = CHAR_H + 6; int row_h = CHAR_H + 6;
int click_y = g_cursor.my - top_y - CHAR_H - 6; int click_y = g_cursor.my - top_y - CHAR_H - 6;
if (click_y >= 0) { if (click_y >= 0) {
@@ -196,11 +331,36 @@ void dsk_entry(const da_boot_context *ctx) {
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; } if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
if (sc & 0x80) { e0 = 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 (e0) {
if (sc == 0x48) { /* Up */ if (sc == 0x48) { /* Up */
if (selected > 0) selected--; if (selected > 0) selected--;
if (selected < scroll_off) scroll_off = selected;
need_redraw = 1; need_redraw = 1;
} else if (sc == 0x50) { /* Down */ } else if (sc == 0x50) { /* Down */
if (selected < file_count - 1) selected++; if (selected < file_count - 1) selected++;
@@ -211,12 +371,17 @@ void dsk_entry(const da_boot_context *ctx) {
} }
if (sc == 0x1C) { /* Enter - 打开 */ if (sc == 0x1C) { /* Enter - 打开 */
if (selected >= 0 && selected < file_count) { open_selected();
da_slog("fileman", "open file (stub)");
}
need_redraw = 1; need_redraw = 1;
} else if (sc == 0x0E) { /* Backspace - 返回上级 */ } 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; need_redraw = 1;
} }
} }
+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 完整实现
+231
View File
@@ -0,0 +1,231 @@
#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 */
};
/* ===== 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 */
+6
View File
@@ -0,0 +1,6 @@
开发者模式中启动的Shell的提示符没有在第一行也没有显示
shell光标没有闪烁
Login界面没有显示鼠标指针
在桌面上时每按下按键屏幕就会闪烁
窗口打开和关闭时的动画太卡,而且屏幕会闪,直接去掉动画
启动一次之后,第二次开机启动会直接退出
Binary file not shown.
+7
View File
@@ -42,3 +42,10 @@ verbose: yes
module_cmdline: dsk:main module_cmdline: dsk:main
module_path: boot():/driver/test.fat32 module_path: boot():/driver/test.fat32
module_cmdline: dkm:test_fat32 module_cmdline: dkm:test_fat32
# Linux 6.6 LTS guest kernel (for UTSM+Linux dual-kernel architecture)
# Uncomment after building Linux via CODE/linux/build.sh
# 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.
+1 -1
View File
@@ -1,2 +1,2 @@
0 1
1 1
Binary file not shown.