RDPMC在用户模式下不与PCE集甚至工作 [英] rdpmc in user mode does not work even with PCE set
问题描述
以及英特尔手册,
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屋!