如何从Linux用户和内核模式知道CR寄存器的值 [英] How to know the values of CR registers from linux user and kernel modes

查看:312
本文介绍了如何从Linux用户和内核模式知道CR寄存器的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道x86上的CR0-CR4寄存器值。我可以编写内联汇编以将其读出吗?还有其他方法吗? (例如,OS是否保留任何文件结构来记录这些值)?

解决方案

Linux内核具有读写Control的功能。寄存器,它们是标准CR和 xgetbv read_crX write_crX 函数。 c $ c>, xsetbv 用于扩展CR。



用户模式应用程序需要LKM才能间接使用这些功能。

从理论上讲,您只需要创建一个具有一个或多个设备的LKM并通过从CR读取或写入来处理IO请求。实际上,您通常有多个CPU,因此您需要处理MP。



我将内核模块用于 CPUID 作为模板并创建此LKM。



代码没有任何保证,仅在64位VM上的DEBIAN 8上进行了测试

  #include< linux / module.h> / *所有模块都需要* / 
#include< linux / kernel.h> / * KERN_INFO需要* /
#include< linux / fs.h> / * KERN_INFO需要* /
#include< linux / types.h>
#include< linux / errno.h>
#include< linux / fcntl.h>
#include< linux / init.h>
#include< linux / poll.h>
#include< linux / smp.h>
#include< linux / major.h>
#include< linux / fs.h>
#include< linux / device.h>
#include< linux / cpu.h>
#include< linux / notifier.h>
#include< linux / uaccess.h>
#include< linux / gfp.h>
#include< asm / processor.h>
#include< asm / msr.h>
#include< asm / xcr.h>

#define MAKE_MINOR(cpu,reg)(cpu << 8 | reg)
#define GET_MINOR_REG(minor)(minor& 0xff)
#define GET_MINOR_CPU(minor )(次要>> 8)
#定义XCR_MINOR_BASE 0x80

static int major_n = 0;
静态结构类* ctrlreg_class;


struct ctrlreg_info
{
unsigned int reg;
个无符号长值;
unsigned int错误;
};

静态无效ctrlreg_smp_do_read(void * p)
{
struct ctrlreg_info * info = p;
info->错误= 0;

printk(KERN_INFO ctrlreg:do read of reg%u\n,info-> reg);

开关(info-> reg)
{
case 0:info-> value = read_cr0();打破;
情况2:info-> value = read_cr2();打破;
情况3:info-> value = read_cr3();打破;
情况4:info-> value = read_cr4();打破;

#ifdef CONFIG_X86_64
情况8:info-> value = read_cr8();打破;
#endif

case XCR_MINOR_BASE:info-> value = xgetbv(0);打破;

默认值:
info->错误= -EINVAL;
}
}

静态无效ctrlreg_smp_do_write(void * p)
{
struct ctrlreg_info * info = p;
info->错误= 0;

开关(info-> reg)
{
case 0:write_cr0(info-> value);打破;
情况2:write_cr2(info-> value);打破;
情况3:write_cr3(info-> value);打破;
情况4:write_cr4(info-> value);打破;

#ifdef CONFIG_X86_64
情况8:read_cr8();打破;
#endif

case XCR_MINOR_BASE:xgetbv(0);打破;

默认值:
info->错误= -EINVAL;
}
}


静态ssize_t ctrlreg_read(结构文件* file,char __user * buf,size_t count,loff_t * ppos)
{
unsigned int minor = iminor(file_inode(file));
unsigned int cpu = GET_MINOR_CPU(未成年人);
unsigned int reg = GET_MINOR_REG(minor);
struct ctrlreg_info info = {.reg = reg};
int err;

printk(KERN_INFO ctrlreg:读取cpu%u reg%u\n,cpu,reg);
printk(KERN_INFO ctrlreg:读取%zu个字节\n,计数);

if(count< sizeof(unsigned long))
return -EINVAL;

printk(KERN_INFO ctrlreg:调度读取);

err = smp_call_function_single(cpu,ctrlreg_smp_do_read,& info,1);
if(IS_ERR_VALUE(err))
return err;

printk(KERN_INFO ctrlreg:读取成功:%x\n,信息错误);

如果(IS_ERR_VALUE(info.error))
返回错误;

err = copy_to_user(buf,& info.value,sizeof(unsigned long)));

printk(KERN_INFO ctrlreg:读取复制结果:%x(%lu)\n,err,sizeof(unsigned long));

