裸机 hypervisor 的系统组织——无 OS 时系统如何自组装

裸机 VT(Intel VT-x)程序没有 Linux 的 init、没有 RTOS 的调度器、没有 Qt 的事件循环——它必须自己建立整个系统:从硬件抽象接口开始,把 CPU/VMX/Memory/EPT/Interrupt/Communication/Hook 组织成子系统,再由 hypervisor_main() 完成初始化与组装。这一条线是「系统组织」最纯粹的案例:没有操作系统替你组装,一切组装责任都落在程序自己身上。同时它展示了两个重要方法:MVP 增量开发(先跑通最小闭环再逐步加子系统)与结构/流程/状态三维理解(只看目录看不到系统怎么运行)。


一、裸机 VT 程序本身就是一个小型系统

先不要从「代码文件夹」开始,而是从系统职责开始。假设是 x86-64 Intel VT-x 裸机 hypervisor(EFI/bootloader 进入后自己建立 VMX 环境,最终启动 Windows/Linux guest):

1
2
3
4
5
6
7
8
9
10
11
VT Hypervisor
├── Boot
├── CPU / VMX
├── Memory
├── Interrupt
├── VM
├── EPT
├── VCPU
├── Device / I/O
├── Communication
└── Debug / Hook

这些不能全部叫「层」——它们更准确地说是子系统(Memory、VMX、EPT、Interrupt、VCPU 是并列的系统能力)。

最底层:硬件抽象接口

裸机没有 Linux/Windows 提供 malloc/thread/socket/driver,所以首先要自己建立最基本的硬件接口:

1
2
Platform
├── CPU / Memory / Serial / PCI / Interrupt / Timer
1
2
3
cpu.read_msr();  cpu.write_msr();
memory.allocate_page(); memory.map();
serial.write();

MSR、CR0/CR3/CR4、CPUID、APIC、页表、物理内存是硬件/CPU 能力read_msr() / write_msr() / allocate_page() / map() 是你封装出来的接口


二、子系统拆解

VMX 子系统

1
2
VMX:VMXON / VMCS / VM Entry / VM Exit / VMX Controls / VCPU
VCPU → VMCS → VM Entry → Guest → VM Exit → Hypervisor

这是一个完整的虚拟化子系统

Memory / EPT 子系统

1
2
3
Memory:Physical Memory / Page / Page Table / EPT / Mapping / Memory Pool

Guest Virtual Address → Guest Page Table → Guest Physical Address → EPT → Host Physical Address

EPT 本身又可以拆成组件:EPT PML4 / PDPT / PD / PT / Entry,而 ept.map() / ept.unmap() / ept.translate() 是对这些组件封装出来的接口——完全对应「组件 → 封装 → 接口」。

VM Exit 子系统

1
2
3
VM Exit
├── Exit Dispatcher
├── CPUID Handler / MSR Handler / EPT Violation Handler / IO Handler / Exception Handler

这里就出现前面一直在讨论的「解析 → 分发 → 处理」:

1
VM Exit → 读取 VMCS Exit Reason → Dispatcher → EPT Violation Handler → Memory/Hook Service

注意:Dispatcher 是 VM Exit 子系统中的组件/机制,而不是整个 VT 系统的「控制器层」。

Hook 子系统(VT Debugger/Protector)

1
2
3
Hook:EPT Hook / Execute Hook / Read Hook / Write Hook / Page Permission / Hook Manager

Guest → 访问被保护页面 → EPT Violation → VM Exit → EPT Handler → Hook Manager → 执行对应动作

「让程序运行后不能被读写」可以成为一个应用功能/策略,而不是直接塞进 EPT 实现里。

Communication 子系统

Windows 驱动或其他程序要控制 hypervisor:

1
2
3
Communication:Transport / Message / Command / Request / Response / Dispatcher

Windows Driver → Command → Communication → Command Dispatcher → Service

READ_MEMORY / WRITE_MEMORY / PROTECT_PAGE / HOOK / UNHOOK 属于上层功能。

Service 层

1
2
3
Service:MemoryService / ProcessService / HookService / DebugService / ProtectionService

ProtectProcessMemory() 可能组装:ProtectionService → Process → Memory → EPT → VMX

Service 是使用多个底层子系统完成业务功能的地方——与服务器分析的结论一致。

完整 VT 系统结构

