Linux kernel 如何使用C程序将ARM处理器置于不同的模式?

Linux kernel 如何使用C程序将ARM处理器置于不同的模式?,linux-kernel,arm,device-driver,Linux Kernel,Arm,Device Driver,我正在经历不同的ARM处理器模式。我想在处理器处于不同模式时检查处理器statex:寄存器值 所以,有人能帮我找出将处理器置于不同模式的示例代码吗 例如,我发现了一个未定义模式的代码: asm volatile.short 0xffff\n 这需要内联汇编,因为C没有内置的方法来实现您想要的。这并不是最有效的方法,我的GCC仍然生成一条无用的movr3,0指令 您最好在裸汇编中实现该函数 #define MODE_USR 0x10 #define MODE_FIQ 0

我正在经历不同的ARM处理器模式。我想在处理器处于不同模式时检查处理器statex:寄存器值

所以,有人能帮我找出将处理器置于不同模式的示例代码吗

例如,我发现了一个未定义模式的代码:
asm volatile.short 0xffff\n

这需要内联汇编,因为C没有内置的方法来实现您想要的。这并不是最有效的方法,我的GCC仍然生成一条无用的movr3,0指令

您最好在裸汇编中实现该函数

#define MODE_USR        0x10
#define MODE_FIQ        0x11
#define MODE_IRQ        0x12
#define MODE_SVC        0x13
#define MODE_ABT        0x17
#define MODE_UND        0x1B
#define MODE_SYS        0x1F

#define MODE_MASK       0x1F

void switch_mode(int mode) {
        register unsigned long tmp = 0;

        mode &= ~MODE_MASK;

        asm volatile(
                "mrs %[tmp], cpsr_all \n"
                "bic %[tmp], %[tmp], %[mask] \n"
                "orr %[tmp], %[tmp], %[mode] \n"
                "msr cpsr_all, %[tmp] \n"
                : : [mode] "r" (mode), [mask] "I" (MODE_MASK), [tmp] "r" (tmp)
        );
}
该函数仅设置程序状态寄存器中的模式位。您可以在ARM参考手册中找到适合您具体架构的常数

以下是我的编译器生成的代码:

00000000 <switch_mode>:
   0:   e3c0001f    bic r0, r0, #31   ; sane-ify the input
   4:   e3a03000    mov r3, #0        ; useless instruction generated by gcc
   8:   e10f3000    mrs r3, CPSR      ; saves the current psr
   c:   e3c3301f    bic r3, r3, #31   ; clear mode bits
  10:   e1833000    orr r3, r3, r0    ; set mode bits to the inputted bits
  14:   e129f003    msr CPSR_fc, r3   ; set current psr to the modified one
  18:   e12fff1e    bx  lr

因为C没有内置的方式来实现您想要的,所以您需要内联汇编。这并不是最有效的方法,我的GCC仍然生成一条无用的movr3,0指令

您最好在裸汇编中实现该函数

#define MODE_USR        0x10
#define MODE_FIQ        0x11
#define MODE_IRQ        0x12
#define MODE_SVC        0x13
#define MODE_ABT        0x17
#define MODE_UND        0x1B
#define MODE_SYS        0x1F

#define MODE_MASK       0x1F

void switch_mode(int mode) {
        register unsigned long tmp = 0;

        mode &= ~MODE_MASK;

        asm volatile(
                "mrs %[tmp], cpsr_all \n"
                "bic %[tmp], %[tmp], %[mask] \n"
                "orr %[tmp], %[tmp], %[mode] \n"
                "msr cpsr_all, %[tmp] \n"
                : : [mode] "r" (mode), [mask] "I" (MODE_MASK), [tmp] "r" (tmp)
        );
}
该函数仅设置程序状态寄存器中的模式位。您可以在ARM参考手册中找到适合您具体架构的常数

以下是我的编译器生成的代码:

00000000 <switch_mode>:
   0:   e3c0001f    bic r0, r0, #31   ; sane-ify the input
   4:   e3a03000    mov r3, #0        ; useless instruction generated by gcc
   8:   e10f3000    mrs r3, CPSR      ; saves the current psr
   c:   e3c3301f    bic r3, r3, #31   ; clear mode bits
  10:   e1833000    orr r3, r3, r0    ; set mode bits to the inputted bits
  14:   e129f003    msr CPSR_fc, r3   ; set current psr to the modified one
  18:   e12fff1e    bx  lr

如果您希望从用户空间测试模式,这是一个困难的问题。如果系统中没有FIQ外围设备,可能无法进入FIQ模式。您的系统可能根本没有使用监视器模式等。要进入中止模式,您可以使用无效指针,或使用mmap。然而,如果没有内核的帮助,从用户空间应答所有模式切换将像书本一样或者不可能。使用/proc或/sys文件创建测试模块并使用以下技术实现内核代码将是最直接的方法