如果(IS_ERR_VALUE(err))
返回错误;

printk(KERN_INFO ctrlreg:read done\n);

return sizeof(unsigned long);
}


静态ssize_t ctrlreg_write(结构文件* file,const char __user * buf,size_t count,loff_t * ppos)
{
unsigned int次要= iminor(file_inode(file));
unsigned int cpu = GET_MINOR_CPU(未成年人);
unsigned int reg = GET_MINOR_REG(minor);
struct ctrlreg_info info = {.reg = reg};
int err;

printk(KERN_INFO ctrlreg:为cpu%u reg%u\n写,cpu,reg);
printk(KERN_INFO ctrlreg:写入%zu个字节\n,计数);

if(count< sizeof(unsigned long))
return -EINVAL;

printk(KERN_INFO ctrlreg:调度write\n);

err = copy_from_user((void *)buf,& info.value,sizeof(unsigned long));

printk(KERN_INFO ctrlreg:写副本数据:%x(%lu)\n,err,sizeof(unsigned long));

如果(IS_ERR_VALUE(err))
返回错误;

err = smp_call_function_single(cpu,ctrlreg_smp_do_write,& info,1);
if(IS_ERR_VALUE(err))
return err;

printk(KERN_INFO ctrlreg:成功写入:%x\n,info.error);

如果(IS_ERR_VALUE(info.error))
返回错误;

printk(KERN_INFO ctrlreg:写完成\n);

return sizeof(unsigned long);
}

static void ctrlreg_can_open(void * p)
{
unsigned int * reg = p;
unsigned int reg_num = * reg;
unsigned int ebx,edx,eax,ecx;
unsigned int support_xgetbv,support_ia32e;

* reg = 0; //成功

printk(KERN_INFO ctrlreg:can open reg%u\n,reg_num);

如果(reg_num< = 4&® num!= 1)
返回;

#ifdef CONFIG_X86_64
如果(reg_num == 8)
返回;
#endif



cpuid_count(0x0d,1,& eax,& ebx,& ecx,& edx);

support_xgetbv = cpuid_ecx(1)& 0x04000000;
support_ia32e = cpuid_edx(0x80000001)& 0x20000000;

printk(KERN_INFO ctrlreg:xgetbv =%d\n,support_xgetbv);
printk(KERN_INFO ctrlreg:ia32e =%d\n,support_ia32e);

如果(support_xgetbv&& support_ia32e)
返回;

printk(KERN_INFO ctrlreg:打开被拒绝);

* reg = -EIO;
}

static int ctrlreg_open(struct inode * inode,结构文件* file)
{
unsigned int cpu;
unsigned int reg;
未签名的int未成年人;
int err;



minor = iminor(file_inode(file));
cpu = GET_MINOR_CPU(较小);
reg = GET_MINOR_REG(未成年人);

printk(KERN_INFO ctrlreg:cpu%u reg%u\n的打开设备,cpu,reg);

if(cpu> = nr_cpu_ids ||!cpu_online(cpu))
return -ENXIO; / *没有这样的CPU * /

err = smp_call_function_single(cpu,ctrlreg_can_open,& reg,1);
if(IS_ERR_VALUE(err))
return err;

返还reg;
}


静态const struct file_operations ctrlreg_fops =
{
.owner = THIS_MODULE,
.read = ctrlreg_read,
.write = ctrlreg_write,
.open = ctrlreg_open
};


static int ctrlreg_device_create(int cpu)
{
struct device * dev = NULL;
int i;

printk(KERN_INFO ctrlreg:为CPU%d\n创建设备,cpu);




// CR0,2-4,8
for(i = 0; i< = 8; i ++)
{
如果((i>& i< 8)|| i == 1)
继续; //跳过不存在的规则

printk(KERN_INFO ctrlreg:device cpu%dcr%d\n,cpu,i);
dev = device_create(ctrlreg_class,NULL,MKDEV(major_n,MAKE_MINOR(cpu,i)),NULL, cpu%dcr%d,cpu,i);
如果(IS_ERR(dev))
返回PTR_ERR(dev);
}

// XCR0
for(i = 0; i< = 0; i ++)
{
printk(KERN_INFO ctrlreg:device cpu%dxcr%d\n,cpu,i);
dev = device_create(ctrlreg_class,NULL,MKDEV(major_n,MAKE_MINOR(cpu,(XCR_MINOR_BASE + i)))),NULL, cpu%dxcr%d,cpu,i);
如果(IS_ERR(dev))
返回PTR_ERR(dev);
}