1
2
3
4
5
6
7
8
9
10
VT Hypervisor
├── Boot
├── Platform CPU / Memory / Interrupt / Serial
├── Virtualization VMX / VCPU / VMCS / VM Exit
├── Memory Physical Memory / Page Table / EPT
├── Interrupt / Event
├── Device / I/O
├── Communication Message / Command / Dispatcher
├── Hook EPT Hook / Hook Manager
└── Service Memory / Debug / Hook / Protection

三、谁组装?——裸机最重要的问题

裸机没有 Linux init / RTOS scheduler / Qt event loop,所以必须自行有一个系统启动/组装流程

1
2
EFI → Hypervisor Entry → CPU 初始化 → Memory 初始化 → VMX 初始化
→ EPT 初始化 → Interrupt 初始化 → Communication 初始化 → 启动 BSP/Guest → 进入运行状态
1
2
3
4
5
6
7
8
9
10
11
12
int hypervisor_main()
{
platform::initialize();
memory::initialize();
vmx::initialize();
ept::initialize();
interrupt::initialize();
communication::initialize();

vm::start();
run();
}

hypervisor_main() 就非常接近一直在寻找的那个「Server.cpp 到底在哪里?」——它就是裸机系统的组装入口(Composition Root)。

不要把所有东西都叫「层」

1
2
3
VT System
├── Layers Platform Interface / Service Interface / Communication API
└── Subsystems VMX / EPT / Interrupt / Communication / Hook

每个子系统内部:Subsystem → Components → Interfaces。最后由 hypervisor_main() 完成初始化 + 组装

VT Hypervisor 是系统;VMX、EPT、Memory、Interrupt、Communication、Hook 是子系统;子系统内部由组件构成;组件通过接口暴露能力;Service 负责跨子系统组织业务;启动入口负责初始化和组装整个系统。


四、算法属于子系统内部,不是独立层

VT 和 Linux 都有大量算法,但通常不会单独放一个 Algorithm/ 目录

1
2
3
4
5
EPT          → 页表建立/遍历/地址转换算法
Memory → 页分配、回收、映射算法
VM Exit → Exit Reason 分发逻辑
Scheduler → VCPU 调度算法
Hook → Hook 状态管理、地址匹配等算法

它们通常属于相应子系统内部。这与「功能 = 算法 + 数据结构 + 接口」不冲突——两个维度不同:

1
2
系统如何组织:System → Subsystem → Component
一个功能如何实现:Function → Algorithm → Data Structure → Interface

假设 Memory 子系统:

1
2
3
组件:Page / PageTable / MemoryPool / EPT
接口:allocate() / free() / map() / translate()
算法:page allocation / page table walk / address translation

它们共同构成 Memory Subsystem。

算法是实现功能的组成部分,不一定是系统架构中的独立层。 这就是为什么 Linux 和 VT 裸机都有大量算法,但没有必要存在一个统一的 Algorithm 层。


五、结构 / 流程 / 状态 三维理解

对于裸机 VT 这种系统程序,这样理解比单纯看「分层目录」更准确。

1. 生命周期(启动 → 运行 → 结束)

1
2
3
启动 → Boot → Platform 初始化 → Memory 初始化 → VMX 初始化
→ EPT 初始化 → Interrupt 初始化 → Communication 初始化
→ 创建/初始化 VCPU → 启动 Guest → 运行状态 → 关闭/退出 → 释放资源 → 结束

回答:这个系统从出生到死亡经历什么阶段?

2. 运行中的流程(一次事件怎么走)

1
2
Guest Running → VM Exit → 读取 Exit Reason → Dispatcher → 对应 Handler
→ Service/Subsystem → 修改 VMCS/EPT/Memory → VM Entry → Guest Running

回答:系统运行的时候,一件事情经过哪些组件? 这与服务器的「接收 → 解析 → 分发 → 调用功能」是同一类动态处理链

3. 状态变化

1
2
VCPU: Created → Initialized → Running → VM Exit → Handling → VM Entry → Running
Hook: Uninstalled → Installing → Installed → Triggered → Handling → Installed → Uninstalled

回答:某个对象/子系统的状态怎么变化?

三者合起来

1
2
3
4
系统
├── 结构:有什么(Component / Subsystem / Interface)
├── 流程:怎么运行(Event / Flow)
└── 状态:怎么变化(State / Transition)

