linux内核访问寄存器,嵌入式编程中应如何使用 mmap 访问 CPU 寄存器

之前忘了在哪了,看到一个面试题:在 Linux 中如果不允许你写内核驱动,但是要访问内核寄存器,那应该怎么做?

答案就是使用 mmap() 系统调用,搭配 Linux 的一个设备节点 /dev/mem。

Reference

bVD0IB?w=800&h=3

mmap() 和 /dev/mem

mmap() 大家都知道,是用来做内存映射的,可以将一个文件描述符映射到内存当中,实现对该文件描述符的直接读写。

/dev/mem 则关注的人比较少。这个设备节点在 Linux 中都有,是物理内存的完整映像。这个厉害了,如果你访问这个节点,实际可以这么说:你就取得了对整个系统的最高权限。在用户空间中实现设备驱动,实际上就是通过这个节点实现的。

很多跑 Linux 的芯片,会将自己的寄存器映射到内存空间的一个段中。这些相对于 /dev/mem 来说就是绝对地址上的一段内存空间而已,因此通过 mmap 对应的地址段,就可以实现对指定寄存器的访问。

bVD0IB?w=800&h=3

Bus error

道理是这么讲,但是实际上写代码,直接尝试去映射 /dev/mem 的时候,内核却直接抛出 “Bus error” 的错误信息,把我的程序中止了。查阅资料,才知道很重要的一个信息:内存映射必须以页对齐。

这个时候就需要用 getpagesize() 来获取页的大小啦。思路就是:当上层调用需要获取某段地址的时候,程序首先找到其对应的段起始地址,mmap 了之后再找偏移。

bVD0IB?w=800&h=3

代码

废话少说,直接上代码(很短):

static int read_global_mem(void *out, size_t length, off_t offset)

{

uint8_t *data = NULL;

int ret = -1;

off_t pageSize = getpagesize();

size_t actualLen = 0;

off_t actualOffset = 0;

if (NULL == out || 0 == length) {

errno = EINVAL;

goto ENDS;

}

int fd = open("/dev/mem", O_RDWR | O_SYNC);

if (fd < 0) {

goto ENDS;

}

actualOffset = offset & ~(pageSize - 1); // 找到对应的段起始

actualLen = length + (offset - actualOffset); // 判断实际需要 mmap 的长度

data = mmap(NULL, actualLen, PROT_READ, MAP_SHARED, fd, actualOffset);

if (NULL == data) {

goto ENDS;

}

memmove(out, data + (offset - actualOffset), length);

/* success */

ret = 0;

ENDS:

if (fd) {

close(fd);

fd = -1;

}

if (data) {

munmap(data, length);

data = NULL;

}

return ret;

}

bVD0IB?w=800&h=3

寄存器写

上面的代码是关于寄存器读的。寄存器写的代码基本上是差不多的,不同的有几个:

mmap() 的时候,改成 “PROT_READ | PROT_WRITE”

在共享内存中修改了指定的数据段之后,调用 msync() 将被修改了的寄存器值写回内核

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值