PRTOS Hypervisor — LoongArch64 平台适配技术方案


1. 概述

本次适配为 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 测试基础设施

2. 硬件虚拟化 (Hardware-Assisted Virtualization)

2.1 LVZ 扩展检测与初始化

在 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 中断控制

2.2 Guest 进入与退出

Guest 进入 (JMP_PARTITION_LVZ 宏, asm.h):

  1. 设置 CSR.ERA = Guest 入口地址
  2. 设置 CSR.PRMD = 0x4 (PPLV=0, PIE=1)
  3. 配置 GSTAT.GID 和 PVM=1 (进入 Guest 模式)
  4. 设置 GTLBC.TGID = GID (TLB 条目关联到此 Guest)
  5. 写 LVZ 标志到 CSR.SAVE5
  6. 保存 Host SP 到 CSR.SAVE0
  7. 执行 ertn 进入 Guest 模式

Guest 退出 (entry.S — _trap_from_lvz_guest):

当 Guest 执行特权操作或发生异常时,LVZ 硬件触发 VM Exit:

  1. 检查 GSTAT.PVM == 1 确认是 LVZ Guest 退出
  2. 清除 GSTAT.PVM (退出 Guest 模式)
  3. 清除 GTLBC.TGID = 0 (后续 TLB 操作不影响 Guest 条目)
  4. 设置 SAVE5 = 1 (标记 LVZ 活跃)
  5. 切换到 Host 栈 (从 SAVE0 恢复)
  6. 保存 Guest 寄存器上下文
  7. 进入 C 陷入处理器 loongarch64_trap_handler()

2.3 GSPR (Guest Sensitive Privileged Resource) 处理

LVZ Guest 执行特权指令时产生 GSPR 异常 (ecode=0x1A),而非 IPE。处理流程:

  1. 从 CSR.BADI 读取陷入指令 (非从 ERA 地址读取,避免 TLB 问题)
  2. 解码指令类型:
    • CPUCFG: 直通硬件值,屏蔽 PTW/LSX/LASX 特性位
    • IOCSR 读写: 直通到硬件 (用于 EIOINTC 中断控制器访问)
    • IDLE: 跳过 (推进 PC)
    • CSR 操作: 调用 emulate_privileged_insn() 统一处理