VT 程序本质上是一个持续运行的状态机(Hypervisor Running ↔ Guest Running ↔ VM Exit ↔ Handling ↔ VM Entry),只看目录只能知道「有什么」,不知道「什么时候初始化、谁先初始化、谁触发 VM Exit、处理完进入哪里」——这些必须通过生命周期 + 运行流程 + 状态变化理解。

以后分析陌生大型程序的四个问题

1
2
3
4
① 结构:系统由什么组成?子系统是什么?组件是什么?
② 接口:组件之间怎么交互?提供什么接口?谁调用谁?
③ 生命周期:怎么启动?怎么初始化?怎么运行?怎么停止?
④ 动态行为:事件发生 → 谁接收?谁解析?谁分发?谁执行?状态怎么改变?

结构看「有什么」,接口看「怎么连接」,流程看「怎么运行」,状态看「怎么变化」。 对于裸机 VT,动态流程甚至比目录分层更重要。


六、MVP 增量开发:先跑通最小闭环,再逐步加子系统

MVP 不应该先按目录做

先定义最小生命周期:

1
启动 → 初始化 → 进入运行 → 处理一次事件 → 继续运行 → 退出

最小 VT MVP 具体化:

1
2
EFI → Hypervisor Entry → CPU/VMX 初始化 → 建立最小 VMCS → 启动 Guest
→ VM Entry → Guest 执行 → VM Exit → 读取 Exit Reason → 处理 → VM Entry → 继续 Guest → 退出

这已经是一个完整的 VT 系统闭环

MVP-0:第一版可以极其小

1
2
3
4
5
6
Boot(Entry)
VMX(VMXON / VMCS / VM Entry / VM Exit)
Guest(最小 Guest)
Exit Handler(处理一个 Exit Reason)

执行流:Entry → VMXON → VMCS Setup → VMLAUNCH → Guest → VM Exit → Exit Handler → 处理 → VMRESUME → Guest

先证明 VT 的基本闭环成立。 此时根本不需要 EPT / Communication / Hook / Windows Driver / Process / Debug / Protection。

逐步添加子系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MVP-0:VMX 闭环
Boot → VMX → Guest ↕ VM Exit
目标:能启动、进入 Guest、产生 VM Exit、恢复 Guest

MVP-1:Memory
Boot → Memory Init → VMX Init → EPT Init → Guest → VM Exit → EPT Handler → VM Resume

MVP-2:Interrupt / Exception
Guest → VM Exit/Interrupt → Dispatcher → Handler → VM Resume

MVP-3:Communication
Windows Driver/Host → Command → Communication → Dispatcher → Service
先只支持 PING,不要一开始就做 READ_MEMORY / WRITE_MEMORY / HOOK / ...

之后才添加真正的业务子系统
Service:MemoryService / DebugService / HookService / ProtectionService

每次增加子系统都遵循一个原则

新增一个子系统,不只是把代码目录加进去,而是要把它接入生命周期和执行流。

例如加入 Communication,不能只是 Communication/ command.cpp + dispatcher.cpp,而应该同时增加:

1
2
3
启动流:Boot → Communication::initialize() → Running
执行流:Receive → Decode → Dispatch → Service → Response
结束流:Shutdown → Communication::shutdown()

每个子系统至少要回答:

1
2
3
4
5
6
1. 谁初始化我?
2. 我依赖谁?
3. 我运行时什么时候被调用?
4. 谁调用我?
5. 我的状态怎么变化?
6. 谁关闭我?

最终得到的是「逐步生长的系统」

1
2
3
4
5
MVP 0:        VMX → Guest ↕ VM Exit
MVP 1: Memory → VMX → Guest ↕ VM Exit
MVP 2: Memory ─ VMX ─ Interrupt → Guest ↕ VM Exit
MVP 3: Memory ─ VMX ─ Interrupt → Guest ↕ VM Exit → Dispatcher ↑ Communication
最终: Platform(Memory/VMX/Interrupt/EPT/Communication/Hook/Debug/Protection) → Service → Guest

这就是一种非常典型的 Vertical Slice(垂直切片)式增量开发

不是先把每一层全部写完,再开始运行;而是先让一个最小的端到端执行链跑起来,然后不断向这个闭环中增加能力。

每个阶段维护三份东西

1
2
3
4
5
6
7
8
        MVP
