当前位置: 首页 > Linux

嵌入式编程中如何使用mmap访问CPU寄存器

时间:2023-04-06 21:39:17 Linux

我忘记了我之前在哪里。看到一道面试题:如果Linux不允许你写内核驱动,但是你想访问内核寄存器,怎么办?答案是对Linux设备节点/dev/mem使用mmap()系统调用。本文地址:https://segmentfault.com/a/1190000008381626Reference/dev/memmmap详细解释Linux驱动虚拟地址和物理地址的映射嵌入式Linux应用如何读取(修改)芯片寄存器文件-C的值-使用mmap时出现总线错误-StackOverflowmmap()和/dev/memmmap()已知用于内存映射。可以将文件描述符映射到内存,直接读写文件描述符。/dev/mem不太关心。该设备节点在Linux中可用,是物理内存的完整映像。这真太了不起了。如果你访问这个节点,你实际上可以这样说:你获得了整个系统的最高权限。用户空间的设备驱动的实现其实就是通过这个节点来实现的。许多运行Linux的芯片将自己的寄存器映射到一段内存空间。与/dev/mem相比,这些只是绝对地址上的一段内存空间,所以可以通过mmap对应的地址段实现对指定寄存器的访问。这就是Buserror的原因,但是当我实际编写代码并尝试直接映射/dev/mem时,内核直接抛出“Buserror”错误消息并停止我的程序。查阅资料后知道了一个很重要的信息:内存映射必须是页对齐的。这时候就需要使用getpagesize()来获取页面大小。思路是:当上层调用需要获取某个段地址时,程序先找到对应的段起始地址,再找到mmap后的偏移量。代码废话,直接上代码(很短):staticintread_global_mem(void*out,size_tlength,off_toffset){uint8_t*data=NULL;intret=-1;off_tpageSize=getpagesize();size_tactualLen=0;off_tactualOffset=0;if(NULL==out||0==length){errno=EINVAL;转到结束;}intfd=open("/dev/mem",O_RDWR|O_SYNC);如果(fd<0){转到结束;}actualOffset=offset&~(pageSize-1);//找到对应的段开始actualLen=length+(offset-actualOffset);//确定mmap实际需要的长度data=mmap(NULL,actualLen,PROT_READ,MAP_SHARED,fd,actualOffset);if(NULL==data){gotoENDS;}memmove(out,data+(offset-actualOffset),length);/*成功*/ret=0;ENDS:if(fd){close(fd);fd=-1;}如果(数据){蒙马p(数据,长度);数据=空;}returnret;}寄存器写入上面的代码是关于寄存器读取和寄存器写入的代码基本相同,有几点不同:在mmap()时,改为“PROT_READ|PROT_WRITE”修改指定数据段后共享内存,调用msync()将修改后的寄存器值写回内核