我正在编写一个内核模块,它是关于读写 MSR 的。我写了一个简单的测试程序,但它仍然失败。它所做的只是写入 MSR,然后再读回。这是代码:
static int __init test3_init(void)
{
uint32_t hi,lo;
hi=0; lo=0xb;
asm volatile("mov %0,%%eax"::"r"(lo));
asm volatile("mov %0,%%edx"::"r"(hi));
asm volatile("mov $0x38d,%ecx");
asm volatile("wrmsr");
printk("exit_write: hi=%08x lo=%08x\n",hi,lo);
asm volatile("mov $0x38d,%ecx");
asm volatile("rdmsr":"=a"(lo),"=d"(hi));
printk("exit_write2: hi=%08x lo=%08x\n",hi,lo);
return 0;
}
输出如下:
exit_write: hi=00000000 lo=0000000b
exit_write2: hi=00000000 lo=00000000
谁能告诉我为什么第二个输出的返回值是0,而不是原来的?我的代码有问题吗?多谢。
最佳答案
问题与这样一个事实有关,即您没有完全告诉 gcc 您在内联汇编中使用了哪些寄存器以及如何使用,并且您还期望 gcc 不会对您的片段之间的寄存器做任何奇怪的事情内联汇编代码。相关的mov
和xxmsr
指令应该在同一个asm block 中。
看看 gcc 对您的代码做了什么(我对它做了一点改动,使其可以作为常规程序编译)...
来源:
// file: msr.c
#include <stdio.h>
typedef unsigned uint32_t;
#define printk printf
#define __init
static int __init test3_init(void)
{
uint32_t hi,lo;
hi=0; lo=0xb;
asm volatile("mov %0,%%eax"::"r"(lo));
asm volatile("mov %0,%%edx"::"r"(hi));
asm volatile("mov $0x38d,%ecx");
asm volatile("wrmsr");
printk("exit_write: hi=%08x lo=%08x\n",hi,lo);
asm volatile("mov $0x38d,%ecx");
asm volatile("rdmsr":"=a"(lo),"=d"(hi));
printk("exit_write2: hi=%08x lo=%08x\n",hi,lo);
return 0;
}
int main(void)
{
return test3_init();
}
编译(使用 MinGW gcc 4.6.2):
gcc msr.c -c -S -o msr.s
从 msr.s 反汇编 test3_init()
:
_test3_init:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %ebx
subl $32, %esp
movl $0, -12(%ebp)
movl $11, -16(%ebp)
movl -16(%ebp), %eax
mov %eax,%eax
movl -12(%ebp), %eax
mov %eax,%edx
mov $0x38d,%ecx
wrmsr
movl -16(%ebp), %eax
movl %eax, 8(%esp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
mov $0x38d,%ecx
rdmsr
movl %edx, %ebx
movl %eax, %esi
movl %esi, -16(%ebp)
movl %ebx, -12(%ebp)
movl -16(%ebp), %eax
movl %eax, 8(%esp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $LC1, (%esp)
call _printf
movl $0, %eax
addl $32, %esp
popl %ebx
popl %esi
popl %ebp
ret
请注意,当 CPU 开始执行 wrmsr
时,它有 ecx
=0x38d (OK), edx
=0 (OK), eax
=0(不是 0xb,糟糕!)。按照说明进行查看。
你可以而且应该写的是类似下面的东西,甚至比原来更短:
static int __init test3_init2(void)
{
uint32_t hi,lo;
hi=0; lo=0xb;
asm volatile("wrmsr"::"c"(0x38d),"a"(lo),"d"(hi));
printk("exit_write: hi=%08x lo=%08x\n",hi,lo);
asm volatile("rdmsr":"=a"(lo),"=d"(hi):"c"(0x38d));
printk("exit_write2: hi=%08x lo=%08x\n",hi,lo);
return 0;
}
现在,test3_init2()
的反汇编:
_test3_init2:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %ebx
subl $48, %esp
movl $0, -12(%ebp)
movl $11, -16(%ebp)
movl $909, %ecx
movl -16(%ebp), %eax
movl -12(%ebp), %edx
wrmsr
movl -16(%ebp), %eax
movl %eax, 8(%esp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
movl $909, -28(%ebp)
movl -28(%ebp), %ecx
rdmsr
movl %edx, %ebx
movl %eax, %esi
movl %esi, -16(%ebp)
movl %ebx, -12(%ebp)
movl -16(%ebp), %eax
movl %eax, 8(%esp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $LC1, (%esp)
call _printf
movl $0, %eax
addl $48, %esp
popl %ebx
popl %esi
popl %ebp
ret
此外,请记住每个 CPU 都有自己的 MSR,您可能希望为所有 CPU 设置此 MSR。另一个重要的考虑因素是,在您处理完 MSR 之前,不应在不同的 CPU 之间移动您在其中操作 MSR 的线程。
关于无法从 MSR 读回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11183654/