- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
dbs-arch是 Dragonball Sandbox(Kata Containers 的 Rust VMM 后端之一)中负责隔离 CPU 架构细节的核心 crate:它把 x86_64 与 ARM64 的架构特定常量、结构和工具函数封装在一层统一接口之下,使上层 VMM 代码无需关心具体指令集差异。读完本文,你将理解dbs-arch在 Dragonball 中的定位与模块划分,掌握基于VmSpec与process_cpuid()的 x86_64 CPUID 过滤机制(包括 CPU 拓扑、品牌字符串与 VPMU 计数器控制),并了解 aarch64 侧 GICv2/GICv3/ITS 中断控制器与寄存器配置的底层实现细节。
设计目标:把架构差异藏在 crate 边界之后
dbs-arch的设计初衷可以用一句话概括:收集 CPU 架构特定的常量与工具,将 CPU 架构细节从 Dragonball Sandbox(或其他 VMM)中隐藏起来。crate 入口文件 lib.rs 的模块文档与 README 一致地阐明了这一定位。
从源码结构看,crate 通过条件编译按目标架构暴露模块:
#[cfg(target_arch = "x86_64")] mod x86_64;以及pub use x86_64::*;;#[cfg(target_arch = "aarch64")] mod aarch64;以及pub use aarch64::*;。
也就是说,同一份上层代码链接dbs-arch后,在 x86_64 宿主上得到的是 CPUID/MSR/GDT 相关工具,在 aarch64 宿主上得到的则是 GIC/PMU/寄存器相关工具。这种“编译期分派”保证了上层 VMM 逻辑不需要出现if arch == ...式的分支。
crate 当前支持两种架构:
- AMD64(x86_64);
- ARM64(aarch64)。
另外,lib.rs 还定义了一个跨架构共享的枚举VpmuFeatureLevel,用于表达虚拟 PMU(性能监控单元)能力的三档级别,这一点与 CPUID 过滤逻辑直接相关,下文会重点展开。
依赖方面,Cargo.toml 声明 crate 名为dbs-arch,license 为Apache-2.0 AND BSD-3-Clause,核心依赖包括kvm-bindings(启用fam-wrappersfeature)、kvm-ioctls、libc、memoffset、thiserror、vm-memory与vmm-sys-util——可见该 crate 的全部工作都围绕 KVM 接口展开。
子模块总览
README 给出了子模块清单,下面结合仓库实际目录结构逐项说明其职责:
| 模块 | 架构 | 描述 | 源码路径 |
|---|---|---|---|
x86_64::cpuid | x86_64 | 处理 CPUID 信息的工具 | src/x86_64/cpuid/ |
x86_64::msr | x86_64 | 模型特定寄存器(MSR)的常量与函数 | src/x86_64/msr.rs |
aarch64::gic | aarch64 | 管理 GICv2/GICv3/ITS 设备的结构 | src/aarch64/gic/ |
aarch64::regs | aarch64 | 配置与管理 CPU 寄存器的常量与函数 | src/aarch64/regs.rs |
实际仓库中还包含若干 README 未单独列出但同样重要的模块:
- x86_64 侧:gdt.rs(全局描述符表)、interrupts.rs(中断向量常量)、regs.rs(寄存器位域常量),在 x86_64/mod.rs 中与
cpuid、msr一同导出; - aarch64 侧:pmu.rs 负责 PMU 虚拟化,与 x86_64 的 VPMU 功能在语义上对应。
x86_64 CPUID 过滤:VmSpec 与 process_cpuid
CPUID 过滤是dbs-arch最有实战价值的能力。按照 CPUID 设计文档,其设计目标是:作为 Intel 与 AMD CPU 的 CPUID 过滤器,通过 CPUID 配置为 VM 设定CPU 拓扑、缓存拓扑、PMU 状态及其他特性。该实现基于 Firecracker 的 CPUID 代码,并额外扩展了 CPU Topology 与 VPMU 特性。
VmSpec:描述目标 VM 的规格
过滤动作需要一个“目标规格”输入。VmSpec定义在 transformer/mod.rs 中,字段含义如下:
pub struct VmSpec { /// CPU 的 vendor id cpu_vendor_id: [u8; 12], /// 当前逻辑 CPU 的 id,取值范围 [0..cpu_count] cpu_id: u8, /// 逻辑 CPU 总数(包含可热插拔的 CPU) cpu_count: u8, /// 期望呈现给 guest 的品牌字符串 brand_string: BrandString, /// CPU 拓扑:每核线程数 threads_per_core: u8, /// CPU 拓扑:每 die 核心数 cores_per_die: u8, /// CPU 拓扑:每 socket die 数 dies_per_socket: u8, /// VPMU 特性级别: /// Disabled 表示关闭(默认); /// LimitedlyEnabled 表示仅支持最小计数器(cycles 和 instructions); /// FullyEnabled 表示支持全部 vpmu 计数器 vpmu_feature: VpmuFeatureLevel, }几个值得注意的实现细节:
- cpu_count 是 u8:
Error::VcpuCountOverflow错误变体明确说明“可寻址逻辑 CPU 上限无法存入 u8”时会报错,即 x86_64 的 CPUID 拓扑编码天然受 8 位字段约束; - cpu_vendor_id 自动探测:
VmSpec::new()并不接收 vendor 参数,而是通过get_vendor_id()从宿主环境读取(见 transformer/mod.rs),品牌字符串再由BrandString::from_vendor_id()推导; - 构造签名:
VmSpec::new(cpu_id, cpu_count, threads_per_core, cores_per_die, dies_per_socket, vpmu_feature),返回值是Result<VmSpec, Error>。
VpmuFeatureLevel:虚拟 PMU 的三档开关
VpmuFeatureLevel定义在 crate 根 lib.rs,是跨架构共享的枚举:
Disabled:VPMU 关闭(默认值);LimitedlyEnabled:仅支持最小计数器(cycles 与 instructions)。源码注释特别指出,aarch64 上目前尚不支持该级别,能力将在未来实现;FullyEnabled:支持全部 vpmu 计数器。
该枚举派生了Debug、Eq、PartialEq、Copy、Clone,其 trait 行为有对应单元测试覆盖(lib.rs 测试模块)。
process_cpuid:按厂商分派的过滤入口
核心入口函数是 process_cpuid():
pub fn process_cpuid(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { use transformer::CpuidTransformer; match vm_spec.cpu_vendor_id() { self::common::VENDOR_ID_INTEL => { self::transformer::intel::IntelCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec) } self::common::VENDOR_ID_AMD => { self::transformer::amd::AmdCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec) } self::common::VENDOR_ID_HYGON => { self::transformer::amd::AmdCpuidTransformer::new().process_cpuid(kvm_cpuid, vm_spec) } _ => Err(Error::CpuNotSupported), } }从源码结构看,过滤逻辑采用策略模式:IntelCpuidTransformer与AmdCpuidTransformer(transformer/intel.rs、transformer/amd.rs)都实现统一的CpuidTransformertrait,Hygon(海光)CPU 复用 AMD 转换器。不支持的 vendor 直接返回Error::CpuNotSupported——这一行为有测试印证:transformer/mod.rs 的测试 将cpu_vendor_id置为[1; 12]后断言process_cpuid返回错误。
CpuidTransformertrait 本身(transformer/mod.rs)的设计也很清晰:
process_cpuid()默认委托给process_entries();process_entries()遍历CpuId的每一个kvm_cpuid_entry2,为每个 entry 查询entry_transformer_fn(),若存在对应的转换函数则执行;entry_transformer_fn()返回Option<EntryTransformerFn>,各厂商实现只需声明“我关心哪些 leaf”,即可精准改写目标条目而保留其余条目原样。
使用流程:KVM_GET_CPUID2 → VmSpec → process_cpuid → SET
按照 CPUID 文档 的 Usage 章节,标准使用流程分三步:
第一步:通过 KVM_GET_CPUID2 ioctl 获取原始 CPUID。这部分不在 dbs-arch 内部完成,需要 VMM 侧调用:
// 在 VMM 中获取 cpuid 的示例 let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; let ret = unsafe {ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr())}; if ret != 0 { return Err(errno::Error::last()); }第二步:构造VmSpec并调用process_cpuid()过滤。文档给出的 VMM 侧调用示例:
let cpuid_vm_spec = VmSpec::new( self.id, vcpu_config.max_all_vcpu_count as u8, vcpu_config.threads_per_core, vcpu_config.cores_per_die, vcpu_config.dies_per_socket, vcpu_config.vpmu_feature, ) .map_err(VcpuError::CpuId)?; process_cpuid(&mut self.cpuid, &cpuid_vm_spec).map_err(|e| { METRICS.vcpu.process_cpuid.inc(); error!("Failure in configuring CPUID for vcpu {}: {:?}", self.id, e); VcpuError::CpuId(e) })?;示例中self.id是 vCPU 编号(对应cpu_id),max_all_vcpu_count是 VM 允许的最大 vCPU 数(含可热插拔部分),拓扑三元组threads_per_core / cores_per_die / dies_per_socket决定了 guest 看到的 CPU 层级结构,vpmu_feature则控制性能计数器的暴露级别。
第三步:将过滤后的 CPUID 写入 guest vCPU(通过 KVM 相应 ioctl 设置),guest 内核与用户态程序此后看到的 CPUID 即为 VMM 精心修饰过的结果。
CPUID leaf 常量与位操作辅助
过滤之所以可行,是因为 cpu_leaf.rs 按 leaf 维度把 CPUID 各字段的位域定义成了常量模块,例如 leaf 0x1 中:
eax:EXTENDED_FAMILY_ID_BITRANGE、PROCESSOR_FAMILY_BITRANGE、PROCESSOR_MODEL_BITRANGE、STEPPING_BITRANGE等;ebx:APICID_BITRANGE(31..24,APIC ID)、CPU_COUNT_BITRANGE(23..16,逻辑处理器数)、CLFLUSH_SIZE_BITRANGE(15..8,CLFLUSH 粒度);ecx:DTES64、MONITOR、TM2、VMX、EIST 等特性位索引。
这些BitRange由 bit_helper.rs 提供的bit_range!宏构造,转换器函数据此对原始 entry 做精确的位级读写——这正是“隐藏架构细节”的落地方式:上层只调用process_cpuid(),位域操作全部封存在dbs-arch内部。此外 common.rs 提供 vendor 探测与通用工具,brand_string.rs 负责 guest 品牌字符串的生成与解析。
x86_64 MSR:Model Specific Registers 管理
msr.rs 提供 MSR 相关的常量与函数,文件头注明 MSR 常量“automatically generated by rust-bindgen”。核心抽象是MsrRange结构(base + nmsrs 的区间),配合SINGLE_MSR!/MSR_RANGE!宏声明允许透传或设置的 MSR 集合;contains()用于判定某个 MSR 是否落在允许区间内。错误类型msr::Error覆盖了读取支持的 MSR 列表失败(GetSupportedModelSpecificRegisters)、设置失败(SetModelSpecificRegisters)与部分设置失败(SetModelSpecificRegistersCount)等场景,底层依赖kvm_bindings::MsrList与kvm_ioctls::Kvm。
aarch64:GIC 中断控制器、PMU 与寄存器
ARM64 侧没有 CPUID 概念,其架构抽象重心是中断控制器与设备树。
GICv2 / GICv3 / ITS
gic/mod.rs 导出gicv2、gicv3与its三个子模块(分别对应 gicv2.rs、gicv3.rs、its.rs),并定义了以下关键常量:
IRQ_BASE: u32 = 32:aarch64 上第一个可用中断号;IRQ_MAX: u32 = 159:最后一个可用中断号;GIC_REG_END_ADDRESS: u64 = 1 << 30(1GB):GIC 寄存器区结束地址。
源码注释解释了中断数量选择的依据:参照内核virt/kvm/arm/vgic/vgic-kvm-device.c的约束,GIC 支持的中断数必须大于 32、小于 1023 且是 32 的倍数,因此最终配置为最多支持 128 个中断(32..159)。
该模块还提供:
save_pending_tables(fd):在 VM 停止时将RDISTpending 表刷入 guest RAM,通过KVM_DEV_ARM_VGIC_GRP_CTRL/KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES属性实现;GICDevicetrait:统一 GIC 设备接口,要求实现device_fd()、device_properties()、vcpu_count()与fdt_compatibility(),即设备同时面向 KVM 设备 fd 操作和 FDT(Flattened Device Tree)生成两条路径;gic::Error枚举:覆盖 KVM ioctl 失败(CreateGIC)、设备属性设置失败(SetDeviceAttribute)、vCPU 数不一致(InconsistentVcpuCount)、vgic 系统寄存器状态无效(InvalidVgicSysRegState)以及 ITS 创建/属性设置失败(CreateITS、SetITSAttribute)。
此外,aarch64/mod.rs 定义了MMIODeviceInfo(MMIO 基地址、大小、irq 列表、可选 device_id)与DeviceInfoForFDTtrait,用于把 MMIO 设备信息喂给 FDT 生成逻辑;其irq()实现严格校验 irq 列表长度必须为 1 且落在IRQ_BASE..=IRQ_MAX区间内,测试用例test_mmo_device_info_get_irq覆盖了越界与空列表的负路径。DeviceType枚举区分 Virtio(带 ID)、Serial 与 RTC 设备类型。
regs 与 pmu
regs.rs 提供 aarch64 CPU 寄存器配置的常量与函数(与 x86_64 的 regs/gdt/interrupts 模块角色对应),pmu.rs 负责 PMU 虚拟化。二者与VpmuFeatureLevel中的 aarch64 限制说明相呼应:aarch64 上LimitedlyEnabled级别尚待实现,即 ARM64 侧 VPMU 当前实际可用的语义档位更少,使用方需注意这一差异。
血缘与许可
README 的 Acknowledgement 部分明确说明:部分代码派生自 Firecracker 项目(CPUID 过滤即源于 Firecracker 并做了扩展)。crate 许可声明为Apache-2.0 AND BSD-3-Clause(见 Cargo.toml 与 LICENSE、THIRD-PARTY),源码文件头中也可以看到 Alibaba Cloud 与 Amazon 的双重版权声明及 Chromium OS 的 BSD 许可声明(如 cpuid/mod.rs 文件头)。
小结:在 Dragonball 体系中如何定位 dbs-arch
从仓库整体结构看,dbs-arch位于 src/dragonball/crates/ 下的 crate 集合中,与dbs_address_space、dbs_device、dbs_virtio_devices等 crate 共同构成 Dragonball VMM 的组件层,而 Dragonball 主库 的 vCPU、VM 与设备管理代码则依赖它完成架构相关的底层工作。对维护者而言,理解dbs-arch的三条主线即可把握全貌:
- x86_64:以
process_cpuid()+ 厂商CpuidTransformer为核心的 CPUID 过滤(拓扑、品牌字符串、VPMU 三档开关),辅以 MSR 区间管理、GDT/中断常量与 leaf 位域定义; - aarch64:GICv2/GICv3/ITS 设备抽象(32..159 中断空间、FDT 兼容性接口)、MMIO 设备信息与 PMU/寄存器支持;
- 跨架构共享:
VpmuFeatureLevel枚举把虚拟 PMU 能力档位统一表达,供两种架构的调用方共用。
对于需要在 Kata Containers Dragonball 运行时中调整 guest CPU 呈现形态(拓扑、特性位、性能计数器暴露)的开发者,入口就是 docs/x86_64_cpuid.md 描述的KVM_GET_CPUID2 → VmSpec::new() → process_cpuid()调用链;需要深入位级细节时,则顺着 cpuid 目录 下的cpu_leaf.rs、bit_helper.rs与各厂商 transformer 继续阅读即可。
- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
相关推荐
kata-containers Dragonball 虚拟机的地址空间管理:dbs-address-space 源码解析
kata containers Dragonball 虚拟机的地址空间管理:dbs address space 源码解析 Dragonball 是 Kata C
云原生容器运行时Arduino-ESP32硬件抽象层深度解析:HAL架构与实现原理
Arduino ESP32硬件抽象层深度解析:HAL架构与实现原理 引言 在嵌入式开发领域,硬件抽象层(Hardware Abstraction Layer,H
嵌入式物联网驱动开发终极指南:深入理解Kata Containers架构与轻量级VM技术
Kata Containers是一个革命性的开源项目,致力于构建轻量级虚拟机(VMs)的标准实现,这些虚拟机既具备容器的轻量级特性和性能,又提供虚拟机的工作负载
云原生容器运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考