This commit is contained in:
Linehu
2026-08-09 22:20:01 +08:00
parent 8717ea8f20
commit 2a50fa58c7
185 changed files with 19890 additions and 710 deletions
+17
View File
@@ -14,6 +14,23 @@
/* ---- Probe 信息结构(通过 reserved[6] 传递给 shell/desktop) ---- */
#define PROBE_INFO_MAGIC 0x50524F42u /* "PRFB" — Probe inFo Buffer */
/* ---- 网络租约信息(netman 写入,DSK 持有存储,经 reserved[7] 指针传递) ----
* reserved[0..6] 已被占用(user.conf/login/dev_mode/pe/lxc/probe),
* 仅 reserved[7] 空闲,故以其存放指向本结构的指针。 */
#define NET_LEASE_MAGIC 0x4E45544Cu /* "NETL" */
#define NET_LEASE_F_VALID (1u << 0) /* ip/gateway/dns 有效 */
#define NET_LEASE_F_STATIC (1u << 1) /* 静态配置(非 DHCP) */
typedef struct net_lease_info {
u32 magic; /* NET_LEASE_MAGIC */
u32 flags; /* NET_LEASE_F_* */
u32 ip; /* guest IP(网络序) */
u32 gateway; /* 网关(网络序) */
u32 dns; /* DNS(网络序) */
u32 server; /* DHCP server(网络序,调试) */
u32 reserved[2];
} net_lease_info;
typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC */
u32 version; /* ABI 版本,当前 1 */
+3 -2
View File
@@ -8,8 +8,9 @@
#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 级页表 */
/* EPTP 构造:bits [2:0]=memory type, bits [5:3]=walk length-1, bit 6=AD, bits [51:12]=PML4 HPA
* P8.4 Fix: walk length was at bits [7:6] (wrong), should be bits [5:3] per Intel SDM. */
#define EPTP_WALK_LEN_4 (3ULL << 3) /* 4 级页表 (3 = 4-1) at bits [5:3] */
#define EPTP_MEMTYPE_WB (EPT_MEMORY_TYPE_WB)
/* 初始化 EPT:分配 PML4,置零。返回 0 成功。 */
+80
View File
@@ -21,6 +21,34 @@
#define LINUX_COMPAT_MAGIC 0x4C4E58434F4D5000ULL /* "LNXCOMP\0" */
/* VSCode Phase 3: virtio-gpu scanout 查询结果。
* 镜像 virtio_mmio.h 的 virtio_gpu_scanout_info 字段布局,保持公共 ABI 自洽
* (不强制消费者 include virtio_mmio.h)。必须在 linux_compat_service 之前
* 定义,因为服务表中有指向此类型的函数指针。 */
struct linux_compat_scanout_info {
void *host_vaddr;
u32 width;
u32 height;
u32 stride;
u32 dirty;
u32 enabled;
/* Phase 7: 累积 dirty rect(scanout 坐标系并集,查询随 dirty 清零)。
* dirty=0 时全为 0;desktop 据此只 blit 变化区域(尾部追加,ABI 兼容)。 */
u32 dirty_x;
u32 dirty_y;
u32 dirty_w;
u32 dirty_h;
/* P7.6: hardware cursor state(尾部追加,ABI 兼容)。 */
u32 cursor_visible;
u32 cursor_x;
u32 cursor_y;
u32 cursor_hot_x;
u32 cursor_hot_y;
u32 cursor_w;
u32 cursor_h;
void *cursor_bitmap;
};
/* Linux 兼容层服务表 — 通过 boot context reserved[5] 传递 */
typedef struct linux_compat_service {
u64 magic; /* LINUX_COMPAT_MAGIC */
@@ -93,6 +121,41 @@ typedef struct linux_compat_service {
* 返回 0 成功,负值失败(-11=文件不存在,其余同 file_pull)。 */
int (*file_push)(const char *fat32_name, const char *linux_path,
u64 *out_size);
/* VSCode Phase 2: 输入转发到 Linux guest(virtio-input 键盘+鼠标)。
* 由 desktop 在 IDE attached 时调用,把 Deshab PS/2 事件注入 guest。
* 事件排到 pending ring,由 vmexit poll 路径排空到 eventq + 注入 IRQ。
*
* input_forward_keyboard(code, value)
* code : linux keycode(KEY_A=30 等,见 linux/input-event-codes.h)
* value : 0=release, 1=press, 2=repeat
*
* input_forward_mouse(dx, dy, buttons)
* dx, dy : 相对位移(像素差,可为负)
* buttons : 按钮位图 bit0=左, bit1=右, bit2=中(内部与上次状态比较,
* 仅变化的按钮产生 press/release 事件;末尾自动追加 SYN) */
void (*input_forward_keyboard)(u16 code, u32 value);
void (*input_forward_mouse)(i32 dx, i32 dy, u8 buttons);
/* VSCode Phase 3: 查询 virtio-gpu scanout 状态(desktop IDE blit 用)。
* 在 IDE attached 时由 desktop 周期调用:若 dirty=1,把 host_vaddr 指向的
* scanout 影子缓冲(width×height,stride 字节/行,XRGB8888)blit 到
* framebuffer 的 IDE host 区域,然后重新查询以清零 dirty。
* out->host_vaddr : scanout 影子缓冲 host 虚拟地址(SAS-R0 下可直接解引用)
* out->enabled : 1 = scanout 已绑定 2D resource(X server 已 SET_SCANOUT)
* out->dirty : 1 = 自上次查询有新帧(查询会清零该位)
* 返回 0 成功(enabled=0 时其余字段为零),-1 后端未就绪(无 surface pool)。 */
int (*gpu_get_scanout_info)(struct linux_compat_scanout_info *out);
/* VSCode Phase 5: 异步执行 Linux 程序(daemon fork+setsid+detach)。
* 与 exec 语义相同,但 daemon 在 spawn 成功后立即回 EXEC_EXIT(0),
* 不捕获 stdout、不等待进程退出——用于启动 X server / VSCode 等
* 长生命周期 GUI 进程而不阻塞 UTSM 侧。子进程 stdio 重定向到
* /dev/null,环境自带 LD_LIBRARY_PATH=/usr/lib/deshab 与 DISPLAY=:0。
* 返回 0 = 已成功 spawn(不代表程序后续存活),负值失败
* (-1=服务不可用, -2=IPC 发送失败, -3=linux_resume 失败,
* -4=无响应, -5=daemon 报告 spawn 失败)。 */
int (*exec_async)(const char *path, int argc, const char *const *argv);
} linux_compat_service;
/* ===== UTSM 内部接口(不进服务表,供 hypercall.c 等内核代码使用) =====
@@ -111,4 +174,21 @@ const linux_compat_service *linux_compat_get_service(void);
/* 初始化 Linux 兼容层服务(在 linux_launch 成功后调用)。 */
void linux_compat_init(void);
/* 同步 FAT32 lib 目录下所有 .so 文件到 Linux guest。
* guest_lib_path : guest 端库目录路径(如 "/usr/lib/deshab")
* 调用时机:linux_compat_init 成功后。失败不阻断启动。
* 返回 0 成功,负值失败。 */
int lxc_sync_lib_dir(const char *guest_lib_path);
/* VSCode Phase 5: 把 VSCode tarball(Limine boot module,路径含 "vscode")
* 安装到 Linux guest 的 /opt/vscode(经 overlay 持久化在 extra-rootfs 卷)。
* 流程:已安装标记探测(/opt/vscode/bin/code 可执行则跳过)→
* 流式推送 module 字节到 guest /tmp/vscode.tar.gz →
* mkdir -p /opt/vscode → tar 解包(--strip-components=1,失败回退不解层)→
* 清理 /tmp tarball → 验证 /opt/vscode/bin/code。
* 调用时机:linux_compat_init 成功后(kernel main,FUCK [compat] vscode_install=1)。
* 返回 0 成功或已安装;负值失败(-1 服务不可用, -11 无 tarball module,
* -12 推送失败, -13 解包失败, -14 验证失败)。所有失败均不阻断启动。 */
int lxc_vscode_install(void);
#endif /* UTSM_LINUX_COMPAT_H */
+80 -9
View File
@@ -103,7 +103,9 @@ struct linux_setup_header {
* 0x01000000-0x02FFFFFF : Linux kernel image
* 0x03000000-0x03FFFFFF : Linux initrd
* 0x04000000-0x040FFFFF : IPC shared memory (1MB, both kernels RW)
* 0x05000000-MAX : Linux general RAM
* 0x04100000-0x04FFFFFF : Graphics surface pool (16MB, VSCode integration Phase 1)
* 0x05000000-0x5FFFFFFF : Linux general RAM (~1.4GB, expanded Phase 1 for VSCode)
* 0xF4000000-0xF4007000 : virtio-mmio devices (8 slots, expanded Phase 1)
*/
#define LINUX_GUEST_KERNEL_GPA 0x01000000ULL /* 16 MB, 2MB-aligned */
#define LINUX_GUEST_BOOTPARAMS_GPA 0x00400000ULL /* 4 MB */
@@ -118,8 +120,19 @@ struct linux_setup_header {
#define LINUX_GUEST_IPC_SHM_GPA 0x04000000ULL /* 64 MB */
#define LINUX_GUEST_IPC_SHM_SIZE (1024 * 1024) /* 1MB */
/* General RAM starts after IPC region */
/* VSCode integration Phase 1: Graphics surface pool.
* Separate DMA region EPT-mapped into the Linux guest. Linux user-space mmaps
* /dev/utsm at this GPA to obtain a writable scanout buffer that UTSM reads
* for blitting to the Deshab framebuffer (Phase 3 virtio-gpu shadow buffer).
* 16MB accommodates a 1920×1080×4 framebuffer (~8MB) with headroom for
* cursor plane and dirty rect tracking metadata. */
#define LINUX_GUEST_SURFACE_GPA 0x04100000ULL /* 65 MB (immediately after IPC shm) */
#define LINUX_GUEST_SURFACE_SIZE (16 * 1024 * 1024) /* 16MB */
/* General RAM starts after IPC + surface regions.
* Phase 1 expansion: 1.4GB to fit VSCode + Electron + GUI stack. */
#define LINUX_GUEST_RAM_GPA 0x05000000ULL /* 80 MB */
#define LINUX_GUEST_RAM_SIZE 0x5B000000ULL /* 1.4GB (0x05000000..0x5FFFFFFF) */
/* ===== virtio-mmio device GPA layout =====
* virtio-mmio 设备放在 GPA 高位 MMIO 区(guest RAM 之上,不占用 e820 RAM)。
@@ -129,16 +142,33 @@ struct linux_setup_header {
* 这些 GPA 在 EPT 中【故意不映射】,访问时触发 EPT violation,
* 由 vmexit handler 路由到 virtio_mmio 后端模拟寄存器读写。
*
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x09000000)。 */
* GPA 选择 0xF4000000 起始(接近 4GB,远离 guest RAM 0x05000000-0x5FFFFFFF)。
*
* VSCode integration Phase 1 expansion: 槽位从 3 扩到 8,为后续阶段预留:
* slot 0: blk (Phase 1-) IRQ5
* slot 1: net (Phase 1-) IRQ6
* slot 2: rootfs (Phase 1-) IRQ7
* slot 3: keyboard (Phase 2) IRQ8 — virtio-input, evdev /dev/input/event0
* slot 4: mouse (Phase 2) IRQ9 — virtio-input, evdev /dev/input/event1
* slot 5: gpu (Phase 3) IRQ10 — virtio-gpu 2D, /dev/dri/card0
* slot 6: extra-rootfs (Phase 4) IRQ11 — persistent rw volume (/dev/vdc)
* slot 7: reserved — future use
*/
#define VIRTIO_MMIO_GPA_BASE 0xF4000000ULL
#define VIRTIO_MMIO_GPA_STRIDE 0x1000ULL /* 每个设备 4KB */
#define VIRTIO_MMIO_MAX_SLOTS 8 /* Phase 1: 3→8 */
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
#define VIRTIO_MMIO_BLK_GPA (VIRTIO_MMIO_GPA_BASE + 0 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4000000 */
#define VIRTIO_MMIO_NET_GPA (VIRTIO_MMIO_GPA_BASE + 1 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4001000 */
#define VIRTIO_MMIO_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 2 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4002000 */
/* VSCode integration: Phase 2/3/4 device slots (currently unused, reserved) */
#define VIRTIO_MMIO_KBD_GPA (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4003000 */
#define VIRTIO_MMIO_MOUSE_GPA (VIRTIO_MMIO_GPA_BASE + 4 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4004000 */
#define VIRTIO_MMIO_GPU_GPA (VIRTIO_MMIO_GPA_BASE + 5 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4005000 */
#define VIRTIO_MMIO_EXTRA_ROOTFS_GPA (VIRTIO_MMIO_GPA_BASE + 6 * VIRTIO_MMIO_GPA_STRIDE) /* 0xF4006000 */
/* 判断 GPA 是否落在 virtio-mmio 设备区(含两端) */
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + 3 * VIRTIO_MMIO_GPA_STRIDE)
#define VIRTIO_MMIO_GPA_END (VIRTIO_MMIO_GPA_BASE + VIRTIO_MMIO_MAX_SLOTS * VIRTIO_MMIO_GPA_STRIDE)
#define IS_VIRTIO_MMIO_GPA(gpa) ((gpa) >= VIRTIO_MMIO_GPA_BASE && (gpa) < VIRTIO_MMIO_GPA_END)
/* ===== Linux loader API ===== */
@@ -156,6 +186,13 @@ struct linux_guest_info {
u64 pgt_gpa; /* GPA of guest page tables */
u64 gdt_gpa; /* GPA of guest GDT */
int loaded; /* 1 = successfully loaded */
/* VSCode integration Phase 1: graphics surface pool.
* Allocated as a separate DMA buffer and EPT-mapped into the guest at
* surface_gpa. Exposed to Linux user-space via UTSM_HCALL_SURFACE_INFO
* and /dev/utsm mmap (see CODE/linux/patches/utsm_hcall.c). */
u64 surface_hpa; /* Host physical address of surface pool (0 = none) */
u64 surface_gpa; /* GPA visible inside Linux guest */
u64 surface_size; /* Surface pool size in bytes */
};
/* Find the Linux bzImage boot module among Limine modules.
@@ -167,6 +204,17 @@ void *linux_find_bzimage_module(u64 *size_out);
* Exposed to Linux guest via memory-backed virtio-blk as /dev/vdb. */
void *linux_find_rootfs_module(u64 *size_out);
/* VSCode Phase 4: find the extra rootfs image boot module (path contains
* "extra"). Exposed via writable virtio-blk slot6 as /dev/vdc (overlayfs
* upper / VSCode data volume). Returns NULL when the module is absent. */
void *linux_find_extra_rootfs_module(u64 *size_out);
/* VSCode Phase 5: find the VSCode tarball boot module (path contains
* "vscode", e.g. linux-vscode.tar.gz). The tarball is streamed into the
* guest and extracted to /opt/vscode by lxc_vscode_install().
* Returns NULL when the module is absent (install skipped, non-fatal). */
void *linux_find_vscode_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,
@@ -181,6 +229,20 @@ 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);
/* VSCode integration Phase 1: query graphics surface pool.
* Returns 0 and fills outputs if the surface pool was allocated and EPT-mapped
* into the Linux guest; returns -1 if not initialized (e.g. Linux guest not
* loaded). hpa_out receives the host physical address (for UTSM-side blit),
* gpa_out receives the GPA visible inside Linux (for the guest mmap path),
* size_out receives the pool size in bytes. Any output pointer may be NULL. */
int linux_get_surface_info(u64 *hpa_out, u64 *gpa_out, u64 *size_out);
/* VSCode Phase 3: return host virtual address of the graphics surface pool.
* The virtio-gpu 2D backend composites the scanout into this buffer; the
* desktop reads it to blit guest graphics onto the IDE host area.
* Returns NULL if the pool was not allocated (Linux guest not loaded / alloc failed). */
void *linux_get_surface_vaddr(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. */
@@ -193,13 +255,22 @@ int linux_launch(void);
* noapic/nolapic/nosmp: UTSM VMM 只模拟 legacy PIC(8259) + PIT(8254),
* 不提供 LAPIC/IOAPIC;guest 走 XT-PIC 模式,IRQ vector = 0x30+irq。
* virtio_mmio.device=: 向 guest 声明 virtio-mmio 设备(blk@0xF4000000 IRQ5,
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。 */
* net@0xF4001000 IRQ6),需 CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y。
* VSCode Phase 2: kbd@0xF4003000 IRQ8, mouse@0xF4004000 IRQ9 (virtio-input)。
* VSCode Phase 3: gpu@0xF4005000 IRQ10 (virtio-gpu 2D, /dev/dri/card0)。
* VSCode Phase 4: xrootfs@0xF4006000 IRQ11 (可写 virtio-blk, /dev/vdc)。
* 注:slot6 无对应 boot module 时后端不注册,guest 探测会超时跳过,
* 不影响启动(virtio_mmio 对无响应设备只打一条 warn)。 */
#define LINUX_DEFAULT_CMDLINE \
"console=ttyS0,115200 earlyprintk=serial nokaslr " \
"no_timer_check loglevel=7 nohlt idle=poll " \
"noapic nolapic nosmp " \
"virtio_mmio.device=4K@0xF4000000:5 " \
"virtio_mmio.device=4K@0xF4001000:6 " \
"virtio_mmio.device=4K@0xF4002000:7"
"virtio_mmio.device=4K@0xF4002000:7 " \
"virtio_mmio.device=4K@0xF4003000:8 " \
"virtio_mmio.device=4K@0xF4004000:9 " \
"virtio_mmio.device=4K@0xF4005000:10 " \
"virtio_mmio.device=4K@0xF4006000:11"
#endif
+5
View File
@@ -13,4 +13,9 @@ void log_error(const char *msg);
void log_hex64(const char *prefix, u64 value);
void panic(const char *msg);
/* ---- 内存日志缓冲区(实机启动日志持久化) ---- */
void log_capture_enable(int enable);
const char *log_get_buffer(void);
u32 log_get_length(void);
#endif
+38
View File
@@ -24,6 +24,29 @@ typedef struct pe_image_info {
int load_ok; /* 1=加载成功 */
} pe_image_info;
/* ===== 窗口模式(P5 桌面嵌入)=====
* 默认 run() 为全屏独占:shim 直写 Limine framebuffer、直接轮询 PS/2 端口。
* run_windowed() 把 PE GUI 嵌入桌面受管窗口:
* - 渲染目标改为 host 提供的内存 surface(32bpp 0xAARRGGBB)
* - PE 消息队列空转(GetMessage/PeekMessage 无消息)时回调 host->pump(),
* 由 desktop 驱动一帧(渲染全部窗口 + 采集输入 + 注入 PE 队列)
* - 输入由 host 经 inject_scancode/inject_pointer 注入(坐标已转换为
* surface 局部坐标,shim 内部合成 Win32 消息与修饰键状态)
*
* 协作模型约束:
* - pump 回调运行在 PE 大栈上,desktop 帧代码栈用量必须远小于 1MB;
* - pump 内禁止再次调用 run/run_windowed(禁止嵌套 PE 实例);
* - 关闭 PE 窗口 = inject WM_CLOSE(desktop 经 inject_scancode 无此路径,
* 使用 inject_input 直接投递消息),等待 PE 自愿退出,run 才返回。 */
typedef struct pe_window_host {
int (*pump)(void *ud); /* PE 消息空转让出点;返回 0 继续,非 0 请求退出 */
void *ud;
u8 *surface; /* PE 渲染目标(32bpp 0xAARRGGBB),NULL=全屏 */
u32 width; /* surface 宽(像素) */
u32 height; /* surface 高(像素) */
u32 pitch; /* 字节/行 */
} pe_window_host;
/* PE 服务表 — 通过 boot context reserved[4] 传递 */
typedef struct pe_service {
u64 magic; /* PE_SERVICE_MAGIC */
@@ -34,6 +57,21 @@ typedef struct pe_service {
int (*run)(const void *pe_data, u64 size, const char *cmdline, u64 *exit_code);
/* 释放 pe_load 分配的镜像内存。 */
void (*unload)(pe_image_info *info);
/* ===== ABI 尾部追加(P5 窗口模式)。同镜像一致性假设:
* desktop 与 UTSM 由同一 build.ps1 产出,旧 UTSM 配新 desktop
* 的组合现实中不存在;调用前仍须校验 magic。 ===== */
/* 窗口模式运行:语义同 run,渲染/输入/让出按 pe_window_host 重定向。
* host==NULL 或 host->surface==NULL 时等价 run。 */
int (*run_windowed)(const void *pe_data, u64 size, const char *cmdline,
u64 *exit_code, const pe_window_host *host);
/* 注入键盘扫描码(set-1,bit7=release;e0_prefix!=0 表示 E0 扩展键)。
* shim 内部完成 vk 映射/修饰键跟踪/WM_CHAR 合成,与全屏路径共用。 */
int (*inject_scancode)(u32 scancode, u32 e0_prefix);
/* 注入鼠标状态(surface 局部坐标;buttons bit0=L bit1=R bit2=M)。
* shim 内部与上次状态比较合成 MOUSEMOVE/BUTTONDOWN/UP 边沿。 */
int (*inject_pointer)(i32 x, i32 y, u32 buttons);
/* 直接投递一条 Win32 消息(用于 WM_CLOSE 等宿主发起的控制消息)。 */
int (*inject_input)(u32 msg, u64 wparam, u64 lparam, i32 ptx, i32 pty);
} pe_service;
/* 获取全局 PE 服务实例指针(UTSM 内部使用,外部通过 boot context 获取) */
+106
View File
@@ -47,6 +47,8 @@
#define VIRTIO_ID_NET 1
#define VIRTIO_ID_BLOCK 2
#define VIRTIO_ID_CONSOLE 3
#define VIRTIO_ID_INPUT 18 /* VSCode Phase 2: virtio-input (keyboard/mouse) */
#define VIRTIO_ID_GPU 16 /* VSCode Phase 3: virtio-gpu 2D */
/* device status bits */
#define VIRTIO_STATUS_ACKNOWLEDGE 1
@@ -85,6 +87,10 @@ struct virtio_backend {
/* 读取 device config 空间(offset 相对 0x100)。返回 32 位值。 */
u32 (*read_config)(u32 offset, int width);
/* VSCode Phase 2: 写入 device config 空间(offset 相对 0x100)。
* virtio-input 用此通道设置 select/subsel 再读结果;其它设备 config
* 只读,置 NULL 时框架忽略 config 写。 */
void (*write_config)(u32 offset, u32 value);
/* 通知:guest 写入 QUEUE_NOTIFY,后端处理该队列的请求。 */
void (*queue_notify)(u32 queue_idx);
/* 复位设备。 */
@@ -153,13 +159,113 @@ int virtio_queue_get_ptrs(u32 device_id, u32 queue_idx,
* 由后端在写 used ring 元素后调用,内部做内存屏障。 */
void virtio_queue_bump_used(struct virtq_used *used, u16 new_idx);
/* VSCode Phase 2: 按 gpa_base 定位队列(用于 device_id 相同的多实例设备,
* 如两个 virtio-input:键盘+鼠标同为 VIRTIO_ID_INPUT=18)。
* 语义与 virtio_queue_get_ptrs 一致,只是查找键由 device_id 改为 gpa_base。 */
int virtio_queue_get_ptrs_by_gpa(u64 gpa_base, u32 queue_idx,
struct virtq_desc **desc_out,
struct virtq_avail **avail_out,
struct virtq_used **used_out,
u32 *queue_num_out);
/* 异步触发设备中断(设置 INTERRUPT_STATUS bit0 并向 guest 注入虚拟 IRQ)。
* 用于后端在 QUEUE_NOTIFY 上下文之外完成工作(如周期轮询收到 RX 包)。
* device_id: VIRTIO_ID_*;设备未注册或 irq==0 时为空操作。 */
void virtio_mmio_raise_irq(u32 device_id);
/* VSCode Phase 2: 按 gpa_base 触发中断。用于 device_id 相同的多实例设备
*(两个 virtio-input 同为 18):virtio_mmio_raise_irq(18) 只命中第一个。 */
void virtio_mmio_raise_irq_by_gpa(u64 gpa_base);
/* 周期轮询 virtio-net RX 路径(填充 guest 已投递的 RX buffer,
* 有填充时自动注入 IRQ)。由 vmexit 的 preemption timer 路径调用。 */
void virtio_net_poll(void);
/* ===== VSCode Phase 2: virtio-input 后端 ===== */
/* Linux input event types/codes (subset, see <uapi/linux/input-event-codes.h>).
* We only need the few event types we actually forward from Deshab. */
#define VIO_INPUT_EV_KEY 0x01
#define VIO_INPUT_EV_REL 0x02 /* relative movement (mouse) */
#define VIO_INPUT_EV_ABS 0x03
#define VIO_INPUT_EV_SYN 0x00 /* sync marker */
#define VIO_INPUT_REL_X 0x00
#define VIO_INPUT_REL_Y 0x01
#define VIO_INPUT_BTN_LEFT 0x110
#define VIO_INPUT_BTN_RIGHT 0x111
#define VIO_INPUT_BTN_MIDDLE 0x112
/* virtio_input_event (8 bytes, little-endian). Matches Linux struct
* virtio_input_event in <uapi/linux/virtio_input.h>. */
struct virtio_input_event {
u16 type;
u16 code;
u32 value;
} __attribute__((packed));
/* 注册键盘+鼠标两个 virtio-input 后端(无设备时安全空操作)。
* 在 virtio_mmio_init 中调用。 */
void virtio_input_backend_init(void);
/* 推送键盘事件(type=EV_KEY, code=linux keycode, value=0/1/2)。
* value: 0=release, 1=press, 2=repeat。 */
void virtio_input_push_keyboard(u16 code, u32 value);
/* 推送鼠标事件。三种便捷封装:
* push_mouse_rel(axis, value) — 相对位移 (EV_REL, REL_X/REL_Y)
* push_mouse_button(btn, value)— 按键 (EV_KEY, BTN_LEFT/RIGHT/MIDDLE)
* push_mouse_syn() — 同步标记 (EV_SYN) */
void virtio_input_push_mouse_rel(u16 axis, i32 value);
void virtio_input_push_mouse_button(u16 btn, u32 value);
void virtio_input_push_mouse_syn(void);
/* 周期轮询:把挂起的输入事件排空到 guest eventq 已投递的空 buffer 中,
* 有事件投递时自动注入 IRQ。由 vmexit 的 preemption timer 路径调用。 */
void virtio_input_poll(void);
/* ===== VSCode Phase 3: virtio-gpu 2D 后端 ===== */
/* Scanout 查询结果:desktop 在 IDE attached 时读取,把 guest 图形 blit 到
* Deshab framebuffer 的 IDE host 区域。
* host_vaddr : scanout 影子缓冲的 host 虚拟地址(位于 graphics surface pool,
* 16MB,足够 1920×1080×4)。guest virtio-gpu 驱动经
* TRANSFER_TO_HOST_2D + RESOURCE_FLUSH 把帧合成到这里。
* width/height/stride : scanout 几何(XRGB8888,stride = width*4)
* dirty : 1 = 自上次查询以来有新帧合成(查询会清零该位)
* enabled : 1 = scanout 已绑定一个 2D resource(X server 已 SET_SCANOUT)
* dirty_x/y/w/h : Phase 7 累积 dirty rect(scanout 坐标系并集),
* 查询随 dirty 一并清零;dirty=0 时全为 0。
* desktop 据此只 blit 变化区域(尾部追加,ABI 兼容)。 */
struct virtio_gpu_scanout_info {
void *host_vaddr;
u32 width;
u32 height;
u32 stride;
u32 dirty;
u32 enabled;
u32 dirty_x;
u32 dirty_y;
u32 dirty_w;
u32 dirty_h;
/* P7.6: hardware cursor state(尾部追加,ABI 兼容)。
* cursor_visible=1 时 cursor_bitmap 指向 ARGB 像素(row stride=64),
* desktop 在 blit 后 alpha-blend 叠加到 IDE host 区域。 */
u32 cursor_visible;
u32 cursor_x;
u32 cursor_y;
u32 cursor_hot_x;
u32 cursor_hot_y;
u32 cursor_w;
u32 cursor_h;
void *cursor_bitmap;
};
/* 注册 virtio-gpu 2D 后端(slot 5 @ 0xF4005000 IRQ10)。
* 在 virtio_mmio_init 中调用。无 surface pool 时安全降级(scanout disabled)。 */
void virtio_gpu_backend_init(void);
/* 查询当前 scanout 状态(desktop blit 用)。返回 0 成功,-1 后端未就绪。 */
int virtio_gpu_get_scanout_info(struct virtio_gpu_scanout_info *out);
#endif /* UTSM_VIRTIO_MMIO_H */
+6 -1
View File
@@ -48,9 +48,14 @@ extern volatile struct vmexit_guest_regs g_guest_regs;
* handle_hlt 检查此标志:Linux guest HLT 时 park(推进 RIP,exit-to-host),
* self-test guest HLT 时 terminate。
* g_guest_parked: 1 = Linux guest 已 HLT 驻留,可被 linux_resume() 唤醒。
* linux_compat_service 通过此标志判断 guest 是否就绪。 */
* linux_compat_service 通过此标志判断 guest 是否就绪。
*
* g_xj380_guest_active: 1 = 当前运行的 guest 是 OpenXJ380 guest。
* handle_hlt 检查此标志:XJ380 guest HLT 时推进 RIP 并立即 vmresume
* (继续执行,不 park 不 terminate)。同一时刻只有一个 guest active。 */
extern volatile int g_linux_guest_active;
extern volatile int g_guest_parked;
extern volatile int g_xj380_guest_active;
/* ===== VM-Exit host 恢复点(vmm.c 定义,vmm.c / linux_boot.c / linux_resume.c 共用) =====
*
+4 -2
View File
@@ -73,10 +73,10 @@
#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_ES_LIMIT 0x4800
#define VMCS_GUEST_CS_LIMIT 0x4802
#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
@@ -95,6 +95,7 @@
#define VMCS_GUEST_PREEMPTION_TIMER 0x482E
/* 64-bit guest-state fields(与 PDPTR 共享编码,仅在 EPT 启用时为 EFER/PAT) */
#define VMCS_GUEST_LINK_POINTER 0x2800 /* P8.4: must be 0xFFFFFFFFFFFFFFFF */
#define VMCS_GUEST_IA32_EFER 0x2806
#define VMCS_GUEST_IA32_PAT 0x2804
#define VMCS_GUEST_IA32_PERF_GLOBAL_CTRL 0x2808
@@ -265,6 +266,7 @@ int vmx_enable(void);
int vmx_disable(void);
int vmx_vmcs_alloc(u64 *phys_out);
int vmx_vmcs_load(u64 phys);
int vmx_vmcs_clear(u64 phys); /* P8.4: VMCLEAR before VMLAUNCH */
u64 vmx_vmcs_read(u64 field);
void vmx_vmcs_write(u64 field, u64 value);
int vmx_vmlaunch(void);
+106
View File
@@ -0,0 +1,106 @@
#ifndef UTSM_XJ380_LOADER_H
#define UTSM_XJ380_LOADER_H
#include <utsm/types.h>
/* xj380_loader.h — OpenXJ380 (kernel.krl) guest loader for UTSM VMX non-root guest.
*
* 三内核架构路线 B:UTSM hypervisor 同时运行 Linux 与 OpenXJ380 两个 VM guest。
* 本模块把 OpenXJ380 编译产物 kernel.krl(固定链接 0xFFFFFFFF80000000 的
* ELF64 ET_EXEC)作为第二 guest 加载:
* - 解析 ELF PT_LOAD 段 → 装入 guest 物理内存
* - 构造 guest 初始页表(恒等 + HHDM + 内核区三区布局,同 XJ380 引导器契约)
* - 构造 BOOT_CONFIG / FrameBufferConfig / 伪 EFI_SYSTEM_TABLE 交接
* - 构造 MADT/FADT/HPET/MCFG 桩表(LAPIC/IOAPIC/HPET/MMCONF 均为幽灵地址)
* - 建立 EPT 映射(boot area / framebuffer / kernel / RAM)
*
* XJ380 内核侧启动契约(证据见 OpenXJ380/boot/include/boot.h 与
* OpenXJ380/kernel/main.cpp / memory/frame.cpp):
* - KernelMain(fbc, SystemTable, BootConfig) — 3 个 SysV 参数,引用退化为指针
* - BOOT_CONFIG 在入口立即被 memcpy 拷贝(main.cpp:281),原缓冲仅需入口可读
* - MemoryMap.Buffer / saved_mtrrs 是物理地址,内核自行 +0xffff800000000000
* - temp_stack[] 是 HHDM 虚拟地址数组(仅拷贝,1 CPU 时不 deref)
* - MADT 在清低半(main.cpp:392)之前按物理地址直读,之后经 HHDM 读
*
* 里程碑 M1:guest 启动至根文件系统挂载(预期在 mount_root 死循环处停止,
* 无虚拟块设备)。串口经 UTSM COM1 模拟可见 XJ380 启动全程。
*/
/* ===== XJ380 guest GPA 布局 =====
*
* 0x00000000-0x0000FFFF : 保留(XJ380 AP 跳板区 APU_BASE_ADDR=0x10000)
* 0x00020000-0x00033FFF : UTSM loader 私有 boot area(<1MB,内核不分配)
* page tables / GDT / ACPI 桩表 / 内存图 /
* BOOT_CONFIG / MTRR 缓冲 / stack / 伪 EFI 表
* 0x00100000-0x00400000 : framebuffer 1024x768x4(3MB,隐式保留)
* 0x01000000-0x02FFFFFF : kernel.krl 内核镜像窗口(32MB,隐式保留)
* 0x03000000-0x43000000 : guest RAM 1GB(内存图中唯一的 Conventional 区)
*/
#define XJ380_KERNEL_VADDR 0xFFFFFFFF80000000ULL /* XJ380 linker.ld 固定链接地址 */
#define XJ380_HHDM_OFFSET 0xFFFF800000000000ULL /* XJ380 HHDM 常量(hhdm.cpp:15) */
#define XJ380_BOOT_AREA_GPA 0x00020000ULL /* 128KB,避开 AP 跳板 0x10000 */
#define XJ380_PGT_GPA (XJ380_BOOT_AREA_GPA + 0x0000) /* 8 页: PML4/PDPT0/PD0-3/PDPT_K/PD_K */
#define XJ380_PGT_SIZE (8 * 4096)
#define XJ380_GDT_GPA (XJ380_PGT_GPA + XJ380_PGT_SIZE)
#define XJ380_MADT_GPA (XJ380_GDT_GPA + 0x1000)
#define XJ380_FADT_GPA (XJ380_MADT_GPA + 0x1000)
#define XJ380_MCFG_GPA (XJ380_FADT_GPA + 0x1000)
#define XJ380_HPET_GPA (XJ380_MCFG_GPA + 0x1000)
#define XJ380_MEMMAP_GPA (XJ380_HPET_GPA + 0x1000)
#define XJ380_BOOTCFG_GPA (XJ380_MEMMAP_GPA + 0x1000)
#define XJ380_MTRR_GPA (XJ380_BOOTCFG_GPA + 0x1000)
#define XJ380_STACK_GPA (XJ380_MTRR_GPA + 0x2000) /* 8KB guest 启动栈 */
#define XJ380_FAKEEFI_GPA (XJ380_STACK_GPA + 0x2000)
#define XJ380_FBC_GPA (XJ380_FAKEEFI_GPA + 0x1000)
#define XJ380_BOOT_AREA_SIZE (XJ380_FBC_GPA + 0x1000 - XJ380_BOOT_AREA_GPA) /* 至 FBC 结束 */
#define XJ380_BOOT_STACK_TOP (XJ380_STACK_GPA + 0x2000)
#define XJ380_GUEST_FB_GPA 0x00100000ULL /* 1MB */
#define XJ380_FB_WIDTH 1024
#define XJ380_FB_HEIGHT 768
#define XJ380_FB_BPP 4
#define XJ380_FB_SIZE ((u64)XJ380_FB_WIDTH * XJ380_FB_HEIGHT * XJ380_FB_BPP)
#define XJ380_GUEST_KERNEL_GPA 0x01000000ULL /* 16MB */
#define XJ380_GUEST_KERNEL_MAX (32 * 1024 * 1024) /* 内核窗口 16MB..48MB */
#define XJ380_GUEST_RAM_GPA 0x03000000ULL /* 48MB */
#define XJ380_GUEST_RAM_SIZE 0x40000000ULL /* 1GB (0x03000000..0x43000000) */
#define XJ380_GUEST_RAM_MIN 0x10000000ULL /* 降级下限 256MB */
/* BOOT_CONFIG.boot_flags(与 OpenXJ380 boot/include/boot.h 一致) */
#define XJ380_BOOT_FLAG_DISABLE_KMOD (1ULL << 2)
#define XJ380_BOOT_FLAG_SAFE_STORAGE_IO (1ULL << 3)
/* ===== 加载后的 XJ380 guest 状态 ===== */
struct xj380_guest_info {
u64 kernel_gpa; /* 内核段装入的 GPA 基址 */
u64 kernel_entry; /* 64 位入口(guest 虚拟地址 0xFFFFFFFF80000000+) */
u64 kernel_size; /* 内核跨度(memsz 上界 - 基址) */
u64 pgt_gpa; /* guest CR3(页表 GPA) */
u64 gdt_gpa; /* guest GDT GPA */
u64 ram_gpa; /* guest RAM GPA 基址 */
u64 ram_size; /* guest RAM 实际大小 */
u64 fb_gpa; /* framebuffer GPA */
int loaded; /* 1 = 加载成功 */
};
/* 在 Limine boot module 中查找 XJ380 内核(路径含 "xj380")。
* 返回模块数据指针,*size_out 为大小;未找到返回 NULL。 */
void *xj380_find_kernel_module(u64 *size_out);
/* 初始化 XJ380 guest:解析 ELF、分配内存、构造页表/GDT/ACPI 桩表/
* BOOT_CONFIG/内存图、EPT 映射。返回 0 成功,负值失败。 */
int xj380_loader_init(void);
/* 获取加载后的 guest 信息(未加载返回 NULL)。 */
const struct xj380_guest_info *xj380_get_guest_info(void);
/* 配置 VMCS 并 vmlaunch XJ380 guest。
* 前置条件:vmm_init() + xj380_loader_init()。
* guest 终止(异常/三重故障/0xCF9 重启)后返回;期间 host 挂起。
* 返回 0 表示 guest 终止返回,负值表示启动失败。 */
int xj380_launch(void);
#endif /* UTSM_XJ380_LOADER_H */