返回0;
}

static void ctrlreg_device_destroy(int cpu)
{
int i;

// CR0,2-4,8
for(i = 0; i <= 8; i ++)
{
if((i> 4 && i< 8)|| i == 1)
继续; //跳过不存在的reg

device_destroy(ctrlreg_class,MKDEV(major_n,MAKE_MINOR(cpu,i)));
}

// XCR0
for(i = 0; i< = 0; i ++)
device_destroy(ctrlreg_class,MKDEV(major_n,MAKE_MINOR(cpu, (XCR_MINOR_BASE + i))));
}


静态整数ctrlreg_class_cpu_callback(struct notifier_block * nfb,unsigned long action,void * hcpu)
{
unsigned int cpu =(unsigned long )hcpu;
int err = 0;

开关(操作)
{
case CPU_UP_PREPARE:
err = ctrlreg_device_create(cpu);
休息时间;

case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
ctrlreg_device_destroy(cpu);
休息时间;
}
return notifier_from_errno(err);
}

静态结构notifier_block __refdata ctrlreg_class_cpu_notifier =
{
.notifier_call = ctrlreg_class_cpu_callback,
};

静态字符* ctrlreg_devnode(结构设备* dev,umode_t * mode)
{
unsigned int minor = MINOR(dev-> devt),cpu = GET_MINOR_CPU(minor) ,reg = GET_MINOR_REG(minor);

if(reg< XCR_MINOR_BASE)
return kasprintf(GFP_KERNEL, crs / cpu%u / cr%u,cpu,reg);
else
return kasprintf(GFP_KERNEL, crs / cpu%u / xcr%u,cpu,reg-XCR_MINOR_BASE);
}


int __init ctrlreg_init(void)
{
int err = 0,i = 0;

printk(KERN_INFO ctrlreg:init\n);

if((major_n = __register_chrdev(0,0,NR_CPUS, crs,& ctrlreg_fops))< 0)
return major_n;

printk(KERN_INFO ctrlreg:主号码是%u\n,major_n);



ctrlreg_class = class_create(THIS_MODULE, ctrlreg\n);
if(IS_ERR(ctrlreg_class))
{
err = PTR_ERR(ctrlreg_class);
goto out_chrdev;
}

printk(KERN_INFO ctrlreg:class created\n);

ctrlreg_class-> devnode = ctrlreg_devnode;

cpu_notifier_register_begin();
for_each_online_cpu(i)
{
err = ctrlreg_device_create(i);
if(IS_ERR_VALUE(err))
goto out_class;
}

__register_hotcpu_notifier(& ctrlreg_class_cpu_notifier);
cpu_notifier_register_done();

printk(KERN_INFO ctrlreg:初始化成功\n);

err = 0;
退出;

out_class:
i = 0;
for_each_online_cpu(i)
{
ctrlreg_device_destroy(i);
}
cpu_notifier_register_done();
class_destroy(ctrlreg_class);

out_chrdev:
__unregister_chrdev(CPUID_MAJOR,0,NR_CPUS, ctrlreg);
out:
返回错误;
}


静态void __exit ctrlreg_exit(void)
{
int cpu = 0;

cpu_notifier_register_begin();
for_each_online_cpu(cpu)
ctrlreg_device_destroy(cpu);
class_destroy(ctrlreg_class);
__unregister_chrdev(CPUID_MAJOR,0,NR_CPUS, ctrlreg);
__unregister_hotcpu_notifier(& ctrlreg_class_cpu_notifier);
cpu_notifier_register_done();
}

module_init(ctrlreg_init);
module_exit(ctrlreg_exit);

MODULE_LICENSE(双BSD / GPL);
MODULE_AUTHOR( Kee Nemesis 241);
MODULE_DESCRIPTION(读写控制寄存器);

此模块创建以下dev节点:

  / dev / crs / cpu0 / cr0 
/ dev / crs / cpu0 / cr2
/ dev / crs / cpu0 / cr3
/ dev / crs / cpu0 / cr4
/ dev / crs / cpu0 / cr8
/ dev / crs / cpu0 / xcr0

/ dev / crs / cpu1 / cr0
/ dev / crs / cpu1 / cr2
/ dev / crs / cpu1 / cr3
/ dev / crs / cpu1 / cr4
/ dev / crs / cpu1 / cr8
/ dev / crs / cpu1 / xcr0

