How Can I put ARM processor in different modes using C program?

后端 未结 2 1040
误落风尘
误落风尘 2021-01-03 15:23

I am going through different mode of ARM processor. I want to check the processor state(ex: register values) while it is in different mode.

So can someone help me to

相关标签:
2条回答
  • 2021-01-03 15:46

    You'll need inline assembly for this since C has no built in way to achieve what you want. This isn't exactly the most efficient way to do this (my GCC still generates a useless mov r3, #0 instruction).

    You're probably better off implementing the function in bare assembly.

    #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)
            );
    }
    

    The function just sets the mode bits in the program status register. You can find the constants in the ARM reference manual for your exact architecture.

    Here is the code generated by my compiler:

    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
    
    0 讨论(0)
  • 2021-01-03 15:49

    If you wish to test the modes from user space, this is a difficult question. There maybe no way to go to FIQ mode, if there is no FIQ peripheral in the system. Your system may not be using Monitor mode at all, etc. To get to abort mode, you can use an invalid pointer, or use mmap. However, to answer all mode switches from user space would be book like (or impossible) without assistance from the kernel. Creating a test module with a /proc or /sys file and using the techniques below to implement the kernel code would be the most straight forward method.

    You should be aware that not all mode switch transitions are allowed. For instance, you may never switch from user mode to any other mode except through the exception mechanisms. Another issue is that each ARM mode has banked registers. One of these is the very important sp (or stack pointer) and lr (or link register) that is fundamental to 'C' code.

    It is generally more safe to use a macro of in-line assembler to bound your test snippets, than to use function calls. The test code must not call external routines. This can be difficult as using floating point, etc may result in the compiler inserting hidden sub-routine calls. You should inspect the generated assembler and provide comments for others.

     /* 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;
     }
    

    You can put this in a header file. The compiler will inline the code so, you will just get the msr instruction placed within a routine.

    To change the mode use a define like,

     /* Change the mode */
     #define change_mode(mode) asm("cps %0" : : "I"(mode))
    

    Tangr's has the correct mode defines,

    #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... */
    

    You also need to restore the previous mode,

    #define restore_mode(mode) \
         mode &= 0x1f; \
         asm(" msr cpsr, %0\n" : : "r"(mode) : "cc")
    

    Put this together as follows,

      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);
      }
    

    This answers your question directly. However, due to all of the difficulties, it is often easier to write assembler to achieve a test. I believe you were trying to leverage the existing Linux code to test all the modes. This is not an ARM-Linux design goal and without modifying the source, it will be very difficult to achieve and highly system specific if it is.

    0 讨论(0)
提交回复
热议问题