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
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
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.