本文介绍了使用 XSETBV 写入 XCR0 会在支持 MPX 的硬件上的 VM 中产生一般保护错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 x86_64 Debian v7 虚拟机上写入扩展控制寄存器 0 (xcr0).我这样做的方法是通过带有一些内联程序集的内核模块(所以 CPL=0).但是,当我尝试执行 xsetbv 指令时,我不断收到一般保护错误 (#GP).

I am trying to write to Extended Control Register 0 (xcr0) on an x86_64 Debian v7 virtual machine. My approach to doing so is through a kernel module (so CPL=0) with some inline assembly. However, I keep getting a general protection fault (#GP) when I try to execute the xsetbv instruction.

我的模块的 init 函数首先检查控制寄存器 4 (cr4) 中的 osxsave 位是否设置.如果不是,它会设置它.然后,我使用 xgetbv 读取 xcr0 寄存器.这工作正常,并且(在我所做的有限测试中)具有值 0b111.我想设置 bndregbndcsr 位,它们是第 3 位和第 4 位(0 索引),所以我做了一些 ORing并使用 xsetbv0b11111 写回 xcr0.实现这最后一部分的代码如下.

The init function of my module first checks that the osxsave bit is set in control register 4 (cr4). If it isn't, it sets it. Then, I read the xcr0 register using xgetbv. This works fine and (in the limited testing I have done) has the value 0b111. I would like to set the bndreg and bndcsr bits which are the 3rd and 4th bits (0-indexed), so I do some ORing and write 0b11111 back to xcr0 using xsetbv. The code to achieve this last part is as follows.

unsigned long xcr0;             /* extended register    */
unsigned long bndreg = 0x8;     /* 3rd bit in xcr0      */
unsigned long bndcsr = 0x10;    /* 4th bit in xcr0      */

/* ... checking cr4 for osxsave and reading xcr0 ... */

if (!(xcr0 & bndreg))
    xcr0 |= bndreg;

if (!(xcr0 & bndcsr))
    xcr0 |= bndcsr;

/* ... xcr0 is now 0b11111 ... */

/*
 * write changes to xcr0; ignore high bits (set them =0) b/c they are reserved
 */
unsigned long new_xcr0 = ((xcr0) & 0xffffffff);
__asm__ volatile (
    "mov $0, %%ecx      \t\n" // %ecx selects the xcr to write
    "xor %%rdx, %%rdx   \t\n" // set %rdx to zero
    "xsetbv             \t\n" // write from edx:eax into xcr0
    :
    : "a" (new_xcr0)        /* input    */
    : "ecx", "rdx"          /* clobbered    */
);

通过查看一般保护故障的跟踪,我确定 xsetbv 指令是问题所在.但是,如果我不操作 xcr0 而只是读取它的值并将其写回,事情似乎工作正常.查看英特尔手册和本网站,我发现了#GP的各种原因,但它们似乎都不符合我的情况.原因如下,以及我对它们很可能不适用的原因的解释.

By looking at the trace from the general protection fault, I determined that the xsetbv instruction is the problem. However, if I don't manipulate xcr0 and just read its value and write it back, things seem to work fine. Looking at the Intel manual and this site, I found various reasons for a #GP, but none of them seem to match my situation. The reasons are as follows along with my explanation for why they most likely don't apply.

  • 如果当前权限级别不是0 --> 我用内核模块实现CPL=0

如果在 %ecx 中指定了无效的 xcr --> 0 在 %ecx 中,它是有效的并且适用于xgetbv

If an invalid xcr is specified in %ecx --> 0 is in %ecx which is valid and worked for xgetbv

如果edx:eax中的值设置了ecx指定的xcr中保留的位-->根据英特尔手册和维基百科我设置的位未被保留

If the value in edx:eax sets bits that are reserved in the xcr specified by ecx --> according to the Intel manual and Wikipedia the bits I am setting are not reserved

如果试图清除 xcr0 的第 0 位 --> 我在设置它之前打印了 xcr0,它是 0b11111

If an attempt is made to clear bit 0 of xcr0 --> I printed out xcr0 before setting it, and it was 0b11111

如果尝试将 xcr0[2:1] 设置为 0b10 --> 我之前打印了 xcr0设置它,它是0b11111

If an attempt is made to set xcr0[2:1] to 0b10 --> I printed out xcr0 before setting it, and it was 0b11111

在此先感谢您提供任何帮助来发现此 #GP 发生的原因.

Thank you in advance for any help discovering why this #GP is happening.

推荐答案

Peter Cordes 是对的,这是我的虚拟机管理程序的问题.我正在使用 VMWare Fusion 进行虚拟化,在互联网上进行了大量挖掘后,我发现以下来自 VMWare 的引用:

Peter Cordes was right, it was a problem with my hypervisor. I am using VMWare Fusion for virtualization, and after a lot of digging on the internet I found the following quote from VMWare:

内存保护扩展 (MPX) 是在 Intel Skylake 一代 CPU 中引入的,并为边界检查提供硬件支持.从 Ice Lake 一代开始,Intel CPU 将不支持此功能.

从 ESXi 6.7 P02 和 ESXi 7.0 GA 开始,为了最大限度地减少未来升级过程中的中断,VMware 将不再默认在开机时向虚拟机公开 MPX.可以使用 VM 配置选项继续公开 MPX.

Starting with ESXi 6.7 P02 and ESXi 7.0 GA, in order to minimize disruptions during future upgrades, VMware will no longer expose MPX by default to VMs at power-on. A VM configuration option can be used to continue exposing MPX.

VMWare 提出的解决方案是使用以下指令编辑虚拟机的 .vmx 文件.

The solution VMWare proposed was to edit the virtual machine's .vmx file with the following directive.

cpuid.enableMPX = "TRUE"

完成此操作后,一切正常,我能够使用 xsetbv 启用 xcr0 的 bndregbndcsr.

After I did this, things worked and I was able to use xsetbv to enable the bndreg and bndcsr bits of xcr0.

当使用 VMWare 在更正常的条件下将 CPU 功能从主机暴露给来宾时(即该功能不受弃用的困扰),您可以通过添加以下内容来屏蔽 cpuid 叶的位到 VM 的 .vmx 文件.

When using VMWare to expose CPU features from the host to the guest under more normal conditions (i.e. the feature isn't plagued with deprecation) you can mask the bits of cpuid leaves by adding the following to the VM's .vmx file.

cpuid.<leaf>.<register> = "<value>"

因此,例如,如果我们假设 SMAP 可以通过这种方式公开,我们将要设置 cpuid 叶 7 的第 20 位.

So, for example, if we assume that SMAP can be exposed this way, we would want to set bit 20 of cpuid leaf 7.

cpuid.7.ebx = "----:----:---1:----:----:----:----:----"

冒号是可选的,以便于阅读字符串,1 和 0 覆盖任何默认设置,破折号用于单独保留默认设置.

Colons are optional to ease reading of the string, ones and zeros override any default settings, and dashes are used to leave default setting alone.

这篇关于使用 XSETBV 写入 XCR0 会在支持 MPX 的硬件上的 VM 中产生一般保护错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 17:52