...

您可以读写这些开发节点。在32位系统上,最小读写长度为4个字节,在64位系统上,最小读写长度为8个字节(Linux仍会进行一些缓冲)。



要编译此LKM,请保存上面的代码为 ctrlreg.c 并创建此 Makefile

  obj- m + = ctrlreg.o 

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

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

然后使用 make 来获取 ctrlreg.ko



要加载模块,请使用 sudo insmod ctrlreg.ko ,将其删除 sudo rmmod ctrlreg



我还编写了一个小的用户模式实用程序来读取CR:

代码没有任何担保,已在DEBIAN 8 ON 64上进行了测试仅位虚拟机

  #include< stdio.h> 
#include< stdlib.h>

#define MAX_PATH 256

int main(int argc,char * argv [])
{
unsigned long cpu,reg;
FILE * fin;
char device [MAX_PATH];
个未签名的长数据;

if(argc< 3 || argc> 4)
return fprintf(stderr,用法:\n\t\t cr cpu reg [value] \ n),1;

if(sscanf(argv [1], cpu%u,& cpu)!= 1)
返回fprintf(stderr, cpu的无效值'%s' \n,argv [1]),2;

if(sscanf(argv [2], cr%u,& reg)!= 1&& sscanf(argv [2], xcr%u,& reg )!= 1)
返回fprintf(stderr, reg\n的值'%s'无效,argv [2]),3;

if(argc == 4&& sscanf(argv [3],%lu,& data)!= 1)
返回fprintf(stderr,无效的数值值'%s'\n,argv [3]),6;

snprintf(device,MAX_PATH, / dev / crs / cpu%u /%s,cpu,argv [2]);

fin = fopen(device,argc == 4? wb: rb);

if(!fin)
return fprintf(stderr,无法打开设备%s\n,设备),4;

if(argc == 4)
{
if(fwrite(& data,sizeof(data),1,fin)!= 1)
返回fprintf(stderr,无法写入设备%s(%d)\n,设备,ferror(fin)),5;
}
else
{
if(fread(& data,sizeof(data),1,fin)!= 1)
返回fprintf(stderr,无法读取设备%s(%d)\n,设备,ferror(fin)),7;

printf(%016x\n,数据);
}



fclose(fin);
返回0;

}

将代码另存为cr.c并进行编译。 / p>

要读取第二个CPU的cr0,可以使用:

cr cpu1 cr0



将值0(小心)写入其中

cr cpu1 cr0 0


I would like to know the CR0-CR4 register values on x86. Can I write inline assembly to read it out? Are there any other methods? (e.g., does OS keep any file structures to record these values)

解决方案

The Linux kernel has some function to read and write Control Registers, they are the read_crX and write_crX functions for the standard CR and xgetbv,xsetbv for the extended CR.

User mode applications need a LKM to indirectly use these functions.
In theory you just need to create a LKM with one or more devices and handle IO requests by reading or writing from CR. In practice you usually have more than one CPU, so you need to handle MP.

I used the kernel module for CPUID as a template and create this LKM.

CODE IS WITHOUT ANY WARRANTY, TESTED ON DEBIAN 8 ON 64 bit VM ONLY

#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */
#include <linux/fs.h>   /* Needed for KERN_INFO */
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/uaccess.h>
#include <linux/gfp.h>
#include <asm/processor.h>
#include <asm/msr.h>
#include <asm/xcr.h>

#define MAKE_MINOR(cpu, reg) (cpu<<8 | reg)
#define GET_MINOR_REG(minor) (minor & 0xff)
#define GET_MINOR_CPU(minor) (minor >> 8)
#define XCR_MINOR_BASE  0x80 

static int major_n = 0;
static struct class *ctrlreg_class;


struct ctrlreg_info
{
    unsigned int reg;
    unsigned long value;
    unsigned int error;
};

static void ctrlreg_smp_do_read(void* p)
{
    struct ctrlreg_info* info = p;
    info->error = 0;

    printk(KERN_INFO "ctrlreg: do read of reg%u\n", info->reg);

    switch (info->reg)
    {
        case 0: info->value = read_cr0(); break;
        case 2: info->value = read_cr2(); break;
        case 3: info->value = read_cr3(); break;
        case 4: info->value = read_cr4(); break;

#ifdef CONFIG_X86_64
        case 8: info->value = read_cr8(); break;
#endif

        case XCR_MINOR_BASE: info->value = xgetbv(0); break;

        default:
            info->error =  -EINVAL;
    }   
}

