RDPMC在用户模式下不与PCE集甚至工作 [英] rdpmc in user mode does not work even with PCE set

查看:275
本文介绍了RDPMC在用户模式下不与PCE集甚至工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于对维基百科条目

以及英特尔手册, RDPMC 应提供给用户模式进程,只要第8位 CR4 设置。不过,我仍然运行到一般保护试图从用户空间,即使是位集运行 RDPMC 时出错。

我在一个8核英特尔X3470 内核 2.6.32-279.el6.x86_64 运行

下面是用户模式程序我试图执行:

 的#define _GNU_SOURCE#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&inttypes.h GT;#包括LT&;&sched.h中GT;
#包括LT&;&ASSERT.H GT;uint64_t中
read_pmc(INT ECX)
{
    unsigned int类型A,D;
    __asm​​ __volatile(RDPMC:=一个(一),= D(四):C(ECX));
    回报((uint64_t中)一)| (((uint64_t中)D)<< 32);
}INT主(INT交流,焦炭** AV)
{
    uint64_t中开始,结束;
    cpu_set_t cpuset;
    无符号整型℃;
    INT I;    如果(交流!= 3){
        fprintf中(标准错误,用法:%的CPU-ID PMC-NUM \\ n,AV [0]);
        出口(EXIT_FAILURE);
    }    I =与atoi(AV [1]);
    C =与atoi(AV [2]);    CPU_ZERO(安培; cpuset);
        CPU_SET(I,&放大器; cpuset);
        断言(了sched_setaffinity(0,sizeof的(cpuset),放大器; cpuset)== 0);    的printf(%鲁\\ n,read_pmc(C));
    返回0;
}

下面是设置位和读出的内核模块 CR4 这样我就可以手动验证该位已设置。

  / *
 *在用户模式下启用PMC。
 * /
#包括LT&; Linux的/ - module.h中GT;
#包括LT&;的Linux / kernel.h>INT的init_module(无效)
{
    长期的typedef无符号整数uint64_t中;
    uint64_t中输出;
    //设置CR4,第8位,使PMC
__asm​​ __(推%RAX \\ n \\ t的
                MOV%CR4,RAX%; \\ n \\ t的
                或$(1<< 7),RAX%; \\ n \\ t的
                MOV%RAX,CR4%; \\ n \\ t的
                WBINVD \\ n \\ t的
                啪%RAX
    );    //读回CR4来检查一下。
    __asm​​ __(\\ t MOV %% CR4,%0:= R(输出));
    printk的(KERN_INFO%录,输出);    返回0;
}
虚空在cleanup_module(无效)
{
__asm​​ __(推%RAX \\ n \\ t的
        推%RBX \\ n \\ t的
                MOV%CR4,RAX%; \\ n \\ t的
                MOV $(1 LT;< 7),%RBX \\ n \\ t的
                不能%RBX \\ n \\ t的
                和%RBX,RAX%; \\ n \\ t的
                MOV%RAX,CR4%; \\ n \\ t的
                WBINVD \\ n \\ t的
                啪%RBX \\ n \\ t的
                啪%RAX \\ n \\ t的
    );
}


解决方案

显然,当英特尔表示第8位,他们指的是在第9位从右边,因为它们的索引开始在 0 。更换 $(1 LT;< 7) $(1 LT;< 8)全球解决问题,并允许 RDPMC 来从用户模式调用。

下面是更新内核模块,还采用 on_each_cpu ,以确保它被设置在每一个核心部分。

  / *
 *在内核模式下阅读PMC。
 * /
#包括LT&; Linux的/ - module.h中GT; / *所有模块所需* /
#包括LT&;的Linux / kernel.h> / *集所需的KERN_INFO * /静态无效printc4(无效){
    长期的typedef无符号整数uint64_t中;
    uint64_t中输出;
    //读回CR4来检查一下。
    __asm​​ __(\\ t MOV %% CR4,%0:= R(输出));
    printk的(KERN_INFO%录,输出);
}静态无效setc4b8(void *的信息){
    //设置CR4,第8位(从右侧的第9位),以使
__asm​​ __(推%RAX \\ n \\ t的
                MOV%CR4,RAX%; \\ n \\ t的
                或$(1<< 8),RAX%; \\ n \\ t的
                MOV%RAX,CR4%; \\ n \\ t的
                WBINVD \\ n \\ t的
                啪%RAX
    );    //检查我们在哪个CPU:
    printk的(KERN_INFO的处理器%d个然,smp_processor_id());
    printc4();
}静态无效clearc4b8(void *的信息){
    printc4();
__asm​​ __(推%RAX \\ n \\ t的
        推%RBX \\ n \\ t的
                MOV%CR4,RAX%; \\ n \\ t的
                MOV $(1 LT;< 8),%RBX \\ n \\ t的
                不能%RBX \\ n \\ t的
                和%RBX,RAX%; \\ n \\ t的
                MOV%RAX,CR4%; \\ n \\ t的
                WBINVD \\ n \\ t的
                啪%RBX \\ n \\ t的
                啪%RAX \\ n \\ t的
    );
    printk的(KERN_INFO的处理器%d个然,smp_processor_id());
}INT的init_module(无效)
{
    on_each_cpu(setc4b8,NULL,0);
    返回0;
}
虚空在cleanup_module(无效)
{
    on_each_cpu(clearc4b8,NULL,0);
}