2.4 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 = old
  • CSRXCHG (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,确保中断处理器保存正确的返回地址

2.5 TLB 指令模拟

指令 模拟策略
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 条目地址翻译的核心:

  1. 从 Guest TLBELO 提取 GPA (Guest Physical Address)
  2. 以 1MB 为粒度在 tlb_refill_table[] 中查找对应的 HPA
  3. 构造 Host TLBELO: HPA + 强制 PLV=3 访问权限
  4. 若页面不在分区允许的内存区域内,返回 0 (拒绝访问)

2.6 LDDIR/LDPTE 硬件页表遍历模拟

完整模拟了 LoongArch64 的硬件页表遍历器:

  • LDDIR: 从 BADV 中提取目录索引 (使用 PWCL/PWCH 配置的 Dir1-4 base/width),通过 DMW1 直接访问 Guest 物理内存加载页目录项
  • LDPTE: 加载 PTE 到 guest_tlbrelo0/1,设置 TLBREHI.PS

2.7 ERTN (异常返回) 模拟

if (guest_in_tlb_refill):
    guest_crmd ← guest_tlbrprmd  # 从 TLB Refill 返回
else:
    guest_crmd ← guest_prmd      # 从普通异常返回

恢复后触发 check_guest_pending_irqs() 处理挂起虚拟中断。

2.8 虚拟中断投递

deliver_virtual_irq() 模拟硬件异常入口序列:

  1. 保存 guest_crmd → guest_prmd (PPLV + PIE)
  2. 保存当前 ERA → guest_era
  3. 清除 guest_crmd.IE,设置 PLV=0
  4. 计算向量化入口: VS>0 时 entry = eentry + (64 + hwirq) * vec_size — 匹配 Linux 的 EXCCODE_INT_START=64 约定
  5. 设置 regs->era 为计算出的向量地址

check_guest_pending_irqs(): 当 guest_crmd.IE 开启且 guest_estat & guest_ecfg & 0x1FFF 非零时,调用 deliver_virtual_irq()


3. 半虚拟化 (Para-Virtualization)

3.1 Hypercall 机制

指令syscall 0 (LoongArch64 同步异常指令)

调用约定:

寄存器 用途
$a0 Hypercall 编号 (输入) / 返回值 (输出)
$a1$a5 参数 1–5
$a7 必须为 0 (区分 PRTOS hypercall 与 Linux syscall)

Hypervisor 在 traps.c 中处理 SYS (ecode=0xB) 异常:

  1. 检查 $a7 == 0 确认是 PRTOS hypercall
  2. 从 $a0 获取 hypercall 编号
  3. 分发到对应处理函数 (与其他架构共享 hypercall 表)

3.2 IPE (Instruction Privilege Error) 陷入

半虚拟化 Guest 运行在 PLV3,执行特权指令时触发 IPE 异常 (ecode=0xE)。处理流程与 GSPR 相同:

  1. 从 CSR.BADI 读取陷入指令
  2. 解码并模拟 CSR 读写、TLB 操作、IOCSR 访问等
  3. 推进 Guest ERA
  4. 返回 Guest 继续执行

3.3 半虚拟化 Guest 入口

JMP_PARTITION 宏 (para-virt 路径):

  1. 设置 ERA = entryPRMD = 0x7 (PPLV=3, PIE=1)
  2. 保存 Host SP 到 CSR.SAVE0
  3. $a0 = PCT (Partition Control Table) 物理地址
  4. ertn 进入 PLV3

3.4 Guest 中断处理

半虚拟化中断投递 (fix_stack() in irqs.c):

  1. 保存 eracrmd$a0$a7 到 PCT arch 字段
  2. 设置 irq_vector (高位标记: vector | 0x80000000)
  3. 重定向 regs->era 到 pct->arch.trap_entry
  4. Guest 中断处理完成后通过 IRET hypercall (nr=44) 恢复: 从 PCT 恢复 eracrmd$a0$a7

3.5 FreeRTOS 半虚拟化移植

完全定制的 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

3.6 FreeRTOS 硬件虚拟化 (Native)

硬件虚拟化模式下的 FreeRTOS 使用 完全未修改的原生代码:

对比项 半虚拟化 硬件虚拟化
Guest 修改 完全定制端口 原生未修改代码
定时器 Hypercall (set_timer) 原生 CSR (TCFG/TVAL),LVZ 模拟
中断交付 PCT trap_entry + IRET 原生异常向量
UART Hypercall 或 MMIO 直接 MMIO 直通
启动方式 自定义 start.S + PCT 原生 start.S

4. MMU 与内存隔离

4.1 DMW (Direct Mapped Window) 架构

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_0000VA_uncached = PA | 0x8000_0000_0000_0000

4.2 Per-CPU TLB Refill 表

prtos_u64_t tlb_refill_table[CONFIG_NO_CPUS][4096];  // 每 CPU 4096 条目 × 1MB = 4GB 覆盖

每个条目是完整的 TLBELO 值 (64-bit) 或 0 (不可访问)。此表实现了 1MB 粒度的分区间内存隔离

表的物理地址存储在 CSR.SAVE4 中,供 TLB Refill 处理器在 DA 模式下直接访问。

4.3 分区内存映射 (setup_stage2_mmu)

每次分区切入前调用,配置当前分区的内存视图:

  1. 清零表 — 所有页面对 PLV3 不可访问
  2. 映射 Hypervisor — 前 2MB 从 CONFIG_PRTOS_LOAD_ADDR 起,只读 (V=1, D=0, PLV3, CC, G)
  3. 映射分区内存区域 — 遍历 XML 配置的 physical_memory_areas:
    • 普通: V|D|PLV3|CC|G
    • 只读: V|PLV3|CC|G (无 D 位)
    • 非缓存: V|D|PLV3|G (MAT=SUC)
  4. 映射 PCT 页 — 只读
  5. 写入 CSR.SAVE4 — 更新 refill 表物理地址
  6. 全量 TLB 刷新 — invtlb 0x0

4.4 TLB Refill 处理器 (entry.S)

运行在 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)

