如何在Linux上使用GCC汇编语言设置x86-64的控制寄存器0(CR0)位?

9
我正在使用以下代码来设置cr0位以禁用缓存。当我编译时,请注意。
#include <stdio.h>

int main()
{
        __asm__("pushl  %eax\n\t"
                "mov    %cr0,%eax;\n\t"
                "orl    $(1 << 30),%eax;\n\t"
                "mov    %eax,%cr0;\n\t"
                "wbinvd\n\t"
                "popl   %eax"
);

        return 0;
}

我遇到了一个错误,提示mov操作数无效。

请问有没有人能够指引我找一份好的gcc x86-64指南来完成这些操作?另外,上面的代码到底是哪里出了问题?


如果您在嵌入式汇编之外组装它(制作一个汇编文件并使用gasnasm进行组装),它是否有效? - Jonathan
有用的 SO 问题: https://dev59.com/zkjSa4cB1Zd3GeqPEl25 - Zan Lynx
你为什么想要这样做?你想要实现什么目标? - Hasturkun
我正在测试CPU缓存带来的加速效果。 - pranith
https://dev59.com/ums05IYBdhLWcg3wJurp - Ciro Santilli OurBigBook.com
5个回答

10

好的,最终我编写了以下内核模块。不确定是否正确,因为当禁用缓存时应该会出现明显的减速,但它可以编译和插入。

任何指针都将是有帮助的。

谢谢!

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
        printk(KERN_ALERT "Hello, world\n");
        __asm__("push   %rax\n\t"
                "mov    %cr0,%rax;\n\t"
                "or     $(1 << 30),%rax;\n\t"
                "mov    %rax,%cr0;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
);
        return 0;
}
static void hello_exit(void)
{
        printk(KERN_ALERT "Goodbye, cruel world\n");
        __asm__("push   %rax\n\t"
                "mov    %cr0,%rax;\n\t"
                "and     $~(1 << 30),%rax;\n\t"
                "mov    %rax,%cr0;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
);
}
module_init(hello_init);
module_exit(hello_exit);

好的。我刚确认这个有效。你还需要通过以下命令禁用MTRR寄存器: $ echo "disable=00" >| /proc/mtrr - pranith

4

我认为你没有看到“急剧减缓”的原因是因为你有多个核心,对吗?

我进行了一些实验,似乎设置%cr0中的CD只影响运行模块的处理器。

确保在您想要禁用缓存的所有核心上运行代码。例如,您可以创建一个/proc/cachedisable文件,其中读取会触发您的代码。然后使用

taskset -c cpu_number cat /proc/cachedisable

禁用CPU编号为cpu_number的缓存。同时使用/proc/cacheenable进行相同操作,这样你就有了所需的一切。对我而言,这很有效,并且无需修改MTRRs,这非常复杂。如果你有多个处理器,那么只需在其中一个上禁用缓存并在该CPU上执行实验,那么系统的其余部分将仍然可用。


4

您无法从用户代码中执行此类操作,即使以root用户身份运行也是如此。

您需要将其制作为驱动程序模块,并使用insmod加载它。


好的,我会尝试将其编写为一个模块!谢谢Zan。 - pranith

1
试试这个: "mov %%cr0, %%eax \n"
一个简单的%会被解释为用户参数(我想)。
你应该阅读this

asm("mov %%cr0,%%rax;"); 这段代码报错:寄存器名称错误 `%%cr0'。 - pranith
在寄存器名称前有两个百分号。这有助于GCC区分操作数和寄存器。操作数只有一个百分号作为前缀。 - pranith
你确定在64位中寄存器的名称仍然是cr0吗?我粘贴的代码来自我编写的迷你内核。 - Thomas
是的,cr0是x86/x86-64处理器上控制寄存器的名称。它是已更改的寄存器。 我需要进入保护模式才能访问cr0吗?我正在以root身份运行此exec。 - pranith
是的,这是一条受保护的指令(幸运的是 :))。 - Thomas
@pranith,@Thomas:确实!没有任何操作系统会允许用户程序(即使是root也是用户程序!)禁用缓存、更改页表或任何其他受保护的指令。 - Zan Lynx

0

这段代码在我的32位x86机器上编译正常,但在x86-64上编译不通过,我的gcc版本是Mac OS X自带的4.2.1版本。

$ gcc -Wall -m32 cr0.c -o cr0
$

没有错误或警告。

$ gcc -Wall -m64 cr0.c -o cr0
/var/folders/.../cce0FYAB.s:9:suffix or operands invalid for `push'
/var/folders/.../cce0FYAB.s:10:suffix or operands invalid for `mov'
/var/folders/.../cce0FYAB.s:12:suffix or operands invalid for `mov'
/var/folders/.../cce0FYAB.s:14:suffix or operands invalid for `pop'
$

所以我猜这里与x86-64上的汇编指令mov %eax,%cr0有更深层次的问题。

看一下x86-64 ISA,似乎你需要像这样的东西:

#include <stdio.h>

int main()
{
        __asm__("pushq  %rax\n\t"
                "movq    %cr0,%rax\n\t"
                "orl    $(1 << 30),%eax\n\t"
                "movq    %rax,%cr0\n\t"
                "wbinvd\n\t"
                "popq   %rax"
);

        return 0;
}

我不知道这是否有效,但至少可以编译/汇编通过:

$ gcc -Wall -m64 cr0.c -o cr0
$ 

是的,问题出在 x86-64 编译上。我使用了 %rax 而不是 %eax。虽然编译通过了,但运行时会出现段错误。 - pranith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接