Based on the Wikipedia entry as well as the Intel manual, rdpmc should be available to user-mode processes as long as bit 8 of CR4 is set. However, I am still running into general protection error when trying to run rdpmc from userspace even with that bit set.

I am running on an 8-core Intel X3470 on kernel 2.6.32-279.el6.x86_64.

Here is the user-mode program I am trying to execute:

#define _GNU_SOURCE

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

#include <sched.h>
#include <assert.h>

uint64_t
read_pmc(int ecx)
{
    unsigned int a, d;
    __asm __volatile("rdpmc" : "=a"(a), "=d"(d) : "c"(ecx));
    return ((uint64_t)a) | (((uint64_t)d) << 32);
}

int main(int ac, char **av)
{
    uint64_t start, end;
    cpu_set_t cpuset;
    unsigned int c;
    int i;

    if (ac != 3) {
        fprintf(stderr, "usage: %s cpu-id pmc-num\n", av[0]);
        exit(EXIT_FAILURE);
    }

    i = atoi(av[1]);
    c = atoi(av[2]);

    CPU_ZERO(&cpuset);
        CPU_SET(i, &cpuset);
        assert(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);

    printf("%lu\n", read_pmc(c));
    return 0;
}

Here is the kernel module which sets the bit and reads out CR4 so I can manually verify that the bit has been set.

/*  
 *  Enable PMC in user mode.
 */
#include <linux/module.h>
#include <linux/kernel.h>



int init_module(void)
{
    typedef long unsigned int uint64_t;
    uint64_t output;
    // Set CR4, Bit 8  to enable PMC
__asm__("push   %rax\n\t"
                "mov    %cr4,%rax;\n\t"
                "or     $(1 << 7),%rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
    );

    // Read back CR4 to check the bit.
    __asm__("\t mov %%cr4,%0" : "=r"(output));
    printk(KERN_INFO "%lu", output);

    return 0;
}
void cleanup_module(void)
{
__asm__("push   %rax\n\t"
        "push   %rbx\n\t"
                "mov    %cr4,%rax;\n\t"
                "mov  $(1 << 7), %rbx\n\t"
                "not  %rbx\n\t"
                "and   %rbx, %rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rbx\n\t"
                "pop    %rax\n\t"
    ); 
}

解决方案

Apparently, when Intel says Bit 8, they are referring to the 9th bit from the right, since their indexing begins at 0. Replacing $(1 << 7) with $(1 << 8) globally resolves the issue, and allows rdpmc to be called from user mode.

Here is the updated kernel module, also using on_each_cpu to make sure that it is set on every core.

/*  
 *  Read PMC in kernel mode.
 */
#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */

static void printc4(void) {
    typedef long unsigned int uint64_t;
    uint64_t output;
    // Read back CR4 to check the bit.
    __asm__("\t mov %%cr4,%0" : "=r"(output));
    printk(KERN_INFO "%lu", output);
}

static void setc4b8(void * info) {
    // Set CR4, Bit 8 (9th bit from the right)  to enable
__asm__("push   %rax\n\t"
                "mov    %cr4,%rax;\n\t"
                "or     $(1 << 8),%rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
    );

    // Check which CPU we are on:
    printk(KERN_INFO "Ran on Processor %d", smp_processor_id());
    printc4();
}

static void clearc4b8(void * info) {
    printc4();
__asm__("push   %rax\n\t"
        "push   %rbx\n\t"
                "mov    %cr4,%rax;\n\t"
                "mov  $(1 << 8), %rbx\n\t"
                "not  %rbx\n\t"
                "and   %rbx, %rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rbx\n\t"
                "pop    %rax\n\t"
    );
    printk(KERN_INFO "Ran on Processor %d", smp_processor_id());
}



int init_module(void)
{
    on_each_cpu(setc4b8, NULL, 0);
    return 0;
}
void cleanup_module(void)
{
    on_each_cpu(clearc4b8, NULL, 0);
}

这篇关于RDPMC在用户模式下不与PCE集甚至工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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