01
ARM Cortex-A9 Registers
For the ARM Cortex-A9 processor, its registers mainly consist of two parts: general-purpose registers and system control registers.

The general-purpose registers shown in the figure above are primarily used during code execution, allowing the CPU to execute code and perform related arithmetic operations. During debugging, the registers of particular interest are SP, LR, PC, and R0~R3. Other registers also have applications in specific scenarios; for example, U-Boot uses the R9 register to temporarily store the address of gd_t during code redirection. When code crashes during debugging, the first step is to check the value of PC to locate the specific instruction by tracing the disassembly file.
As for the system control registers, they implement some control and status indication functions of the CPU. For instance, the ACTLR register is used for multi-core configuration, while the CPSR register indicates the logical operation status of the CPU. The CP15 register consists of 16 sets of registers, which can be accessed using the MRC/MCR instructions combined with different opcodes.

The MRC and MCR instructions are shown in the figure below:


The process of CPU instruction fetching and computation is shown in the figure below. In addition to operating on memory, the internal register set will also be updated synchronously.

02
Debugging Tools for Operating CPU Registers
During the chip development and verification process, it is inevitable to encounter abnormal internal logic implementations of the CPU that prevent code from executing normally. At this point, it is necessary to locate the cause of the fault, which can generally be done using tools like JLINK or DSTREAM to access and manipulate the CPU registers. In scenarios where the program has already encountered an error but JTAG can still connect, the JLINK tool can be used to obtain information about the fault site. After connecting with JLINK, entering the Regs command can dump the values of the CPU registers:

When wishing to rewrite the CPU general-purpose registers, this can be achieved using the wreg command:

Similarly, the CP15 registers can also be manipulated:

03
Inline Assembly in C Code
When the cause of the fault cannot be determined from the fault site, it is necessary to track the changes in the registers during the CPU execution process. For example, during the process of a user-space program in the kernel requesting a page table, we can obtain the page table address through the CP15 ttbr0 register.
1#define _ARM_MRC(coproc, opcode1, Rt, CRn, CRm, opcode2)
2 asm volatile ("mrc p" #coproc ", " #opcode1 ", %[output], c" #CRn ", c" #CRm ", " #opcode2 "\n" : [output] "=r" (Rt))
3#define _ARM_MCR(coproc, opcode1, Rt, CRn, CRm, opcode2)
4 asm volatile ("mcr p" #coproc ", " #opcode1 ", %[input], c" #CRn ", c" #CRm ", " #opcode2 "\n" :: [input] "r" (Rt))
5
6void func1(void)
7{
8 u32 ttbr0,ttbr1;
9 _ARM_MRC(15, 0, ttbr0, 2, 0, 0);
10 _ARM_MRC(15, 0, ttbr1, 2, 0, 1);
11 printk("ttbr0 0x%08x ttbr1 0x%08x\n", ttbr0, ttbr1);
12}
For operations on other registers, the code in U-Boot located at \arch\arm\include\asm\system.h can be referenced. By porting this part of the code, we can achieve our requirements:
1static inline unsigned int get_cr(void)
2{
3 unsigned int val;
4
5 if (is_hyp())
6 asm volatile("mrc p15, 4, %0, c1, c0, 0 @ get CR" : "=r" (val)
7 :
8 : "cc");
9 else
10 asm volatile("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val)
11 :
12 : "cc");
13 return val;
14}
15
16static inline void set_cr(unsigned int val)
17{
18 if (is_hyp())
19 asm volatile("mcr p15, 4, %0, c1, c0, 0 @ set CR" :
20 : "r" (val)
21 : "cc");
22 else
23 asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" :
24 : "r" (val)
25 : "cc");
26 isb();
27}
04
devmem
After the kernel has mounted the file system, there are many methods to operate peripheral registers, but the preferred method is to use the devmem command. This command can directly manipulate physical memory space without considering the mapping between virtual and physical addresses. This command is provided by busybox, and by configuring busybox, it can be included in the generated file system.

The process of operating registers using devmem is as follows:

END
Recommended Reading:
Collection | Summary of Linux ArticlesCollection | Programming LifeCollection | C LanguageMy Knowledge Circle