• Linux下如何操作寄存器


    本期主题:
    linux下操作寄存器


    往期链接:



    1.为什么有这个问题

    Linux开发与裸机开发不同,在裸机开发中直接使用指针方式就能访问到对应地址,但是在Linux中,由于内存管理的存在,并不能这么粗暴的直接用指针进行访问。

    那么Linux下的内存管理是怎样的呢?linux内存管理相当复杂,本篇文章只能简单介绍一部分:

    1.内存空间与I/O空间

    • I/O空间: 在x86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言的,通过特定的指令 in、out来访问,端口号标识了外设的寄存器地址;
    //intel的in指令格式如下:
    IN 累加器, { 端口号 | DX}
    
    • 1
    • 2
    • 内存空间: 大多数的嵌入式微控制器(如ARM、PowerPC等)并不提供I/O空间,仅存在内存空间,可以直接通过地址、指针来进行访问;

    下图给出了内存空间和I/O空间的对比:
    在这里插入图片描述
    从上图可以看出,内存空间是必须的,而I/O空间是可选的。

    2.内存管理单元

    高性能处理器一般会提供一个内存管理单元MMU,该单元辅助操作系统进行内存管理。
    提供虚拟地址和物理地址映射内存访问权限保护cache缓存控制等硬件支持。

    3.Linux内存管理

    对于包含MMU的处理器,linux提供了复杂的存储管理系统,(以32位处理器为例),进程所能够访问的空间达到4GB。
    在Linux系统中,进程的4GB内存空间被分为两个部分——用户空间和内核空间,具体的描述可以看这个链接:操作系统系列(二)——进程

    2.用户空间下怎么访问寄存器

    用户空间下我们对于寄存器的访问可以使用 mmap接口

    1.mmap原理

    mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。

    在这里插入图片描述

    2.mmap函数原型

    void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
    
    • 1

    参数:

    start:映射区的开始地址
    length:映射区的长度
    
    prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
    PROT_EXEC :页内容可以被执行
    PROT_READ :页内容可以被读取
    PROT_WRITE :页可以被写入
    PROT_NONE :页不可访问
    
    flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
    MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
    MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
    MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
    MAP_DENYWRITE //这个标志被忽略。
    MAP_EXECUTABLE //同上
    MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
    MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
    MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
    MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
    MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
    MAP_FILE //兼容标志,被忽略。
    MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
    MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
    MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
    
    fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1
    
    offset:被映射对象内容的起点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.mmap使用例子

    以操作一个地址为0xA4030000的寄存器为例

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #deinfe REG_BASE 0xA4030000
    
    int main(void)
    {
    	int fd;
    	void *map_reg_base;
        // map to register base and memory
    	fd = open("/dev/mem", O_RDWR | O_SYNC);
    	if (fd)	{
    		DEBUG("Success to open /dev/mem fd=%08x\n", fd);
    	} else {
    		DEBUG("Fail to open /dev/mem fd=%08x\n", fd);
    	}
    	//这里的map_reg_base就是被mmap过后的指针,可以直接操作了
    	map_reg_base = mmap(0, 0x400, PROT_READ | PROT_WRITE,
    			MAP_SHARED, fd, REG_BASE);
        printf("--------start write regs\n");
        *(volatile uint32_t *)map_reg_base = 0x01;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试结果:

    $ devmem 0xA4030000 32
    0x81
    $ ./test
    $ devmem 0xA4030000 32
    0x01
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.内核空间下怎么访问寄存器

    1.ioremap

    在内核中访问I/O内存(通常是一些控制器的寄存器)之前,需要使用ioremap()函数将设备所处的物理地址映射到虚拟地址上

    函数原型:

    void __iomem *ioremap(resource_size_t phys_addr, unsigned long size)
    /**
     * ioremap     -   map bus memory into CPU space
     * @phys_addr:    bus address of the memory
     * @size:      size of the resource to map
     *
     * ioremap performs a platform specific sequence of operations to
     * make bus memory CPU accessible via the readb/readw/readl/writeb/
     * writew/writel functions and the other mmio helpers. The returned
     * address is not guaranteed to be usable directly as a virtual
     * address.
     *
     * This version of ioremap ensures that the memory is marked uncachable
     * on the CPU as well as honouring existing caching rules from things like
     * the PCI bus. Note that there are other caches and buffers on many
     * busses. In particular driver authors should read up on PCI writes
     *
     * It's useful if some control registers are in such an area and
     * write combining or read caching is not desirable:
     *
     * Must be freed with iounmap.
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.实际例子

    使用ioremap和不使用ioremap进行对比:

    #include 
    #include 
    #include 
    #include 
    
    #define USE_IOREMAP
    
    #define H3_GPIO_BASE (0x01C20800)
    
    static volatile unsigned long *gpio_regs = NULL;
    
    static int __init ioremap_mod_init(void)
    {
    
        int i = 0;
        printk(KERN_INFO "ioremap_mod init\n");
    
    #ifdef USE_IOREMAP
        gpio_regs = (volatile unsigned long *)ioremap(H3_GPIO_BASE, 1024);
    #else
        gpio_regs = (volatile unsigned long *)H3_GPIO_BASE;
    #endif
    
        for (i=0; i<3; i++)
            printk(KERN_INFO "reg[%d] = %lx\n", i, gpio_regs[i]);
    
        return 0;
    }
    module_init(ioremap_mod_init);
    
    static void __exit ioremap_mod_exit(void)
    {
        printk(KERN_INFO "ioremap_mod exit\n ");
    
    #ifdef USE_IOREMAP
        iounmap(gpio_regs);
    #endif 
    }
    
    module_exit(ioremap_mod_exit);
    
    MODULE_AUTHOR("es-hacker");
    MODULE_LICENSE("GPL v2");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    实验结果:
    1.使用ioremap

    $ insmod ioremap
    ioremap_mod init
    reg[0] = 71227722
    reg[1] = 33322177
    reg[2] = 773373
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.不使用ioremap

    $ insmod ioremap_mod.ko
    
    Unable to handle kernel paging request at virtual address 01c20800
    pgd = c9ece7c0
    [01c20800] *pgd=6ddd7003, *pmd=00000000
    Internal error: Oops: 206 [#1] SMP ARM
    CPU: 1 PID: 1253 Comm: insmod Tainted: G           O    4.14.111 #116
    Hardware name: sun8i
    task: ef15d140 task.stack: edc50000
    PC is at ioremap_mod_init+0x3c/0x1000 [ioremap_mod]
    LR is at ioremap_mod_init+0x14/0x1000 [ioremap_mod]
    pc : [<bf5d903c>]    lr : [<bf5d9014>]    psr: 600e0013
    sp : edc51df8  ip : 00000007  fp : 118fa95c
    r10: 00000001  r9 : ee7056c0  r8 : bf5d6048
    r7 : bf5d6000  r6 : 00000000  r5 : bf5d6200  r4 : 00000000
    r3 : 01c20800  r2 : 01c20800  r1 : 00000000  r0 : bf5d5048
    Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
    Control: 30c5387d  Table: 49ece7c0  DAC: b106c794
    Process insmod (pid: 1253, stack limit = 0xedc50210)
    
    [<bf5d903c>] (ioremap_mod_init [ioremap_mod]) from [<c0201a70>] (do_one_initcall+0x40/0x16c)
    [<c0201a70>] (do_one_initcall) from [<c02b20c8>] (do_init_module+0x60/0x1f0)
    [<c02b20c8>] (do_init_module) from [<c02b1214>] (load_module+0x1b48/0x2250)
    [<c02b1214>] (load_module) from [<c02b1ad8>] (SyS_finit_module+0x8c/0x9c)
    [<c02b1ad8>] (SyS_finit_module) from [<c0221f80>] (ret_fast_syscall+0x0/0x4c)
    Code: e5953000 e3050048 e1a01004 e34b0f5d (e7932104) 
    ---[ end trace 928c64a33a054308 ]---
    Segmentation fault
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    例子截图:
    在这里插入图片描述

  • 相关阅读:
    简单介绍二分类问题评价指标
    如何用jxTMS开发一个功能(六)
    fiddler导出录制脚本并导出jmter脚本文件
    【opencv】dnn示例-scene_text_detection.cpp 场景文本区域检测
    073:vue+openlayers拖拽实现放大所选区域 (DragZoom示例代码)
    亚马逊云科技在天津设立智能制造数字化赋能中心
    Docker | redis安装及测试
    【AUTOSAR-CanIf】-2.2-图解常用Software Filter Algorithm及其应用场景
    AOF日志:宕机了,Redis如何避免数据丢失?
    编译[Bug]——too few arguments for template template parameter “Tuple“ detected
  • 原文地址:https://blog.csdn.net/weixin_37620587/article/details/127945335