表查找算法:

  1. 计算 1MB 页面索引 (GPA >> 20)
  2. 偶数页: 加载 TLBRELO0
  3. 奇数页: 加载 TLBRELO1
  4. 设置 TLBREHI = 2MB 对齐基址 | PS=20 (1MB)
  5. 执行 tlbfill

拒绝路径: 未映射地址填充 V=0, G=1, PS=14 (16KB) 条目。选择 PS=14 是因为匹配 Guest 内核的 STLBPS 配置,避免 Guest 后续 TLBFILL 时产生 Multi-hit 异常。

4.5 页错误处理

对于 PIL/PIS/PIF (ecode 1-3):

  1. DMW 地址: 提取 GPA,查 refill 表,填充 1MB TLB 条目
  2. 直接 GPA (< 4GB): 同上
  3. Guest 虚拟地址: 通过 deliver_guest_exception() 向 Guest 投递页错误异常

对于 PME (ecode 4, 页修改异常): 临时设置 D=1 允许写入,下次 TLB 刷新时恢复。


5. 设备虚拟化

5.1 UART (NS16550)

组件 地址 说明
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 表允许

5.2 定时器虚拟化

硬件基础: LoongArch64 Stable Counter (100 MHz on QEMU virt)

半虚拟化定时器

  • get_time hypercall (nr=9): 读取 Stable Counter
  • set_timer hypercall (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。

5.3 IOCSR (I/O Control/Status Register) 系统

LoongArch64 独有的 IOCSR 机制用于访问片上设备 (EIOINTC, IPI, PCH-PIC 等)。

IOCSR 直通

对于 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 是否被清除。

IOCSR Syscall 接口

为 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 生命周期

5.4 EIOINTC 中断控制器

EIOINTC (Extended I/O Interrupt Controller) 是 LoongArch64 的高级中断路由器。

HWI (硬件中断) 处理流程:

  1. 硬件中断触发 ESTAT.IS[2:9]
  2. Hypervisor 陷入处理器:
    • 屏蔽 ECFG 中对应位 (防止重入)
    • 注入 到 guest_estat 对应位
    • 记录到 karch.hwi_injected 位图
  3. Guest 处理中断 (通过 IOCSR 清除 EIOINTC ISR)
  4. manage_hwi_state() 检测 ISR 已清除后:
    • 重新启用 ECFG 对应位
    • 清除 guest_estat 和 hwi_injected 对应位

5.5 Virtio 设备虚拟化

双分区 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。


6. SMP 多核支持

6.1 IOCSR 邮箱机制

LoongArch64 的 SMP 启动基于 IOCSR 邮箱:

  1. BSP 调用 write_remote_mailbox(cpu, _secondary_start_addr):
    • 将 _secondary_start 物理地址写入目标 CPU 的 IOCSR CORE_BUF_20 (地址 0x1020)
    • 通过 IOCSR_MAIL_SEND (0x1048) 发送
  2. 通过 IOCSR_IPI_SEND (0x1040) 发送 IPI 唤醒目标 CPU
  3. 目标 CPU 从 QEMU slave_boot_code 轮询循环中醒来
  4. _secondary_start 中执行: 配置 DMW → DA→PG 切换 → 调用 init_secondary_cpu()

6.2 Guest vCPU 启动 (HSM)

prtos_hsm_vcpu_start(target_vcpuid, start_addr, opaque):

  1. 验证目标 vCPU 在分区 num_of_vcpus 范围内
  2. 存储 hsm_entry 和 hsm_opaque 到目标 kthread 的 karch
  3. 调用 setup_kstack() 设置初始栈帧
  4. 设置 KTHREAD_READY_F,清除 KTHREAD_HALTED_F

7. 启动流程

7.1 完整启动链

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

7.2 DA→PG 模式切换的跳板技巧

这是 LoongArch64 启动中最精妙的部分。由于 DA→PG 切换是单条指令完成的 (写 CRMD),需要确保切换瞬间的取指不会失败:

  1. 配置 DMW0: VSEG=0x0 — 映射当前 DA 模式的地址范围
  2. 配置 DMW1: VSEG=0x9 — 最终虚拟地址窗口
  3. csrwr CRMD (PG=1, DA=0) — 此刻 PC 仍在低地址,DMW0 保证取指继续
  4. jr $s1 — 跳转到 0x9000... 地址空间
  5. 重配置 DMW0 为最终值 (VSEG=0x8, SUC)

8. 用户态组件

8.1 RSW 引导加载器

  • 运行在 DA 模式,无 DMW 需求
  • CPU 0 执行 rsw_main(),解包 container.bin (PRTOS core + 分区镜像)
  • 副 CPU 轮询物理地址 0x100 + cpuid*8 的邮箱

8.2 BAIL (Bare-metal Application Interface Library)

分区引导框架:

  1. 从 $a0 获取 PCT 物理地址
  2. 通过 syscall 0 (get_vcpuid) 获取 vCPU ID
  3. vCPU 0 清零 BSS
  4. 每 vCPU 独立栈: sp = _stack + (vcpuid + 1) * STACK_SIZE
  5. 调用链: init_libprtos() → init_arch() → setup_irqs() → partition_main()

8.3 libprtos Hypercall 库

提供 _PRTOS_HCALL0 ~ _PRTOS_HCALL5 宏,封装 syscall 0 指令。特殊 hypercall:

  • IRET (nr=44): 从 PCT 恢复 eracrmd$a0$a7
  • RAISE_TRAP (nr=45): 向分区注入陷入

8.4 Linux Boot Stub

linux_4vcpu_1partion_loongarch64/start.S:

  1. 写 'V' 到 UART (验证标记)
  2. 设置 Linux 启动协议参数: a0=0 (non-EFI), a1=0 (cmdline forced), a2=0 (no EFI table)
  3. 跳转到内核入口: _linux_image_start + 0x1622000 (kernel_entry offset)
  4. vmlinux.bin 通过 .incbin 嵌入在 2MB 对齐偏移处

9. 测试用例适配

9.1 测试矩阵

测试用例 类型 超时 验证标准
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 + 共享内存配置

9.2 测试基础设施

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

9.3 测试结果

======================================
  Test Report [loongarch64]
======================================
  Total: 32  Pass: 16  Fail: 0  Skip: 16
======================================

16 个 LoongArch64 相关测试全部 PASS,16 个其他架构测试正确 SKIP。


10. 与其他平台的对比

10.1 架构差异对照表

特性 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

10.2 LoongArch64 特有挑战

  1. DMW PLV3 启用: 需要同时为 PLV0 (Hypervisor) 和 PLV3 (Guest) 启用 DMW,Guest 可使用 0x8000.../0x9000... 内核 VA
  2. TLB Refill DA 模式: TLB Refill 处理器运行在 DA 模式 (VA=PA),需要在物理地址空间维护 refill 表
  3. BADI 指令读取: 从 CSR.BADI 读取陷入指令而非 ERA 地址,避免 Guest TLB 上下文中的 TLB Miss
  4. V=0 Deny 条目 PS 匹配: 拒绝条目使用 PS=14 (16KB) 匹配 Guest STLBPS,防止 Multi-hit 异常
  5. IOCSR 双重角色: 既是设备访问接口 (EIOINTC, IPI),又是 SMP 启动的邮箱机制
  6. DA→PG 跳板: 需要临时 DMW0 (VSEG=0x0) 保证模式切换时的连续取指

10.3 适配代码量统计

组件 行数 占比
陷入处理与指令模拟 (traps.c) ~1,600 36%
汇编入口/启动 (entry.Sstart.Shead.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 设备虚拟化

继续阅读
历史上的今天
4 月
20
prtos, info
  • 本文由 发表于 2026年4月20日 20:23:18
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定