您应该知道,并非所有模式开关转换都是允许的。例如,除非通过异常机制,否则您可能永远不会从用户模式切换到任何其他模式。另一个问题是,每个ARM模式都有存储寄存器。其中之一是非常重要的sp或堆栈指针以及lr或链路寄存器

通常,使用内嵌汇编程序的宏绑定测试片段比使用函数调用更安全。测试代码不得调用外部例程。这可能很困难,因为使用浮点等可能会导致编译器插入隐藏的子程序调用。您应该检查生成的汇编程序,并为其他人提供注释

 /* Get the current mode for restoration. */
 static inline unsigned int get_cpsr(void)
 {
     unsigned int rval;
     asm (" mrs %0, cpsr\n" : "=r" (rval));
     return rval;
 }
您可以将其放入头文件中。编译器将内联代码,因此,您只需将msr指令放入例程中即可

要更改模式,请使用类似定义的

 /* Change the mode */
 #define change_mode(mode) asm("cps %0" : : "I"(mode))
Tangr的模式定义正确

#define MODE_USR        0x10   /* Never use this one, as there is no way back! */
#define MODE_FIQ        0x11   /* banked r8-r14 */
#define MODE_IRQ        0x12
#define MODE_SVC        0x13
#define MODE_MON        0x16
#define MODE_ABT        0x17
#define MODE_UND        0x1B
#define MODE_SYS        0x1F   /* Same as user... */
您还需要恢复以前的模式

#define restore_mode(mode) \
     mode &= 0x1f; \
     asm(" msr cpsr, %0\n" : : "r"(mode) : "cc")
把这些放在一起,如下所示:

  void test_abort(void)
  {
     unsigned int old_mode = get_cpsr() & 0x1f;
     change_mode(MODE_ABT);
     /* Your test code here... must not call functions. */
     restore_mode(old_mode);
  }

这直接回答了你的问题。然而,由于所有这些困难,编写汇编程序来实现测试通常比较容易。我相信您试图利用现有的Linux代码来测试所有模式。这不是ARM Linux的设计目标,如果不修改源代码,将很难实现,并且系统特定性很强。

如果您希望从用户空间测试模式,这是一个困难的问题。如果系统中没有FIQ外围设备,可能无法进入FIQ模式。您的系统可能根本没有使用监视器模式等。要进入中止模式,您可以使用无效指针,或使用mmap。然而,如果没有内核的帮助,从用户空间应答所有模式切换将像书本一样或者不可能。使用/proc或/sys文件创建测试模块并使用以下技术实现内核代码将是最直接的方法

您应该知道,并非所有模式开关转换都是允许的。例如,除非通过异常机制,否则您可能永远不会从用户模式切换到任何其他模式。另一个问题是,每个ARM模式都有存储寄存器。其中之一是非常重要的sp或堆栈指针以及lr或链路寄存器

通常,使用内嵌汇编程序的宏绑定测试片段比使用函数调用更安全。测试代码不得调用外部例程。这可能很困难,因为使用浮点等可能会导致编译器插入隐藏的子程序调用。您应该检查生成的汇编程序,并为其他人提供注释

 /* Get the current mode for restoration. */
 static inline unsigned int get_cpsr(void)
 {
     unsigned int rval;
     asm (" mrs %0, cpsr\n" : "=r" (rval));
     return rval;
 }
您可以将其放入头文件中。编译器将内联代码,因此,您只需将msr指令放入例程中即可

要更改模式,请使用类似定义的

 /* Change the mode */
 #define change_mode(mode) asm("cps %0" : : "I"(mode))
Tangr的模式定义正确

#define MODE_USR        0x10   /* Never use this one, as there is no way back! */
#define MODE_FIQ        0x11   /* banked r8-r14 */
#define MODE_IRQ        0x12
#define MODE_SVC        0x13
#define MODE_MON        0x16
#define MODE_ABT        0x17
#define MODE_UND        0x1B
#define MODE_SYS        0x1F   /* Same as user... */
您还需要恢复以前的模式

#define restore_mode(mode) \
     mode &= 0x1f; \
     asm(" msr cpsr, %0\n" : : "r"(mode) : "cc")
把这些放在一起,如下所示:

  void test_abort(void)
  {
     unsigned int old_mode = get_cpsr() & 0x1f;
     change_mode(MODE_ABT);
     /* Your test code here... must not call functions. */
     restore_mode(old_mode);
  }
这直接回答了你的问题。然而,由于所有的困难,它

通常更容易编写汇编程序来实现测试。我相信您试图利用现有的Linux代码来测试所有模式。这不是ARM Linux的设计目标,如果不修改源代码,将很难实现,并且系统特定性很强。