static void ctrlreg_smp_do_write(void* p)
{
    struct ctrlreg_info* info = p;
    info->error = 0;

    switch (info->reg)
    {
        case 0: write_cr0(info->value); break;
        case 2: write_cr2(info->value); break;
        case 3: write_cr3(info->value); break;
        case 4: write_cr4(info->value); break;

#ifdef CONFIG_X86_64
        case 8: read_cr8(); break;
#endif

        case XCR_MINOR_BASE: xgetbv(0); break;

        default:
            info->error =  -EINVAL;
    }   
}


static ssize_t ctrlreg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    unsigned int minor = iminor(file_inode(file));
    unsigned int cpu = GET_MINOR_CPU(minor);
    unsigned int reg = GET_MINOR_REG(minor);
    struct ctrlreg_info info = {.reg = reg};
    int err;

    printk(KERN_INFO "ctrlreg: read for cpu%u reg%u\n", cpu, reg);
    printk(KERN_INFO "ctrlreg: read of %zu bytes\n", count);

    if (count < sizeof(unsigned long))
        return -EINVAL;

    printk(KERN_INFO "ctrlreg: scheduling read\n");

    err = smp_call_function_single(cpu, ctrlreg_smp_do_read, &info, 1);
    if (IS_ERR_VALUE(err))
        return err;

    printk(KERN_INFO "ctrlreg: read success: %x\n", info.error);

    if (IS_ERR_VALUE(info.error))
        return err;

    err = copy_to_user(buf, &info.value, sizeof(unsigned long));

    printk(KERN_INFO "ctrlreg: read copy result: %x ( %lu )\n", err, sizeof(unsigned long));

    if (IS_ERR_VALUE(err))
        return err;

    printk(KERN_INFO "ctrlreg: read done\n");

    return sizeof(unsigned long);
}


static ssize_t ctrlreg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    unsigned int minor = iminor(file_inode(file));
    unsigned int cpu = GET_MINOR_CPU(minor);
    unsigned int reg = GET_MINOR_REG(minor);
    struct ctrlreg_info info = {.reg = reg};
    int err;

    printk(KERN_INFO "ctrlreg: write for cpu%u reg%u\n", cpu, reg);
    printk(KERN_INFO "ctrlreg: write of %zu bytes\n", count);

    if (count < sizeof(unsigned long))
        return -EINVAL;

    printk(KERN_INFO "ctrlreg: scheduling write\n");

    err = copy_from_user((void*)buf, &info.value, sizeof(unsigned long));

    printk(KERN_INFO "ctrlreg: write copy data: %x ( %lu )\n", err, sizeof(unsigned long));

    if (IS_ERR_VALUE(err))
        return err;

    err = smp_call_function_single(cpu, ctrlreg_smp_do_write, &info, 1);
    if (IS_ERR_VALUE(err))
        return err;

    printk(KERN_INFO "ctrlreg: write success: %x\n", info.error);

    if (IS_ERR_VALUE(info.error))
        return err;

    printk(KERN_INFO "ctrlreg: write done\n");

    return sizeof(unsigned long);
}

static void ctrlreg_can_open(void *p)
{
    unsigned int* reg = p;
    unsigned int reg_num = *reg;
    unsigned int ebx, edx, eax, ecx;
    unsigned int support_xgetbv, support_ia32e;

    *reg = 0;   //Success

    printk(KERN_INFO "ctrlreg: can open reg %u\n", reg_num);

    if (reg_num <= 4 && reg_num != 1)
        return;

#ifdef CONFIG_X86_64
    if (reg_num == 8)
        return;
#endif  



    cpuid_count(0x0d, 1, &eax, &ebx, &ecx, &edx);

    support_xgetbv = cpuid_ecx(1) & 0x04000000;
    support_ia32e = cpuid_edx(0x80000001) & 0x20000000;

    printk(KERN_INFO "ctrlreg: xgetbv = %d\n", support_xgetbv);
    printk(KERN_INFO "ctrlreg: ia32e = %d\n", support_ia32e);

    if (support_xgetbv && support_ia32e)
        return;

    printk(KERN_INFO "ctrlreg: open denied");  

    *reg = -EIO;
}

