You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KVM搭配QEMU运行时缺失扩展处理器信息与特性位(pdpe1gb等)问题排查

KVM 对外暴露CPU特性异常问题排查

场景说明

  • 联调KVM与QEMU协同运行时发现,QEMU视角可见的CPU特性远少于主机实际支持的范围,经核对QEMU源码确认,QEMU获取的CPU特性列表完全来自KVM接口返回值。
  • 为定位特性缺失问题,编写C程序通过KVM_GET_SUPPORTED_CPUID接口检测KVM层对外暴露的可用CPU特性,核心测试代码如下:
#include <stdio.h>
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>

static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
                                             uint32_t function,
                                             uint32_t index);
void main(){
    int status, fd, max=64;
    uint32_t ret = 0;
    fd = open("/dev/kvm", O_RDWR | O_CLOEXEC);
    if (fd == -1)
        errx("cannot open /dev/kvm");

    status = ioctl(fd, KVM_GET_API_VERSION,0);
    printf("kvm api=%d\n", status);

    struct kvm_cpuid2 *cpuid;
    int r, size;

    size = sizeof(*cpuid) + max * sizeof(*cpuid->entries);
    cpuid = malloc(size);
    cpuid->nent = max;
    r = ioctl(fd , KVM_GET_SUPPORTED_CPUID, cpuid);
    if (r == 0 && cpuid->nent >= max) {
        errx("2 big\n");
    }
    if (r < 0){
        errx("problem\n");
    }

    struct kvm_cpuid_entry2 *entry = cpuid_find_entry(cpuid, 0x80000001, 0);
    if (entry) {
        ret = entry->edx;
    }
    printf("edx = %x\n", ret);
    if (ret & (1U << 0)) printf(" fpu");
    if (ret & (1U << 1)) printf(" vme");
    if (ret & (1U << 2)) printf(" de");
    if (ret & (1U << 3)) printf(" pse");
    if (ret & (1U << 4)) printf(" tsc");
    if (ret & (1U << 5)) printf(" msr");
    if (ret & (1U << 6)) printf(" pae");
    if (ret & (1U << 7)) printf(" mce");
    if (ret & (1U << 8)) printf(" cx8");
    if (ret & (1U << 9)) printf(" apic");
    if (ret & (1U << 11)) printf(" syscall");
    if (ret & (1U << 12)) printf(" mtrr");
    if (ret & (1U << 13)) printf(" pge");
    if (ret & (1U << 14)) printf(" mca");
    if (ret & (1U << 15)) printf(" cmov");
    if (ret & (1U << 16)) printf(" pat");
    if (ret & (1U << 17)) printf(" pse36");
    if (ret & (1U << 18)) printf(" mp");
    if (ret & (1U << 20)) printf(" nx");
    if (ret & (1U << 22)) printf(" mmxext");
    if (ret & (1U << 23)) printf(" mmx");
    if (ret & (1U << 24)) printf(" fxsr");
    if (ret & (1U << 25)) printf(" fxsr_opt");
    if (ret & (1U << 26)) printf(" pdpe1gb");
    if (ret & (1U << 27)) printf(" rdtscp");
    if (ret & (1U << 29)) printf(" lm");
    if (ret & (1U << 30)) printf(" 3dnowext");
    if (ret & (1U << 31)) printf(" 3dnow");
    printf("\n");
}

static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
                                                 uint32_t function,
                                                 uint32_t index)
{
    int i;
    for (i = 0; i < cpuid->nent; ++i) {
        if (cpuid->entries[i].function == function &&
            cpuid->entries[i].index == index) {
            return &cpuid->entries[i];
        }
    }
    /* not found: */
    return NULL;
}
  • 测试程序运行输出:
kvm api=12
edx = 28100800
 syscall nx rdtscp lm
  • 主机侧实际CPU特性(取自系统命令返回值):

cat /proc/cpuinfo | grep flags | uniq
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xsaves arat pku ospke flush_l1d arch_capabilities

  • 显然,对应EAX=0x80000001h功能的“扩展处理器信息与特性位”项中大量特性未被KVM暴露,其中重点关注的pdpe1gb(1GB大页物理地址扩展)特性未被透传。查阅KVM官方API文档未发现相关限制说明。
  • 运行环境参数:
    • CPU型号:Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz,vendor_id为GenuineIntel,cpu family 6,model 85
    • KVM API版本:12
    • 系统版本:Ubuntu 18,内核版本5.4.0-70-generic
    • 已开启嵌套虚拟化,当前主机本身为上层Hypervisor运行的虚拟机

问题与对应解答

1. 可在哪里查询KVM支持与不支持的CPU特性清单?是否存在KVM不支持相关特性的情况?

  • 没有官方对外发布的统一KVM CPU特性清单文档。KVM对CPU特性的过滤、透传逻辑全部实现在对应版本内核的arch/x86/kvm/cpuid.c文件中,所有被屏蔽的特性、触发透传的条件都在该文件的特性匹配规则中明确标注,直接查阅对应内核版本的源码是最准确的查询方式。
  • 确实存在KVM不会向上层暴露全部主机原生CPU特性的情况。出于兼容性、稳定性、安全隔离的要求,KVM会对主机侧的CPU特性做分层过滤:部分特性会被直接屏蔽,部分特性需要匹配特定硬件/固件条件才会透传,还有部分特性需要上层调用方主动传递对应配置才会在CPUID返回值中体现。

2. 本次检查使用的CPUID第26位定义是参考QEMU代码得出的,可在哪里查询Intel CPU各CPUID位的官方定义文档?

  • Intel官方的软件开发手册(SDM)是x86架构CPU所有CPUID位定义的权威来源,手册逐位说明了每个CPUID leaf、每个返回寄存器对应的功能含义、触发条件,所有特性位的定义都可以在手册中直接查到,不需要从QEMU等第三方代码反推定义。
  • 本次用到的0x80000001扩展CPUID leaf的EDX寄存器中,第26位对应的pdpe1gb功能,在官方手册中有明确标注。

3. 为什么上述测试示例中,KVM未对外暴露pdpe1gb对应的特性位?

  • 结合当前嵌套虚拟化的运行环境,该特性缺失的核心原因有两点:
    • 嵌套虚拟化场景下的特性过滤规则:当当前主机本身是上层Hypervisor运行的虚拟机时,KVM会增加一层判断逻辑——只有上层Hypervisor向当前嵌套VM透传了1GB页表支持能力时,下层KVM才会将pdpe1gb特性位加入返回的CPUID列表。
    • 内核版本的前置校验逻辑:5.4.0-70-generic版本的内核中,pdpe1gb特性的透传有单独的校验条件,即使主机硬件本身支持1GB大页,也需要内核启动参数未关闭大页支持、对应的大页功能模块正常加载,KVM才会透传该特性。结合测试输出的EDX值0x28100800,二进制展开后第26位值为0,说明当前嵌套场景下上层Hypervisor未向当前VM传递1GB页表支持,因此下层KVM自然不会暴露该特性位。

内容的提问来源于stack exchange,提问作者yehudahs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 10:45:05