本次适配为 PRTOS 分离内核 架构 Type-1(裸金属)Hypervisor新增了 LoongArch64 平台支持。LoongArch64 是龙芯自主指令集架构,采用 64 位 RISC 设计,具备 LVZ (LoongArch Virtualization) 硬件虚拟化扩展。
适配实现了 双模式虚拟化:
| 模式 | Guest 特权级 | LVZ 依赖 | CSR 访问 | TLB 隔离 |
|---|---|---|---|---|
| 半虚拟化 | PLV3 (用户态) | 否 | IPE 陷入模拟 | Per-CPU refill 表 |
| 硬件虚拟化 | Guest PLV0 (LVZ GSTAT.PVM) | 是 | GSPR 陷入模拟 / 直通 | GID 标记的 TLB |
| 目录 | 文件数 | 说明 |
|---|---|---|
core/kernel/loongarch64/ |
17 | Hypervisor 内核实现 |
core/include/loongarch64/ |
16 | 架构相关头文件 |
core/klibc/loongarch64/ |
2 | C 运行时库 |
core/pbl/ |
2 | Partition Boot Loader |
user/libprtos/loongarch64/ |
2 | Hypercall 库 |
user/bail/loongarch64/ |
5 | BAIL 分区引导框架 |
user/bootloaders/rsw/loongarch64/ |
4 | Resident Software 引导加载器 |
| 示例目录 (6 个) | ~120 | FreeRTOS/Linux/Mixed/Virtio 示例 |
scripts/run_test.sh |
1 | 测试基础设施 |
在 setup.c 的 setup_arch_common() 中通过 CPUCFG[2] 第 10 位 (LVZP) 检测 LVZ 支持:
void detect_lvz(void) {
prtos_u32_t cfg2;
asm volatile("cpucfg %0, %1" : "=r"(cfg2) : "r"(2));
prtos_lvz_available = (cfg2 >> 10) & 0x1;
}
LVZ 初始化 (init_lvz()) 配置以下 Guest 管理 CSR:
| CSR | 配置 | 说明 |
|---|---|---|
GCFG |
MATC=ROOT, 异常托管位 | 配置 Guest 内存地址类型继承和异常托管策略 |
GTLBC |
USETGID=1 | 启用 TLB Guest ID 标记,实现硬件级 TLB 隔离 |
GSTAT |
清零 | 清除 Guest 状态 |
GINTC |
清零 | 清除 Guest 中断控制 |
Guest 进入 (JMP_PARTITION_LVZ 宏, asm.h):
- 设置
CSR.ERA= Guest 入口地址 - 设置
CSR.PRMD=0x4(PPLV=0, PIE=1) - 配置
GSTAT.GID和PVM=1(进入 Guest 模式) - 设置
GTLBC.TGID = GID(TLB 条目关联到此 Guest) - 写 LVZ 标志到
CSR.SAVE5 - 保存 Host SP 到
CSR.SAVE0 - 执行
ertn进入 Guest 模式
Guest 退出 (entry.S — _trap_from_lvz_guest):
当 Guest 执行特权操作或发生异常时,LVZ 硬件触发 VM Exit:
- 检查
GSTAT.PVM == 1确认是 LVZ Guest 退出 - 清除
GSTAT.PVM(退出 Guest 模式) - 清除
GTLBC.TGID = 0(后续 TLB 操作不影响 Guest 条目) - 设置
SAVE5 = 1(标记 LVZ 活跃) - 切换到 Host 栈 (从
SAVE0恢复) - 保存 Guest 寄存器上下文
- 进入 C 陷入处理器
loongarch64_trap_handler()
LVZ Guest 执行特权指令时产生 GSPR 异常 (ecode=0x1A),而非 IPE。处理流程:
- 从
CSR.BADI读取陷入指令 (非从 ERA 地址读取,避免 TLB 问题) - 解码指令类型:
- CPUCFG: 直通硬件值,屏蔽 PTW/LSX/LASX 特性位
- IOCSR 读写: 直通到硬件 (用于 EIOINTC 中断控制器访问)
- IDLE: 跳过 (推进 PC)
- CSR 操作: 调用
emulate_privileged_insn()统一处理
这是整个适配中最核心的函数 (~800 行), 实现了完整的 LoongArch64 CSR 陷入-模拟机制:
CSR 指令解码 (opcode 0x04):
CSRRD(rj==0):rd = guest_csr_read(ka, csr_num)CSRWR(rj==1): 交换 —old = read; write(new); rd = oldCSRXCHG(rj≠0,1): 掩码交换 —new = (old & ~mask) | (val & mask)
软件 CSR 影子寄存器: 在 struct kthread_arch 中维护了超过 60 个 Guest CSR 的影子副本:
| CSR 类别 | 主要寄存器 | 说明 |
|---|---|---|
| 基础控制 | CRMD, PRMD, EUEN, ECFG, ESTAT, ERA, BADV, BADI | Guest 异常和状态管理 |
| 异常入口 | EENTRY, TLBRENTRY | Guest 异常向量 |
| TLB 管理 | TLBIDX, TLBEHI, TLBELO0/1, ASID, PGDL/H, PWCL/H, STLBPS | Guest TLB 和页表配置 |
| TLB Refill | TLBRENTRY, TLBRBADV, TLBRERA, TLBRSAVE, TLBRELO0/1, TLBREHI, TLBRPRMD | TLB Refill 异常上下文 |
| 定时器 | TCFG, TVAL (计算型), CNTC, TICLR | Guest 定时器虚拟化 |
| DMW | DMW0-3 | Guest 直接映射窗口 |
| 杂项 | SAVE0-15, CPUID, PRCFG1-3 | 上下文保存和处理器配置 |
关键设计决策:
- EUEN 直通: Guest 写 EUEN 时直接操作硬件,实现 FP/LASX 上下文的惰性管理
- ECFG/CRMD 写触发中断检查: 每次写入后调用
check_guest_pending_irqs(),实现延迟虚拟中断的及时投递 - ERA 先行推进: 在处理虚拟中断投递前推进 ERA,确保中断处理器保存正确的返回地址
| 指令 | 模拟策略 |
|---|---|
TLBFILL |
翻译 Guest TLBELO → Host TLBELO,填充真实 TLB |
TLBWR |
同上,但检查 TLBIDX.NE 位 |
TLBSRCH |
简化实现:始终返回 NE=1 (未找到) |
TLBRD |
返回默认值 |
TLBCLR/TLBFLUSH |
invtlb 0x0 全部刷新 |
INVTLB |
解码操作数,调用 emulate_invtlb() |
translate_guest_tlbelo(): Guest TLB 条目地址翻译的核心:
- 从 Guest TLBELO 提取 GPA (Guest Physical Address)
- 以 1MB 为粒度在
tlb_refill_table[]中查找对应的 HPA - 构造 Host TLBELO: HPA + 强制 PLV=3 访问权限
- 若页面不在分区允许的内存区域内,返回 0 (拒绝访问)
完整模拟了 LoongArch64 的硬件页表遍历器:
- LDDIR: 从 BADV 中提取目录索引 (使用 PWCL/PWCH 配置的 Dir1-4 base/width),通过 DMW1 直接访问 Guest 物理内存加载页目录项
- LDPTE: 加载 PTE 到
guest_tlbrelo0/1,设置TLBREHI.PS
if (guest_in_tlb_refill):
guest_crmd ← guest_tlbrprmd # 从 TLB Refill 返回
else:
guest_crmd ← guest_prmd # 从普通异常返回
恢复后触发 check_guest_pending_irqs() 处理挂起虚拟中断。
deliver_virtual_irq() 模拟硬件异常入口序列:
- 保存
guest_crmd→guest_prmd(PPLV + PIE) - 保存当前 ERA →
guest_era - 清除
guest_crmd.IE,设置 PLV=0 - 计算向量化入口: VS>0 时
entry = eentry + (64 + hwirq) * vec_size— 匹配 Linux 的EXCCODE_INT_START=64约定 - 设置
regs->era为计算出的向量地址
check_guest_pending_irqs(): 当 guest_crmd.IE 开启且 guest_estat & guest_ecfg & 0x1FFF 非零时,调用 deliver_virtual_irq()。
指令: syscall 0 (LoongArch64 同步异常指令)
调用约定:
| 寄存器 | 用途 |
|---|---|
$a0 |
Hypercall 编号 (输入) / 返回值 (输出) |
$a1–$a5 |
参数 1–5 |
$a7 |
必须为 0 (区分 PRTOS hypercall 与 Linux syscall) |
Hypervisor 在 traps.c 中处理 SYS (ecode=0xB) 异常:
- 检查
$a7 == 0确认是 PRTOS hypercall - 从
$a0获取 hypercall 编号 - 分发到对应处理函数 (与其他架构共享 hypercall 表)
半虚拟化 Guest 运行在 PLV3,执行特权指令时触发 IPE 异常 (ecode=0xE)。处理流程与 GSPR 相同:
- 从
CSR.BADI读取陷入指令 - 解码并模拟 CSR 读写、TLB 操作、IOCSR 访问等
- 推进 Guest ERA
- 返回 Guest 继续执行
JMP_PARTITION 宏 (para-virt 路径):
- 设置
ERA = entry,PRMD = 0x7(PPLV=3, PIE=1) - 保存 Host SP 到
CSR.SAVE0 $a0 = PCT (Partition Control Table) 物理地址ertn进入 PLV3
半虚拟化中断投递 (fix_stack() in irqs.c):
- 保存
era,crmd,$a0,$a7到 PCT arch 字段 - 设置
irq_vector(高位标记:vector | 0x80000000) - 重定向
regs->era到pct->arch.trap_entry - Guest 中断处理完成后通过 IRET hypercall (nr=44) 恢复: 从 PCT 恢复
era,crmd,$a0,$a7
完全定制的 FreeRTOS 端口 (freertos_para_virt_loongarch64/):
| 组件 | 实现 |
|---|---|
| 上下文切换 | 自定义 portASM.S,256 字节栈帧,通过 IRET hypercall 返回 |
| 定时器 | syscall 0 调用 get_time/set_timer hypercall |
| 临界区 | set_irqmask/clear_irqmask hypercall |
| 异常入口 | 注册 _prtos_trap_dispatch 到 PCT trap_entry |
| 启动 | 自定义 start.S,保存 PCT 地址,初始化栈和 BSS |
硬件虚拟化模式下的 FreeRTOS 使用 完全未修改的原生代码:
| 对比项 | 半虚拟化 | 硬件虚拟化 |
|---|---|---|
| Guest 修改 | 完全定制端口 | 原生未修改代码 |
| 定时器 | Hypercall (set_timer) | 原生 CSR (TCFG/TVAL),LVZ 模拟 |
| 中断交付 | PCT trap_entry + IRET | 原生异常向量 |
| UART | Hypercall 或 MMIO | 直接 MMIO 直通 |
| 启动方式 | 自定义 start.S + PCT | 原生 start.S |
LoongArch64 平台的内存模型基于 DMW,这是与 ARM/RISC-V/x86 架构最显著的区别:
| DMW 窗口 | 虚拟地址段 | 物理映射 | MAT | PLV | 用途 |
|---|---|---|---|---|---|
DMW0 |
0x8000_xxxx_xxxx_xxxx |
PA = VA & 0xFFFF_FFFF_FFFF | SUC (非缓存) | PLV0+PLV3 | MMIO 设备访问 |
DMW1 |
0x9000_xxxx_xxxx_xxxx |
PA = VA & 0xFFFF_FFFF_FFFF | CC (缓存一致) | PLV0+PLV3 | 内核代码/数据访问 |
设计影响:
vm_map_page()是 空操作 — DMW 已覆盖全部物理地址空间read_by_pass_mmu_*()/write_by_pass_mmu_*()是简单的指针解引用- Hypervisor 不需要页表层次结构
- 内存访问公式:
VA_cached = PA | 0x9000_0000_0000_0000,VA_uncached = PA | 0x8000_0000_0000_0000
prtos_u64_t tlb_refill_table[CONFIG_NO_CPUS][4096]; // 每 CPU 4096 条目 × 1MB = 4GB 覆盖
每个条目是完整的 TLBELO 值 (64-bit) 或 0 (不可访问)。此表实现了 1MB 粒度的分区间内存隔离。
表的物理地址存储在 CSR.SAVE4 中,供 TLB Refill 处理器在 DA 模式下直接访问。
每次分区切入前调用,配置当前分区的内存视图:
- 清零表 — 所有页面对 PLV3 不可访问
- 映射 Hypervisor — 前 2MB 从
CONFIG_PRTOS_LOAD_ADDR起,只读 (V=1, D=0, PLV3, CC, G) - 映射分区内存区域 — 遍历 XML 配置的
physical_memory_areas:- 普通:
V|D|PLV3|CC|G - 只读:
V|PLV3|CC|G(无 D 位) - 非缓存:
V|D|PLV3|G(MAT=SUC)
- 普通:
- 映射 PCT 页 — 只读
- 写入
CSR.SAVE4— 更新 refill 表物理地址 - 全量 TLB 刷新 —
invtlb 0x0
运行在 DA 模式 (VA=PA, PLV0),仅使用 $t0/$t1:
地址分类:
| 地址类型 | 检测条件 | 处理 |
|---|---|---|
DMW1 (0x9000...) |
高 16 位 = 0x9000 |
提取 GPA,查 refill 表 |
DMW0 (0x8000...) |
高 16 位 = 0x8000 |
同上 |
DMW2 (0xa000...) |
高 16 位 = 0xa000 |
同上 |
直接 GPA (< 4GB) |
高 32 位 = 0 | 直接查 refill 表 |
| 未知 | 其他 | 填充 V=0,G=1 条目 (PS=14, 16KB) |
表查找算法:
- 计算 1MB 页面索引 (GPA >> 20)
- 偶数页: 加载
TLBRELO0 - 奇数页: 加载
TLBRELO1 - 设置
TLBREHI= 2MB 对齐基址 | PS=20 (1MB) - 执行
tlbfill
拒绝路径: 未映射地址填充 V=0, G=1, PS=14 (16KB) 条目。选择 PS=14 是因为匹配 Guest 内核的 STLBPS 配置,避免 Guest 后续 TLBFILL 时产生 Multi-hit 异常。
对于 PIL/PIS/PIF (ecode 1-3):
- DMW 地址: 提取 GPA,查 refill 表,填充 1MB TLB 条目
- 直接 GPA (
< 4GB): 同上 - Guest 虚拟地址: 通过
deliver_guest_exception()向 Guest 投递页错误异常
对于 PME (ecode 4, 页修改异常): 临时设置 D=1 允许写入,下次 TLB 刷新时恢复。
| 组件 | 地址 | 说明 |
|---|---|---|
| RSW | 0x1FE001E0 (PA, DA 模式) |
固件已初始化,直接 MMIO |
| Hypervisor | 0x8000_0000_1FE0_01E0 (DMW0 非缓存) |
通过 DMW0 非缓存窗口访问 |
| HW-virt Guest | 0x1FE001E0 (GPA) |
XML 配置中映射为 uncacheable 区域,直通 |
| Para-virt Guest | 通过 hypercall 或 DMW0 | 直接 MMIO 经 refill 表允许 |
硬件基础: LoongArch64 Stable Counter (100 MHz on QEMU virt)
get_timehypercall (nr=9): 读取 Stable Counterset_timerhypercall (nr=10): 设置定时器中断间隔
Guest 直接读写 CSR.TCFG/TVAL/TICLR,Hypervisor 模拟:
| Guest 操作 | 模拟行为 |
|---|---|
| 写 TCFG | 计算 deadline = rdtime.d() + interval,激活 Guest 定时器 |
| 读 TVAL | 返回 max(0, deadline - now) |
| 写 TICLR | 清除 guest_estat 中的 TI 位 |
| 定时器到期 | check_guest_timer(): 设置 guest_estat TI 位;周期模式下跳过已过期间隔 |
LVZ Guest 定时器: 通过 IOCSR syscall (PRTOS_EXT_TIMER) 直接编程硬件 TCFG。
LoongArch64 独有的 IOCSR 机制用于访问片上设备 (EIOINTC, IPI, PCH-PIC 等)。
对于 para-virt IPE 和 LVZ GSPR 陷入,IOCSR 读写指令 直通到硬件:
handle_iocsr_passthrough(insn, regs):
width = decode(insn[11:10]) // byte/half/word/dword
addr = regs[rj]
iocsrrd/iocsrwr(width, addr, &data)
写操作后调用 manage_hwi_state() 检查 EIOINTC ISR 是否被清除。
为 LVZ hw-virt Guest 提供类似 RISC-V SBI 的系统调用接口 (prtos_iocsr.c):
| Extension ID | 功能 | 说明 |
|---|---|---|
0x10 (CONSOLE) |
putchar(a0) |
直接 UART 写入 |
0x20 (TIMER) |
set_timer(a0) |
编程硬件 TCFG |
0x30 (IPI) |
send_ipi(mask, base) |
设置目标 vCPU 的 ipi_pending,跨 pCPU 发送物理 IPI |
0x40 (HSM) |
vcpu_start/stop/status |
管理 vCPU 生命周期 |
EIOINTC (Extended I/O Interrupt Controller) 是 LoongArch64 的高级中断路由器。
HWI (硬件中断) 处理流程:
- 硬件中断触发 ESTAT.IS[2:9]
- Hypervisor 陷入处理器:
- 屏蔽 ECFG 中对应位 (防止重入)
- 注入 到
guest_estat对应位 - 记录到
karch.hwi_injected位图
- Guest 处理中断 (通过 IOCSR 清除 EIOINTC ISR)
manage_hwi_state()检测 ISR 已清除后:- 重新启用 ECFG 对应位
- 清除
guest_estat和hwi_injected对应位
双分区 Virtio 架构 (virtio_linux_demo_2p_loongarch64/):
┌────────────────────────────────────────────────────┐
│ PRTOS Hypervisor (32MB) │
├─────────────────────┬──────────────────────────────┤
│ System Partition │ Guest Partition │
│ (hw-virt, 2 vCPU) │ (hw-virt, 2 vCPU) │
│ │ │
│ Linux + Backend │ Linux + Frontend │
│ Daemon │ Driver │
│ │ │
│ GPA: 0x80000000 │ GPA: 0xA0000000 │
│ 512MB RAM │ 512MB RAM │
└────────┬────────────┴──────────┬───────────────────┘
│ Shared Memory │
└───────────────────────┘
5 Regions @ 0xC0000000
共享内存区域:
| 设备 | 类型 | 基址 | 大小 | IPVI 门铃 |
|---|---|---|---|---|
| Net0 | bridge | 0xC0000000 |
1MB | 0 |
| Net1 | NAT | 0xC0100000 |
1MB | 1 |
| Net2 | p2p | 0xC0200000 |
1MB | 2 |
| Blk | file-backed | 0xC0300000 |
2MB | 3 |
| Console | char ring | 0xC0500000 |
256KB | 4 |
Backend 守护进程在 System Partition 的 Linux 用户空间运行: 通过 /dev/mem + mmap() 映射共享内存区域,使用 PRTOS Hypervisor 接口 (prtos_hv_raise_partition_ipvi()) 通知 Guest。
LoongArch64 的 SMP 启动基于 IOCSR 邮箱:
- BSP 调用
write_remote_mailbox(cpu, _secondary_start_addr):- 将
_secondary_start物理地址写入目标 CPU 的IOCSR CORE_BUF_20(地址0x1020) - 通过
IOCSR_MAIL_SEND(0x1048) 发送
- 将
- 通过
IOCSR_IPI_SEND(0x1040) 发送 IPI 唤醒目标 CPU - 目标 CPU 从 QEMU
slave_boot_code轮询循环中醒来 _secondary_start中执行: 配置 DMW → DA→PG 切换 → 调用init_secondary_cpu()
prtos_hsm_vcpu_start(target_vcpuid, start_addr, opaque):
- 验证目标 vCPU 在分区
num_of_vcpus范围内 - 存储
hsm_entry和hsm_opaque到目标 kthread 的karch - 调用
setup_kstack()设置初始栈帧 - 设置
KTHREAD_READY_F,清除KTHREAD_HALTED_F
QEMU virt firmware
↓
RSW (Resident SW) — DA 模式, PA
├── CPU 0: rsw_main() → 解包 container.bin → 跳转到 PRTOS 入口
└── CPU 1-3: 轮询邮箱 (PA 0x100 + cpuid*8)
↓
PRTOS Hypervisor — start.S
├── BSS 清零 (PA)
├── DMW0 临时跳板 (VSEG=0x0, 覆盖当前 PC)
├── DMW1 最终窗口 (VSEG=0x9, CC, PLV0+PLV3)
├── DA→PG 模式切换
├── 虚拟空间跳转 (jr $s1 → _virtual_start)
├── DMW0 重配置 (VSEG=0x8, SUC, PLV0+PLV3)
├── 异常入口设置 (EENTRY, TLBRENTRY)
├── ECFG 配置 (TI, IPI, HWI0-7)
└── setup_kernel(cpu_id=0, idle_kthread)
├── 通用初始化 (调度器, 内存, 分区)
├── 架构初始化 (detect_lvz, init_lvz, timer)
├── SMP 启动 (IOCSR 邮箱 → IPI 唤醒)
│ └── 副 CPU: _secondary_start → DMW → PG → init_secondary_cpu()
└── 调度器启动 → JMP_PARTITION / JMP_PARTITION_LVZ
这是 LoongArch64 启动中最精妙的部分。由于 DA→PG 切换是单条指令完成的 (写 CRMD),需要确保切换瞬间的取指不会失败:
- 配置 DMW0:
VSEG=0x0— 映射当前 DA 模式的地址范围 - 配置 DMW1:
VSEG=0x9— 最终虚拟地址窗口 csrwr CRMD(PG=1, DA=0) — 此刻 PC 仍在低地址,DMW0 保证取指继续jr $s1— 跳转到0x9000...地址空间- 重配置 DMW0 为最终值 (
VSEG=0x8, SUC)
- 运行在 DA 模式,无 DMW 需求
- CPU 0 执行
rsw_main(),解包 container.bin (PRTOS core + 分区镜像) - 副 CPU 轮询物理地址
0x100 + cpuid*8的邮箱
分区引导框架:
- 从
$a0获取 PCT 物理地址 - 通过
syscall 0(get_vcpuid) 获取 vCPU ID - vCPU 0 清零 BSS
- 每 vCPU 独立栈:
sp = _stack + (vcpuid + 1) * STACK_SIZE - 调用链:
init_libprtos()→init_arch()→setup_irqs()→partition_main()
提供 _PRTOS_HCALL0 ~ _PRTOS_HCALL5 宏,封装 syscall 0 指令。特殊 hypercall:
- IRET (nr=44): 从 PCT 恢复
era,crmd,$a0,$a7 - RAISE_TRAP (nr=45): 向分区注入陷入
linux_4vcpu_1partion_loongarch64/start.S:
- 写
'V'到 UART (验证标记) - 设置 Linux 启动协议参数:
a0=0(non-EFI),a1=0(cmdline forced),a2=0(no EFI table) - 跳转到内核入口:
_linux_image_start + 0x1622000(kernel_entry offset) vmlinux.bin通过.incbin嵌入在 2MB 对齐偏移处
| 测试用例 | 类型 | 超时 | 验证标准 |
|---|---|---|---|
example.001 ~ example.009 |
BAIL 基础 | 30s | 标准输出匹配 |
helloworld |
单分区 | 30s | 标准输出匹配 |
helloworld_smp |
SMP | 30s | 标准输出匹配 |
freertos_para_virt_loongarch64 |
半虚拟化 | 20s | BAIL 标准测试 |
freertos_hw_virt_loongarch64 |
硬件虚拟化 | 30s | ≥5 个 "Stop" 行 |
linux_4vcpu_1partion_loongarch64 |
HW-virt Linux | 600s | Shell 提示 + 3 副 CPU + init 启动 |
mix_os_demo_loongarch64 |
混合 OS | 420s | Shell 提示 + [RTOS] 输出 + ≥2 副 CPU |
virtio_linux_demo_2p_loongarch64 |
Virtio | 120s | 双分区启动 + SMP + 共享内存配置 |
Linux 相关测试采用 文件型 chardev 轮询 方案替代 pexpect/PTY:
- QEMU 启动参数:
-serial chardev:s0 -chardev file,id=s0,path=... - 测试循环: 每 5-10 秒
grep日志文件检查预期模式 - 原因: QEMU LoongArch64 PTY 串口仅显示 RSW 引导消息,NS16550 UART 输出未映射到 PTY
QEMU 配置: qemu-system-loongarch64 -machine virt -cpu max -smp 4 -m 2G
======================================
Test Report [loongarch64]
======================================
Total: 32 Pass: 16 Fail: 0 Skip: 16
======================================
16 个 LoongArch64 相关测试全部 PASS,16 个其他架构测试正确 SKIP。
| 特性 | x86 (para) | AArch64 (HW) | RISC-V 64 (HW) | LoongArch64 |
|---|---|---|---|---|
| 虚拟化扩展 | — | EL2/vGIC | H-ext/SBI | LVZ/IOCSR |
| 半虚拟化 | Port I/O + syscall | smc/hvc | ecall | syscall 0 + $a7=0 |
| 地址翻译 | 页表 (EPT/shadow) | 页表 (stage-2) | 页表 (G-stage) | DMW + TLB refill 表 |
| TLB 管理 | 硬件 refill | 硬件 refill | 硬件 refill | 软件 refill (1MB 粒度) |
| 中断控制器 | APIC | vGIC | PLIC/AIA | EIOINTC 直通 |
| 定时器 | PIT/HPET | Generic Timer | mtime/mtimecmp | Stable Counter + TCFG |
| 内核 VA 映射 | 页表 | 页表 | 页表 | DMW (无页表) |
| SMP 启动 | SIPI | PSCI | SBI HSM | IOCSR 邮箱 + IPI |
| Linux 镜像格式 | bzImage | Image | Image | vmlinux → vmlinux.bin |
- DMW PLV3 启用: 需要同时为 PLV0 (Hypervisor) 和 PLV3 (Guest) 启用 DMW,Guest 可使用
0x8000.../0x9000...内核 VA - TLB Refill DA 模式: TLB Refill 处理器运行在 DA 模式 (VA=PA),需要在物理地址空间维护 refill 表
- BADI 指令读取: 从
CSR.BADI读取陷入指令而非 ERA 地址,避免 Guest TLB 上下文中的 TLB Miss - V=0 Deny 条目 PS 匹配: 拒绝条目使用 PS=14 (16KB) 匹配 Guest STLBPS,防止 Multi-hit 异常
- IOCSR 双重角色: 既是设备访问接口 (EIOINTC, IPI),又是 SMP 启动的邮箱机制
- DA→PG 跳板: 需要临时 DMW0 (VSEG=0x0) 保证模式切换时的连续取指
| 组件 | 行数 | 占比 |
|---|---|---|
陷入处理与指令模拟 (traps.c) |
~1,600 | 36% |
汇编入口/启动 (entry.S, start.S, head.S) |
~1,100 | 25% |
| 头文件和定义 | ~1,200 | 27% |
| MMU/TLB 管理 | ~300 | 7% |
| 其他 (timer, irqs, smp, setup) | ~500 | 5% |
| Hypervisor 核心合计 | ~4,700 | 100% |
用户态和示例代码另外约 40,000 行 (含 FreeRTOS 端口、Linux 示例、Virtio 框架)。
| 文件 | 说明 |
|---|---|
core/kernel/loongarch64/traps.c |
陷入处理器和完整指令模拟 (~1,600 行) |
core/kernel/loongarch64/entry.S |
异常入口/退出汇编 (~590 行) |
core/kernel/loongarch64/start.S |
BSP/AP 启动代码 (~440 行) |
core/kernel/loongarch64/mmu.c |
Per-CPU TLB refill 表管理 |
core/kernel/loongarch64/prtos_iocsr.c |
IOCSR syscall 接口 (类 SBI) |
core/kernel/loongarch64/smp.c |
IOCSR 邮箱 SMP 启动 |
core/kernel/loongarch64/timer.c |
Stable Counter 定时器 |
core/include/loongarch64/asm.h |
CSR 定义和 JMP_PARTITION 宏 |
core/include/loongarch64/kthread.h |
60+ Guest CSR 影子寄存器结构 |
user/bail/examples/freertos_para_virt_loongarch64/ |
FreeRTOS 半虚拟化完整端口 |
user/bail/examples/freertos_hw_virt_loongarch64/ |
FreeRTOS 硬件虚拟化 (原生) |
user/bail/examples/linux_4vcpu_1partion_loongarch64/ |
Linux 4vCPU 硬件虚拟化 |
user/bail/examples/mix_os_demo_loongarch64/ |
Linux + FreeRTOS 混合 OS |
user/bail/examples/virtio_linux_demo_2p_loongarch64/ |
双分区 Virtio 设备虚拟化 |











评论