FUCK
This commit is contained in:
@@ -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 完整实现
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user