Linux内核模块:设置CR4.VMXE不持久

wvmv3b1j  于 2022-11-22  发布在  Linux
关注(0)|答案(1)|浏览(307)

我在XUbuntu 16.04上使用VMX,但是我在设置CR 4的VMXE位时遇到了一些问题。问题是当我调用退出函数时,该位不再被设置。

虚拟机修改. c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>

#define AUTHOR "me"
#define DESC "Test"

extern u64 read_cr4(void);
extern void write_cr4(u64 val);

static bool IsVMXEEnabled(void)
{
    return (read_cr4() >> 13) & 1;
}

static void SetVMXEEnabled(void* _val)
{
    bool val = *(bool*)_val;
    u64 mask = (1 << 13);
    u64 cr4 = read_cr4();

    if (val)
        cr4 |= mask;
    else
        cr4 &= (~mask);

    write_cr4(cr4);
}

static void LogVMXEState(void* info)
{
    (void) info;
    printk(KERN_INFO "CR4: %08LX\n", read_cr4());
}

static int __init init_(void)
{
    printk(KERN_INFO "===================================\n");

    if (IsVMXEEnabled())
        printk(KERN_INFO "VMXE Is Enabled\n");
    else
    {
        bool new_vmxe_state = true;
        printk(KERN_INFO "Enabling VMXE\n");
        on_each_cpu(SetVMXEEnabled, &new_vmxe_state, 1);

        if (IsVMXEEnabled())
        {
            printk(KERN_INFO "VMXE Has Been Enabled\n");
            on_each_cpu(LogVMXEState, NULL, 1);
        }
        else
        {
            printk(KERN_INFO "VMXE Could Not Be Enabled\n");
            return -1;
        }
    }
    return 0;
}

static void __exit exit_(void)
{
    printk(KERN_INFO "----------------------------------------\n");

    on_each_cpu(LogVMXEState, NULL, 1);
    if (IsVMXEEnabled())
    {
        bool new_val = false;
        printk(KERN_INFO "Disabling VMXE\n");
        on_each_cpu(SetVMXEEnabled, &new_val, 1);

        if (!IsVMXEEnabled())
            printk(KERN_INFO "VMXE Has Been Disabled\n");
        else
            printk(KERN_INFO "Couldn't disabled VMXE...\n");
    }
    else
        printk(KERN_INFO "VMXE Wasn't enabled?\n");

    printk(KERN_INFO "===================================\n");
}

MODULE_LICENSE("GPL");

MODULE_AUTHOR(AUTHOR);
MODULE_DESCRIPTION(DESC);

module_init(init_);
module_exit(exit_);

vmasm.S中指定的值

.intel_syntax noprefix
.text

.global read_cr4
read_cr4:
    mov rax, cr4
    ret

.global write_cr4
write_cr4:
    mov cr4, rdi
    ret

生成文件

obj-m += testmod.o
testmod-objs := vmmod.o vmasm.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

测试

$> sudo insmod testmod.ko && sudo rmmod testmod

输出

[  607.459248] ===================================
[  607.459256] Enabling VMXE
[  607.459302] VMXE Has Been Enabled
[  607.459311] CR4: 000426E0
[  607.459315] CR4: 000426E0
[  607.459318] CR4: 000426E0
[  607.459321] CR4: 000426F0
[  607.459334] CR4: 000426E0
[  607.459336] CR4: 000426E0
[  607.459338] CR4: 000426E0
[  607.459373] CR4: 000426E0
[  607.473007] ----------------------------------------
[  607.473025] CR4: 000406E0
[  607.473065] CR4: 000406E0
[  607.473068] CR4: 000406F0
[  607.473072] CR4: 000406E0
[  607.473074] CR4: 000406E0
[  607.473078] CR4: 000406E0
[  607.473080] CR4: 000406E0
[  607.473103] CR4: 000406E0
[  607.473121] VMXE Wasn't enabled?
[  607.473129] ===================================

输出清楚地显示,CR 4的位13(VMXE)在模块加载功能之后使能,但在模块卸载功能期间不再置位。
是否有会定期重置VMXE的内核模块?我在运行此代码时卸载了kvm.ko和kvm_intel.ko,并且启用了英特尔仿真BIOS设置,CPU支持VMX。
根据(Modifying control register in kernel module),我尝试添加on_each_cpu以在每个CPU内核上设置VMXE,但没有帮助。
有什么想法吗?
谢谢你!

hkmswyz6

hkmswyz61#

Linux内核并没有刻意地将CR4.VMXE设置为CR4.VMXE。相反,Linux会缓存CR 4的值,并使用该高速缓存而不是阅读寄存器,这可能是出于性能原因。由于您没有更改缓存,因此下次内核试图清除CR 4中的位时,它将从缓存中恢复VMXE位,将其清除为零。如果您的驱动程序已经建立了VMXON区域,而是在内核无意中清除了具有活动VMXON区域的CR4.VMXE时看到内核崩溃。
据我所知,没有任何东西会定期重置CR 4位。然而,TLB命中是很常见的,如果任何被无效的页面是全局的,* 唯一 * 的方法就是清除CR4.PGE。我不知道为什么全局页面会频繁地被无效,但我知道我的一个同事不得不调试一个问题,这个问题开始于4. 4. 0系列内核,是由CR 4. PGE被清除引起的,所以它肯定会以一定的频率发生。
启用CR 4功能位的正确方式与内核本身的方式相同,例如在/arch/x86/kernel/cpu/common.c中:

static __always_inline void setup_smep(struct cpuinfo_x86 *c)
{
    if (cpu_has(c, X86_FEATURE_SMEP))
        cr4_set_bits(X86_CR4_SMEP);
}

最后调用此函数:

void cr4_update_irqsoff(unsigned long set, unsigned long clear)
{
    unsigned long newval, cr4 = this_cpu_read(cpu_tlbstate.cr4);

    lockdep_assert_irqs_disabled();

    newval = (cr4 & ~clear) | set;
    if (newval != cr4) {
        this_cpu_write(cpu_tlbstate.cr4, newval);
        __write_cr4(newval);
    }
}

请注意,它没有调用__read_cr4(),而是调用this_cpu_read(cpu_tlbstate.cr4)。如果您希望内核停止禁用CR4.VMXE,则必须更新该高速缓存。

相关问题