您开始使用哪种模式?你说的是哪个手臂核心?这是在操作系统上吗?我必须尝试我已经介绍过的所有7种未定义的模式。我正在使用Android,ARMv7系列。使用SVC切换到管理模式,然后您可以切换到其他模式。//那么你是在没有操作系统的情况下做这一切的?@auselen你能告诉我没有操作系统的确切含义吗?如果你能为切换模式提供一些示例代码,那就太好了。比如说,如果你是在Linux下这样做的话?只需搜索svc指令并阅读cortex,这是一本ARM的编程手册免费书,您从哪种模式开始?你说的是哪个手臂核心?这是在操作系统上吗?我必须尝试我已经介绍过的所有7种未定义的模式。我正在使用Android,ARMv7系列。使用SVC切换到管理模式,然后您可以切换到其他模式。//那么你是在没有操作系统的情况下做这一切的?@auselen你能告诉我没有操作系统的确切含义吗?如果你能为切换模式提供一些示例代码,那就太好了。比如说,如果你是在Linux下这样做的话?只需搜索svc指令并阅读cortex——armtmp=0中的编程手册免费书籍,即可导致mov r3,0;gcc不会解释您的汇编器以知道不需要r tmp输入。使其成为早期的clobber输出,在这种情况下不要初始化它。除了这个答案之外,还有一个更改模式的代码片段和一个早期的clobber示例。cpsXX指令可用于ArmV7 cpu。返回前恢复更安全;他应该知道堆栈等会发生变化。例如,bx lr无法正常工作。有一些优点。我的目标是得到最简单、最简洁的答案。OP确实提到他使用的是armv7系统。tmp=0导致mov r3,0;gcc不会解释您的汇编器以知道不需要r tmp输入。使其成为早期的clobber输出,在这种情况下不要初始化它。除了这个答案之外,还有一个更改模式的代码片段和一个早期的clobber示例。cpsXX指令可用于ArmV7 cpu。返回前恢复更安全;他应该知道堆栈等会发生变化。例如,bx lr无法正常工作。有一些优点。我的目标是得到最简单、最简洁的答案。OP确实提到了他使用的是armv7系统。如果内存没问题的话,有一些特殊形式的加载和存储指令可以从更特权的模式直接与用户空间寄存器一起工作。@artlessnoise首先感谢您的回复。。我尝试了你的方法,但不管是什么模式,我在转储中得到的都是相同的东西,“[69.118189]中断处理程序中的坏模式检测到[69.122969]内部错误:Oops-坏模式:0[1]PREEMPT'@Rahul…你没有在Linux内核中运行此代码,是吗?内核是非常复杂地连接在一起的。在处理内核代码时,通常不需要像更改CPU模式这样的低级操作。编辑:你是。为什么?@tangrs正如我前面提到的,我不想使用汇编,我有内核代码,我把这个代码片段保存在一个sysfs中。我只想检查处理器模式下不同的寄存器值。我知道这可能一点用都没有,但我只是为了学习而做的。@Rahul这比你想象的要困难得多。使用内核代码与编写裸机代码不同。您必须担心内核如何与您的体系结构以及体系结构细节一起工作。通常,在内核不知道的情况下,不应该更改体系结构的配置。在这里,您正在更改CPU模式,而Linux没有预料到这一点。内核在这里死机是正确的,因为内核中的某些代码是在假定它只在某些CPU状态下执行的情况下编写的。如果内存没问题的话,有一些特殊形式的加载和存储指令可以从更特权的模式直接与用户空间寄存器一起工作。@artlessnoise首先感谢您的回复。。我尝试了你的方法,但不管是什么模式,我在转储中得到的都是相同的东西,“[69.118189]中断处理程序中的坏模式检测到[69.122969]内部错误:Oops-坏模式:0[1]PREEMPT'@Rahul…你没有在Linux内核中运行此代码,是吗?内核是非常复杂地连接在一起的。在处理内核代码时,通常不需要像更改CPU模式这样的低级操作。编辑:你是。为什么?@tangrs正如我前面提到的,我不想使用汇编,我有内核代码,我把这个代码片段保存在一个sysfs中。我只是想看看这是什么
在处理器模式下,不同的寄存器值。我知道这可能一点用都没有,但我只是为了学习而做的。@Rahul这比你想象的要困难得多。使用内核代码与编写裸机代码不同。您必须担心内核如何与您的体系结构以及体系结构细节一起工作。通常,在内核不知道的情况下,不应该更改体系结构的配置。在这里,您正在更改CPU模式,而Linux没有预料到这一点。内核在这里死机是正确的,因为内核中的某些代码是在假定它只在某些CPU状态下执行的情况下编写的。