无法从 MSR 读回

标签 c gcc assembly x86 inline-assembly

我正在编写一个内核模块,它是关于读写 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 不会对您的片段之间的寄存器做任何奇怪的事情内联汇编代码。相关的movxxmsr指令应该在同一个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/

相关文章:

c++ - Linux 中的静态链接可移植吗?

c - 你写过很长的函数吗?如果是这样,为什么?

c - 这个汇编语句是什么意思?

assembly - x86 指令帮助 : MOV [edx], eax

assembly - X86 : What does `movsxd rdx,edx` instruction mean?

arrays - 为什么我们在 C 中指定 size_t 数组大小而不是仅仅使用整数?这样做的好处是什么?

c++ - 在 C/C++ 中创建新的证书存储

c - 未定义对另一个库函数的引用

gcc - 如何隐藏#pragma消息中的额外输出

c++ - 将高级语言编译成机器码