How to know the values of CR registers from linux user and kernel modes

drdot

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)

user781847

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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How to know the values of CR registers from linux user and kernel modes

From Dev

how to know who start a Linux kernel module

From Dev

How to know the address range of kernel stack in user process and kernel thread?

From Dev

Does MySQL processes run in user or kernel modes?

From Dev

Does MySQL processes run in user or kernel modes?

From Dev

How to know if a process is user or root in bash, linux

From Dev

How does Linux kernel know that collisions occurred in Ethernet collision domain?

From Dev

How does the linux kernel know device major and minor numbers?

From Dev

From the Linux kernel's point of view, how does a user program communicate with a CUDA GPU?

From Dev

From the Linux kernel's point of view, how does a user program communicate with a CUDA GPU?

From Dev

How to understand the ARM registers dumped by kernel panic?

From Dev

How to understand the ARM registers dumped by kernel panic?

From Dev

How does gnome shell know which config file to use from /usr/share/gnome-shell/modes/?

From Dev

How to know if user arrived from google ads

From Dev

How to know from what module user was called

From Dev

How to get hostname from linux kernel module?

From Dev

Can not get copy_to_user work from the linux kernel

From Dev

Linux Kernel Line Discipline copy_from_user

From Dev

How to limit privileged user access at Linux Kernel level?

From Dev

Kernel OOPs causes when accessing user space pointer from within the Linux Kernel

From Dev

MIPS Assembly: how to know if user inputted values are correctly stored into an array

From Dev

How to know what to enable in the kernel?

From Dev

MIPS, put values from registers into RAM?

From Dev

yielding from linux kernel

From Dev

How does linux kernel switch between user-mode and kernel-mode stack?

From Dev

How Symfony 2 Security Bundle registers to Kernel Events

From Dev

How Symfony 2 Security Bundle registers to Kernel Events

From Dev

How does a linux kernel module know when its file has been opened?

From Dev

How do I know which files will be included in a linux kernel before I build it?

Related Related

  1. 1

    How to know the values of CR registers from linux user and kernel modes

  2. 2

    how to know who start a Linux kernel module

  3. 3

    How to know the address range of kernel stack in user process and kernel thread?

  4. 4

    Does MySQL processes run in user or kernel modes?

  5. 5

    Does MySQL processes run in user or kernel modes?

  6. 6

    How to know if a process is user or root in bash, linux

  7. 7

    How does Linux kernel know that collisions occurred in Ethernet collision domain?

  8. 8

    How does the linux kernel know device major and minor numbers?

  9. 9

    From the Linux kernel's point of view, how does a user program communicate with a CUDA GPU?

  10. 10

    From the Linux kernel's point of view, how does a user program communicate with a CUDA GPU?

  11. 11

    How to understand the ARM registers dumped by kernel panic?

  12. 12

    How to understand the ARM registers dumped by kernel panic?

  13. 13

    How does gnome shell know which config file to use from /usr/share/gnome-shell/modes/?

  14. 14

    How to know if user arrived from google ads

  15. 15

    How to know from what module user was called

  16. 16

    How to get hostname from linux kernel module?

  17. 17

    Can not get copy_to_user work from the linux kernel

  18. 18

    Linux Kernel Line Discipline copy_from_user

  19. 19

    How to limit privileged user access at Linux Kernel level?

  20. 20

    Kernel OOPs causes when accessing user space pointer from within the Linux Kernel

  21. 21

    MIPS Assembly: how to know if user inputted values are correctly stored into an array

  22. 22

    How to know what to enable in the kernel?

  23. 23

    MIPS, put values from registers into RAM?

  24. 24

    yielding from linux kernel

  25. 25

    How does linux kernel switch between user-mode and kernel-mode stack?

  26. 26

    How Symfony 2 Security Bundle registers to Kernel Events

  27. 27

    How Symfony 2 Security Bundle registers to Kernel Events

  28. 28

    How does a linux kernel module know when its file has been opened?

  29. 29

    How do I know which files will be included in a linux kernel before I build it?

HotTag

Archive