Kernel中如何操作CPU及外设寄存器

嵌入式Linux

共 5223字,需浏览 11分钟

 ·

2021-08-01 07:01

01

ARM Coretex-A9寄存器

对于ARM Coretex-A9处理器而言其寄存器主要包括两大部分,分别是通用寄存器以及系统控制寄存器。

上图所示的通用寄存器,主要是在代码运行过程中使用到,CPU通过该部分寄存器执行代码并完成相关的运算操作。对于调试过程中比较关心的是SP、LR、PC以及R0~R3几个寄存器。而其他寄存器在特定场景下也有应用,例如U-Boot在代码重定向的过程中使用到了R9寄存器来暂存gd_t的地址。调试过程中,当代码崩溃的时候我们第一步是看PC是多少,以此通过追踪反汇编文件来定位到具体的指令处。

对于系统控制寄存器而言,其实现了CPU的一些控制以及状态指示等功能,例如通过ACTLR寄存器完成多核的配置,通过CPSR寄存器实现CPU的逻辑运算状态指示等。CP15寄存器一共是16组寄存器,通过MRC/MCR指令并结合不同的指令码实现寄存器的访问。

MRC和MCR指令如下图所示:

CPU的取指、运算过程如下图所示,除了要操作内存之外,内部的寄存器组也会同步进行更新。



02

调试工具操作CPU寄存器

在芯片开发验证的过程中,不可避免的会遇到CPU内部逻辑实现异常而导致代码无法正常得到执行,此时需要定位故障的原因,一般可以通过JLINK、DSTREAM工具获取、操作CPU的寄存器。对于程序已经执行出错的场景而JTAG仍然可以连接上的场景,使用JLINK工具可以获取到故障现场信息。JLINK连接上之后输入Regs命令,可以dump出CPU的寄存器值:

当希望改写CPU通用寄存器的时候,可以通过wreg来实现:

同样,CP15寄存器也可以操作:



03

C代码中内嵌汇编

当通过故障现场不能得知故障原因的时候,需要追踪CPU执行过程中寄存器的变化状态,例如Kerne的用户空间程序申请页表的过程中,我们可以通过CP15的ttbr0寄存器获取页表地址。
 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(150, ttbr0, 200);
10        _ARM_MRC(150, ttbr1, 201);
11        printk("ttbr0 0x%08x ttbr1 0x%08x\n", ttbr0, ttbr1);
12}

对于其他寄存器的操作,U-Boot中\arch\arm\include\asm\system.h的代码可以借鉴,通过移植该部分代码可以实现我们的需求:

 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

对于kernel已经挂在文件系统之后,操作外设寄存器的方法有很多,但是比较倾向于使用devmem这个命令,该命令可以直接操作物理内存空间,不必考虑虚拟地址和物理地址之间的映射。该命令由busybox提供,通过配置busybox可以在生成的文件系统中包含它。

通过devmem操作寄存器的过程如下



END




推荐阅读:


专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
我的知识小密圈


浏览 29
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报