static int ctrlreg_open(struct inode *inode, struct file *file)
{
    unsigned int cpu;
    unsigned int reg;
    unsigned int minor;
    int err;



    minor = iminor(file_inode(file));
    cpu = GET_MINOR_CPU(minor);
    reg = GET_MINOR_REG(minor);

    printk(KERN_INFO "ctrlreg: open device for cpu%u reg%u\n", cpu, reg);

    if (cpu >= nr_cpu_ids || !cpu_online(cpu))
        return -ENXIO;  /* No such CPU */

    err  = smp_call_function_single(cpu, ctrlreg_can_open, &reg, 1);
    if (IS_ERR_VALUE(err))
        return err;

    return reg;
}


static const struct file_operations ctrlreg_fops = 
{
    .owner = THIS_MODULE,
    .read = ctrlreg_read,
    .write = ctrlreg_write,
    .open = ctrlreg_open
};


static int ctrlreg_device_create(int cpu)
{
    struct device *dev = NULL;
    int i;

    printk(KERN_INFO "ctrlreg: device create for cpu %d\n", cpu);




    //CR0, 2-4, 8
    for (i = 0; i <= 8; i++)
    {
        if ((i>4 && i<8) || i == 1)
            continue;       //Skip non existent regs

        printk(KERN_INFO "ctrlreg: device cpu%dcr%d\n", cpu, i);
        dev = device_create(ctrlreg_class, NULL, MKDEV(major_n, MAKE_MINOR(cpu, i)), NULL, "cpu%dcr%d", cpu, i);
        if (IS_ERR(dev))
          return PTR_ERR(dev);
    }   

    //XCR0
    for (i = 0; i <= 0; i++)
    {
        printk(KERN_INFO "ctrlreg: device cpu%dxcr%d\n", cpu, i);
        dev = device_create(ctrlreg_class, NULL, MKDEV(major_n, MAKE_MINOR(cpu, (XCR_MINOR_BASE+i))), NULL, "cpu%dxcr%d", cpu, i);
        if (IS_ERR(dev))
          return PTR_ERR(dev);
    }

    return 0;
}

static void ctrlreg_device_destroy(int cpu)
{
    int i;

    //CR0, 2-4, 8
    for (i = 0; i <= 8; i++)
    {
        if ((i>4 && i<8) || i == 1)
            continue;       //Skip non existent regs

        device_destroy(ctrlreg_class, MKDEV(major_n, MAKE_MINOR(cpu, i)));
    }

    //XCR0
    for (i = 0; i <= 0; i++)
        device_destroy(ctrlreg_class, MKDEV(major_n, MAKE_MINOR(cpu, (XCR_MINOR_BASE+i))));
}


static int ctrlreg_class_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
    unsigned int cpu = (unsigned long)hcpu;
    int err = 0;

    switch (action) 
    {
        case CPU_UP_PREPARE:
            err = ctrlreg_device_create(cpu);
        break;

        case CPU_UP_CANCELED:
        case CPU_UP_CANCELED_FROZEN:
        case CPU_DEAD:
            ctrlreg_device_destroy(cpu);
        break;
    }
    return notifier_from_errno(err);
}

static struct notifier_block __refdata ctrlreg_class_cpu_notifier =
{
    .notifier_call = ctrlreg_class_cpu_callback,
};

static char* ctrlreg_devnode(struct device *dev, umode_t *mode)
{
    unsigned int minor = MINOR(dev->devt), cpu = GET_MINOR_CPU(minor), reg = GET_MINOR_REG(minor);

    if (reg < XCR_MINOR_BASE)
        return kasprintf(GFP_KERNEL, "crs/cpu%u/cr%u", cpu, reg);
    else
        return kasprintf(GFP_KERNEL, "crs/cpu%u/xcr%u", cpu, reg-XCR_MINOR_BASE);
}