┌───────┼───────┐
生命周期 执行流 状态
Start Event State
Init ↓ ↓
Run Handler Transition
Exit ↓
Result

第一阶段:

1
2
3
生命周期:Start → Init → Run → Exit
执行流: Entry → VMXON → VM Entry → VM Exit → VM Resume
状态: OFF → INITIALIZED → RUNNING → EXIT_HANDLING → RUNNING → STOPPED

第二阶段加入 EPT,这三张图一起扩展

系统不是把子系统放进目录里就完成了,而是把子系统接入生命周期、执行流和状态机,整个东西才真正成为一个可运行的系统。


七、启动流程与 EFI 组装

先纠正一个关键点

如果 VT 裸机程序真的 return 回 UEFI,那么它自己就结束了,不可能同时继续驻留并控制随后启动的操作系统。

要做的是:

1
2
UEFI → 加载VT EFI/bootloader → 初始化 VT → 准备 Guest OS
→ 进入 VMX → 把 OS 作为 Guest 启动 → VT 程序继续驻留 → Guest OS 正常运行

而不是:

1
UEFI → VT → return → UEFI → Windows   (VT 已经退出,不可能控制 Windows)

MVP 的真实启动流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UEFI Firmware
↓ Boot
EFI Loader / Hypervisor EFI
├── 获取 UEFI System Table
├── 获取 Memory Map
├── 获取 OS / Guest 镜像
└── 准备启动环境

ExitBootServices()

hypervisor_main()
├── CPU 初始化
├── Memory 初始化
├── VMX 初始化
├── EPT 初始化
├── VMCS 初始化
└── Guest 初始化

VMLAUNCH

Guest OS(正常运行 ↔ VM Exit → VM Exit Handler → Dispatcher → Handler → VMRESUME → Guest OS)

这才是真正的 UEFI → Hypervisor → Guest OS 生命周期。

代码目录(MVP)

1
2
3
4
5
6
7
hypervisor/
├── boot/ efi_main.cpp / loader.cpp / memory_map.cpp
├── platform/ cpu.cpp / msr.cpp / memory.cpp
├── vmx/ vmx.cpp / vmxon.cpp / vmcs.cpp / vm_entry.cpp / vm_exit.cpp
├── memory/ page.cpp / ept.cpp
├── guest/ guest.cpp / guest_memory.cpp
└── main.cpp

关键不是目录,而是 boot/efi_main.cpp → main.cpp → vmx/ → guest/ 形成一条真正的启动执行流

最外层的 EFI 入口

1
2
3
4
5
6
7
8
9
10
11
12
EFI_STATUS EFIAPI efi_main(
EFI_HANDLE image,
EFI_SYSTEM_TABLE* system_table)
{
// 1. 保存 UEFI 环境
// 2. 初始化自己的 boot 环境
// 3. 获取内存地图
// 4. 准备 Guest
// 5. 退出 Boot Services

return hypervisor_main(...);
}

boot/efi_main.cpp 不是 VMX 子系统,它负责把 UEFI 提供的启动环境转换成 Hypervisor 可以运行的环境

系统组装入口

1
2
3
4
5
6
7
8
9
10
int hypervisor_main(const BootInfo& boot)
{
platform::initialize(boot);
memory::initialize(boot);
vmx::initialize();
ept::initialize();
guest::initialize(boot);
vmx::launch_guest();
return 0;
}

这就是前面一直在寻找的「Server.cpp 那种组装整个系统的代码」——System Composition Root(系统组装入口)

VMXON 在哪里?属于 VMX 子系统

vmx::initialize() 内部才执行具体 VMX 初始化:

1
2
3
4
5
6
7
8
9
void vmx::initialize()
{
check_vmx_support();
prepare_cr0_cr4();
allocate_vmxon_region();
write_vmx_revision_id();
vmxon();
initialize_vmcs();
}

hypervisor_main() 不应该自己写一堆 VMXON 细节,它只负责 vmx::initialize();——这就是「组装和组件实现分开」。

VMCS 在哪里?

VMXON 成功后:VMXON → VMCS 创建 → VMCS 初始化(Guest State / Host State / Execution Controls / Exit Controls / Entry Controls)

1
2
3
4
5
6
7
8
9
10
11
void Vmcs::initialize(...)
{
vmclear(...);
vmptrld(...);

setup_guest_state();
setup_host_state();
setup_execution_controls();
setup_exit_controls();
setup_entry_controls();
}

