修改内核模块中的控制寄存器

时间:2015-01-15 00:23:06

标签: c linux linux-kernel kernel-module

以下简单内核模块在加载后设置cr4寄存器的第13位(CR4.VMXE)并在退出时清除该位。

vmx.c

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

MODULE_LICENSE("GPL");

static inline uint64_t getcr4(void) {
    register uint64_t ret = 0;

    asm volatile (
        "movq %%cr4, %0\n"
        :"=r"(ret)
    );

    return ret;
}

static inline void setcr4(register uint64_t val) {
    asm volatile (
        "movq %0, %%cr4\n"
        :
        :"r"(val)
    );
}

static int __init init_routine(void) {
    uint64_t cr4 = getcr4();

    printk(KERN_INFO "VTX Test loaded: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
    cr4 |= (1 << 13);
    setcr4(cr4);
    cr4 = getcr4();
    printk(KERN_INFO "cr4: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));

    return 0;
}

static void __exit exit_routine(void) {
    uint64_t cr4 = getcr4();

    printk(KERN_INFO "cr4: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
    cr4 &= ~(1 << 13);
    setcr4(cr4);
    cr4 = getcr4();
    printk(KERN_INFO "VTX Test exited: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
}

module_init(init_routine);
module_exit(exit_routine);

生成文件

obj-m += vmx.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

要运行模块,请使用make clean && make && sudo insmod vmx.ko && sudo rmmod vmx && sudo dmesg -c。这有时会给我以下(预期)输出

[ 2295.121537] VTX Test loaded: 1312736 (0).
[ 2295.121540] cr4: 1320928 (1).
[ 2295.123975] cr4: 1320928 (1).
[ 2295.123977] VTX Test exited: 1312736 (0).

有时还有以下内容:

[ 2296.256982] VTX Test loaded: 1320928 (1).
[ 2296.256984] cr4: 1320928 (1).
[ 2296.259481] cr4: 1312736 (0).
[ 2296.259483] VTX Test exited: 1312736 (0).

secound输出中的第二行和第三行对我来说似乎很奇怪,因为看起来修改后的控制寄存器cr4在离开init_routine后已被重置。另外奇怪的是,在第一行中似乎设置了VMXE位,这实际上没有任何意义。这种行为是否正常?怎么解释?可能有另一个内核模块运行修改CR4?这看起来很奇怪,因为我已经看到了几个VTX实现,它们都在初始化例程中设置VMXE位,并以与此模块相同的方式清除其出口例程中的位。

2 个答案:

答案 0 :(得分:0)

您可能已加载kvm模块(以及关联的kvm-intelkvm-amd)内核模块。这些模块已经管理了处理器的VT状态,如果你开始在其他地方修改它,很可能非常困惑

查看内核源代码中的arch/x86/kvm目录,了解已存在的内容。

答案 1 :(得分:0)

事实证明,问题是所有CPU内核都没有修改寄存器。为了确保修改发生在所有核心上,似乎足以调用on_each_cpu。固定代码如下,Makefile不变。

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

MODULE_LICENSE("GPL");

static inline uint64_t getcr4(void) {
    register uint64_t ret = 0;

    asm volatile (
        "movq %%cr4, %0\n"
        :"=r"(ret)
    );

    return ret;
}

static inline void setcr4(register uint64_t val) {
    asm volatile (
        "movq %0, %%cr4\n"
        :
        :"r"(val)
    );
}

static void setvmxe(void* info) {
    uint64_t cr4 = getcr4();

    cr4 |= (1 << 13);
    setcr4(cr4);
}

static void clearvmxe(void* info) {
    uint64_t cr4 = getcr4();

    cr4 &= ~(1 << 13);
    setcr4(cr4);
}

static int __init init_routine(void) {
    uint64_t cr4 = getcr4();

    printk(KERN_INFO "VTX Test loaded: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
    on_each_cpu(setvmxe, NULL, 0);
    cr4 = getcr4();
    printk(KERN_INFO "cr4: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));

    return 0;
}

static void __exit exit_routine(void) {
    uint64_t cr4 = getcr4();

    printk(KERN_INFO "cr4: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
    on_each_cpu(clearvmxe, NULL, 0);
    cr4 = getcr4();
    printk(KERN_INFO "VTX Test exited: %llu (%u).\n", cr4, (unsigned char)((cr4 >> 13) & 1));
}

module_init(init_routine);
module_exit(exit_routine);