int __init ctrlreg_init(void)
{
    int err = 0, i = 0;

    printk(KERN_INFO "ctrlreg: init\n");

    if ((major_n = __register_chrdev(0, 0, NR_CPUS, "crs", &ctrlreg_fops)) < 0)
        return major_n;

    printk(KERN_INFO "ctrlreg: major number is %u\n", major_n);



    ctrlreg_class = class_create(THIS_MODULE, "ctrlreg\n");
    if (IS_ERR(ctrlreg_class)) 
    {
        err = PTR_ERR(ctrlreg_class);
        goto out_chrdev;
    }

    printk(KERN_INFO "ctrlreg: class created\n");

    ctrlreg_class->devnode = ctrlreg_devnode;

    cpu_notifier_register_begin();
    for_each_online_cpu(i) 
    {
        err = ctrlreg_device_create(i);
        if (IS_ERR_VALUE(err))
            goto out_class;
    }

    __register_hotcpu_notifier(&ctrlreg_class_cpu_notifier);
    cpu_notifier_register_done();

    printk(KERN_INFO "ctrlreg: init success\n");

    err = 0;
    goto out;

out_class:
    i = 0;
    for_each_online_cpu(i) 
    {
        ctrlreg_device_destroy(i);
    }
    cpu_notifier_register_done();
    class_destroy(ctrlreg_class);

out_chrdev:
    __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "ctrlreg");
out:
    return err;
}


static void __exit ctrlreg_exit(void)
{
    int cpu = 0;

    cpu_notifier_register_begin();
    for_each_online_cpu(cpu)
        ctrlreg_device_destroy(cpu);
    class_destroy(ctrlreg_class);
    __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "ctrlreg");
    __unregister_hotcpu_notifier(&ctrlreg_class_cpu_notifier);
    cpu_notifier_register_done();
}

module_init(ctrlreg_init);
module_exit(ctrlreg_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Kee Nemesis 241");
MODULE_DESCRIPTION("Read and write Control Registers");

This module create the following dev nodes:

/dev/crs/cpu0/cr0
/dev/crs/cpu0/cr2
/dev/crs/cpu0/cr3
/dev/crs/cpu0/cr4
/dev/crs/cpu0/cr8
/dev/crs/cpu0/xcr0

/dev/crs/cpu1/cr0
/dev/crs/cpu1/cr2
/dev/crs/cpu1/cr3
/dev/crs/cpu1/cr4
/dev/crs/cpu1/cr8
/dev/crs/cpu1/xcr0

...

You can read/write these dev nodes. The minimum read/write length is 4 bytes on 32 bit system and 8 bytes on 64 bit ones (Linux do some buffering anyway).

To compile this LKM, save the code above as ctrlreg.c and create this Makefile

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

then use make to get ctrlreg.ko.

To load the module use sudo insmod ctrlreg.ko, to remove it sudo rmmod ctrlreg.

I have also written a small user mode utility to read CR:
CODE IS WITHOUT ANY WARRANTY, TESTED ON DEBIAN 8 ON 64 bit VM ONLY

#include <stdio.h>
#include <stdlib.h>

#define MAX_PATH 256

int main(int argc, char* argv[])
{
    unsigned long cpu, reg;
    FILE* fin;
    char device[MAX_PATH];
    unsigned long data;

    if (argc < 3 || argc > 4)
    return fprintf(stderr, "Usage:\n\t\t cr cpu reg [value]\n"), 1;

    if (sscanf(argv[1], "cpu%u", &cpu) != 1)
    return fprintf(stderr, "Invalid value '%s' for cpu\n", argv[1]), 2;

    if (sscanf(argv[2], "cr%u", &reg) != 1 && sscanf(argv[2], "xcr%u", &reg) != 1)
    return fprintf(stderr, "Invalid value '%s' for reg\n", argv[2]), 3;

    if (argc == 4 && sscanf(argv[3], "%lu", &data) != 1)
    return fprintf(stderr, "Invalid numeric value '%s'\n", argv[3]), 6;

    snprintf(device, MAX_PATH, "/dev/crs/cpu%u/%s", cpu, argv[2]);

    fin = fopen(device, argc == 4 ? "wb" : "rb");

    if (!fin)
      return fprintf(stderr, "Cannot open device %s\n", device), 4;

    if (argc == 4)
    {
       if (fwrite(&data, sizeof(data), 1, fin) != 1)
    return fprintf(stderr, "Cannot write device %s (%d)\n", device, ferror(fin)), 5;     
    }
    else
    {
      if (fread(&data, sizeof(data), 1, fin) != 1)
    return fprintf(stderr, "Cannot read device %s (%d)\n", device, ferror(fin)), 7;

      printf("%016x\n", data);
    }



    fclose(fin);
    return 0;

}

Save the code as cr.c and compile it.

To read cr0 of the second CPU you can use:
cr cpu1 cr0

To write the value 0 (be careful) into it
cr cpu1 cr0 0

这篇关于如何从Linux用户和内核模式知道CR寄存器的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