使用 XSETBV 写入 XCR0 在支持 MPX 的硬件上的 VM 中创建一般保护错误
Using XSETBV to write to XCR0 creates a general protection fault in a VM on hardware that supports MPX
我正在尝试写入 x86_64 Debian v7 虚拟机上的扩展控制寄存器 0 (xcr0
)。我这样做的方法是通过一个带有一些内联汇编的内核模块(所以 CPL=0
)。但是,当我尝试执行 xsetbv
指令时,我不断收到一般保护错误 (#GP
)。
我的模块的 init
函数首先检查 osxsave
位是否设置在控制寄存器 4 (cr4
) 中。如果不是,则设置它。然后,我使用 xgetbv
读取 xcr0
寄存器。这工作正常并且(在我所做的有限测试中)具有值 0b111
。我想设置 bndreg
和 bndcsr
位,它们是第 3 位和第 4 位(0 索引),所以我做了一些 OR
ing 并将 0b11111
写回xcr0
使用 xsetbv
。实现这最后一部分的代码如下。
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 [=10=], %%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
并且只是读取它的值并将其写回,那么事情似乎工作正常。查看Intel手册和this site,我发现了#GP
的各种原因,但其中none似乎符合我的情况。原因如下以及我对它们很可能不适用的原因的解释。
如果当前权限级别不为0 --> 我用一个内核模块来实现CPL=0
如果在 %ecx
中指定了无效的 xcr
--> 0 在 %ecx
中有效并且适用于 xgetbv
If the value in edx:eax
sets bits that are reserved in the xcr
specified by ecx
--> according to the Intel manual and Wikipedia 我设置的位没有保留
If attempt to clear bit 0 of xcr0
--> 我在设置之前打印了xcr0
,结果是0b11111
如果尝试将xcr0[2:1]
设置为0b10
--> 我在设置之前打印了xcr0
,结果是0b11111
提前感谢您提供的任何帮助,以了解为什么会发生这种 #GP
。
/proc/cpuinfo
on the VM doesn't list mpx in the flags (it does list xsave though). My host does have MPX support though. I am running Linux kernel version 3.19 which does support MPX and I already have a binary compiled with MPX (the bnd instructions etc. are all there when I objdump). The problem is that the instructions get treated as NOPs. I thought the process I described above would fix this and enable the CPU to recognize MPX.
如果您在支持 MPX 的机器上 运行 它将启用 MPX。 (假设您的代码是正确的。)
虚拟 x86 CPU 你的 VM 运行ning 上没有,根据它自己的虚拟化 CPUID,所以这个错误一点也不奇怪。管理程序可能在 VMEXIT 中手动执行此操作,模拟 xsetbv
并检查对虚拟化 xcr0 的更改。
如果您想使用您的硬件具有但您的 VM 不支持的功能,通常您必须 运行 在裸机上。或者找一个不同的 VM 向来宾公开该功能。
请注意,MPX 引入了新的架构状态(bnd
寄存器),在上下文切换时必须获得 saved/restored。如果您的管理程序不想这样做,那将是禁用 MPX 的原因之一。 (我认为它可以获得 saved/restored 作为 xsave
的一部分,但它确实使节省的空间稍微大一些。)我没怎么看过 MPX;这可能是管理程序必须在 vmexits 中处理的事情,以便不对管理程序应用边界检查...如果是这样,那将是一个很大的不便。
Peter Cordes 是对的,这是我的管理程序的问题。我正在使用 VMWare Fusion 进行虚拟化,在互联网上进行了大量挖掘之后,我发现了以下来自 VMWare 的引用:
Memory protection extensions (MPX) were introduced in Intel Skylake generation CPUs and provided hardware support for bound checking. This feature will not be supported in Intel CPUs beginning with the Ice Lake generation.
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
文件。
cpuid.enableMPX = "TRUE"
在我这样做之后,一切正常,我能够使用 xsetbv
启用 xcr0
.
的 bndreg
和 bndcsr
位
当使用 VMWare 将 CPU 功能从主机公开到来宾时,在更正常的情况下(即该功能不受弃用的困扰)您可以屏蔽 cpuid
的位将以下内容添加到 VM 的 .vmx
文件中。
cpuid.<leaf>.<register> = "<value>"
因此,例如,如果我们假设 SMAP 可以通过这种方式公开,我们会想要设置 cpuid
叶子 7 的第 20 位。
cpuid.7.ebx = "----:----:---1:----:----:----:----:----"
冒号是可选的,以便于阅读字符串,1 和 0 覆盖任何默认设置,破折号用于保留默认设置。
我正在尝试写入 x86_64 Debian v7 虚拟机上的扩展控制寄存器 0 (xcr0
)。我这样做的方法是通过一个带有一些内联汇编的内核模块(所以 CPL=0
)。但是,当我尝试执行 xsetbv
指令时,我不断收到一般保护错误 (#GP
)。
我的模块的 init
函数首先检查 osxsave
位是否设置在控制寄存器 4 (cr4
) 中。如果不是,则设置它。然后,我使用 xgetbv
读取 xcr0
寄存器。这工作正常并且(在我所做的有限测试中)具有值 0b111
。我想设置 bndreg
和 bndcsr
位,它们是第 3 位和第 4 位(0 索引),所以我做了一些 OR
ing 并将 0b11111
写回xcr0
使用 xsetbv
。实现这最后一部分的代码如下。
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 [=10=], %%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
并且只是读取它的值并将其写回,那么事情似乎工作正常。查看Intel手册和this site,我发现了#GP
的各种原因,但其中none似乎符合我的情况。原因如下以及我对它们很可能不适用的原因的解释。
如果当前权限级别不为0 --> 我用一个内核模块来实现
CPL=0
如果在
%ecx
中指定了无效的xcr
--> 0 在%ecx
中有效并且适用于xgetbv
If the value in
edx:eax
sets bits that are reserved in thexcr
specified byecx
--> according to the Intel manual and Wikipedia 我设置的位没有保留If attempt to clear bit 0 of
xcr0
--> 我在设置之前打印了xcr0
,结果是0b11111
如果尝试将
xcr0[2:1]
设置为0b10
--> 我在设置之前打印了xcr0
,结果是0b11111
提前感谢您提供的任何帮助,以了解为什么会发生这种 #GP
。
/proc/cpuinfo
on the VM doesn't list mpx in the flags (it does list xsave though). My host does have MPX support though. I am running Linux kernel version 3.19 which does support MPX and I already have a binary compiled with MPX (the bnd instructions etc. are all there when I objdump). The problem is that the instructions get treated as NOPs. I thought the process I described above would fix this and enable the CPU to recognize MPX.
如果您在支持 MPX 的机器上 运行 它将启用 MPX。 (假设您的代码是正确的。)
虚拟 x86 CPU 你的 VM 运行ning 上没有,根据它自己的虚拟化 CPUID,所以这个错误一点也不奇怪。管理程序可能在 VMEXIT 中手动执行此操作,模拟 xsetbv
并检查对虚拟化 xcr0 的更改。
如果您想使用您的硬件具有但您的 VM 不支持的功能,通常您必须 运行 在裸机上。或者找一个不同的 VM 向来宾公开该功能。
请注意,MPX 引入了新的架构状态(bnd
寄存器),在上下文切换时必须获得 saved/restored。如果您的管理程序不想这样做,那将是禁用 MPX 的原因之一。 (我认为它可以获得 saved/restored 作为 xsave
的一部分,但它确实使节省的空间稍微大一些。)我没怎么看过 MPX;这可能是管理程序必须在 vmexits 中处理的事情,以便不对管理程序应用边界检查...如果是这样,那将是一个很大的不便。
Peter Cordes 是对的,这是我的管理程序的问题。我正在使用 VMWare Fusion 进行虚拟化,在互联网上进行了大量挖掘之后,我发现了以下来自 VMWare 的引用:
Memory protection extensions (MPX) were introduced in Intel Skylake generation CPUs and provided hardware support for bound checking. This feature will not be supported in Intel CPUs beginning with the Ice Lake generation.
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
文件。
cpuid.enableMPX = "TRUE"
在我这样做之后,一切正常,我能够使用 xsetbv
启用 xcr0
.
bndreg
和 bndcsr
位
当使用 VMWare 将 CPU 功能从主机公开到来宾时,在更正常的情况下(即该功能不受弃用的困扰)您可以屏蔽 cpuid
的位将以下内容添加到 VM 的 .vmx
文件中。
cpuid.<leaf>.<register> = "<value>"
因此,例如,如果我们假设 SMAP 可以通过这种方式公开,我们会想要设置 cpuid
叶子 7 的第 20 位。
cpuid.7.ebx = "----:----:---1:----:----:----:----:----"
冒号是可选的,以便于阅读字符串,1 和 0 覆盖任何默认设置,破折号用于保留默认设置。