组装关系:hypervisor_main() → vmx::initialize() → vmx::create_vmcs() → guest::initialize() → vmx::launch_guest()

.bin → .img 到底是什么关系?

1
2
hypervisor.bin = 一段机器代码
disk.img = 一个模拟磁盘(里面可以有 EFI System Partition)

不是 bin 直接变成 img,而是:

1
2
C/C++/ASM → Compile → Object → Link → EFI executable/binary
→ 放进 FAT EFI System Partition → 创建 disk image → disk.img

UEFI 固件看到 EFI/BOOT/BOOTX64.EFI 之后才能把它作为 UEFI application 加载。

最适合 MVP 的 Guest

1
2
3
4
5
guest_start:
mov ...
nop
hlt
jmp guest_start

甚至专门让它触发一个 VM Exit(比如 CPUID),验证 UEFI → Boot → VMX → VMCS → VMLAUNCH → Guest → VM Exit → Handler → VMRESUME → Guest 整个生命周期成立。

迭代路线

1
2
3
4
5
6
7
MVP 0: 最小 Guest(验证 VMXON/VMCS/VM Entry/VM Exit)
MVP 1: 加入 EPT(Guest Memory → EPT → EPT Violation → VM Exit)
MVP 2: 加入 Interrupt
MVP 3: 加入 Communication
MVP 4: 加入 Memory Read/Write Service
MVP 5: 加入 Hook
MVP 6: 尝试真实 OS Guest

如果目标最终是「VT 启动后接管并启动现有 Windows」,应该把它作为后续阶段单独设计;MVP 最好先用一个极小 Guest 把 UEFI → VMX → VM Entry → VM Exit → VM Resume 的完整闭环跑通


八、这一条线的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
拆解与组织(主题):
01 元原则
02 边界
03 游戏拆解与组装(GameApplication 组装)
04 ECS 与注册机制(运行时注册)
05 只有组件的子系统如何被加载(接口化/框架化)
07 嵌入式与单片机的分层(Driver=接口 / RTOS=框架)
08 Linux 的分层与组装(三种组装:编译期/启动期/注册)
09 裸机 hypervisor 的系统组织(本线)
10 微服务与组装边界
11 软件组织知识与四个层次(收束)

最小闭环(主题):
本线的 MVP 增量开发 = 最小闭环在裸机系统上的展开:
先让最小的端到端执行链跑起来(闭环成立),再逐步加子系统
03-游戏开发的最小闭环 已经用过同样的方法

与最小闭环主题的关系:MVP-0「先证明基本闭环成立」就是最小闭环——最小输入(启动)+ 处理(VMXON/VM Entry)+ 输出(Guest 运行/VM Exit)+ 验证(能恢复 Guest)= 一个可运行验证的闭环。之后每一步都是闭环半径的扩大。


收束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
裸机 VT = 最纯粹的系统组织案例:
没有 OS 替你组装,一切组装责任在 hypervisor_main()

子系统拆分:
Platform / VMX / Memory-EPT / Interrupt / Communication / Hook / Service
每个子系统内部:组件 → 接口 → 算法(算法属于子系统,不是独立层)

组装入口:
hypervisor_main() = Composition Root
初始化 + 组装各子系统 → 启动 Guest → 进入运行

三维理解:
结构(有什么)/ 流程(怎么运行)/ 状态(怎么变化)
只看目录看不到「谁先初始化、谁触发 VM Exit、处理完进入哪里」

MVP 增量开发:
先跑通最小闭环(VMXON → VM Entry → VM Exit → VM Resume)
再逐步加子系统(Memory → Interrupt → Communication → Service)
每次加子系统 = 接入生命周期 + 执行流 + 状态机

EFI 启动链:
UEFI → EFI Loader → ExitBootServices → hypervisor_main → VMLAUNCH → Guest OS
hypervisor 在 Guest OS 运行期间继续存在(不会 return 回 UEFI)

裸机 hypervisor 的系统组织 = 无 OS 时系统自组装:子系统拆解 + hypervisor_main 组装入口 + MVP 增量开发 + 启动链。 它是「拆解与组织」「最小闭环」「入口组装」三条原则在极端环境(没有任何操作系统帮助)下的完整验证。