地址映射及IO内存访问

地址映射

MMU全称叫做Memory Manage Unit,也就是内存管理单元。在老版本的Linux中要求处理器必须有MMU,但是现在Linux内核已经支持无MMU的处理器了。MMU主要完成的功能如下:

  1. 完成虚拟空间到物理空间的映射。
  2. 内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。

我们重点来看一下第1点,也就是虚拟空间到物理空间的映射,也叫做地址映射。首先了解两个地址概念:虚拟地址(VA,Virtual Address)、物理地址(PA,Physcical Address)。对于32位的处理器来说,虚拟地址范围是2^32=4GB,例如CPU搭配的是512MB的DDR3,这512MB的内存就是物理内存,经过MMU可以将其映射到整个4GB的虚拟空间,如下图所示:

在这里插入图片描述

内存映射

物理内存只有512MB,虚拟内存有4GB,那么肯定 存在多个虚拟地址映射到同一个物理地址上去,虚拟地址范围比物理地址范围大的问题处理器自会处理,这里我们不要去深究,因为MMU是很复杂的一个东西,后续有时间的话正点原子Linux团队会专门做MMU专题教程。

Linux内核启动的时候会初始化MMU,设置好内存映射,设置好以后CPU访问的都是虚拟地址。比如ZYNQ的GPIO模块寄存器基地址为 0xE000A000。如果没有开启MMU的话直接读写0xE000A000这个地址是没有问题的,现在开启了MMU,并且设置了内存映射,因此就不能直接向0xE000A000这个地址写入数据了。我们必须得到0xE000A000这个物理地址在Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数:ioremap和iounmap。

  1. ioremap 函数
    ioremap函数用于获取指定物理地址空间对应的虚拟地址空间,定义在arch/arm/include/asm/io.h文件中,定义如下:

    #define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) 
    
    void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) 
    { 
    	return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); 
    }
    

    ioremap是个宏,有两个参数:cookiesize,真正起作用的是函数__arm_ioremap,此函数有三个参数和一个返回值,这些参数和返回值的含义如下:

    • phys_addr:要映射的物理起始地址。
    • size:要映射的内存空间大小。
    • mtypeioremap的类型,可以选择MT_DEVICEMT_DEVICE_NONSHAREDMT_DEVICE_CACHEDMT_DEVICE_WCioremap函数选择MT_DEVICE
    • 返回值:__iomem类型的指针,指向映射后的虚拟空间首地址。

    假如我们要获取ZYNQ的APER_CLK_CTRL寄存器对应的虚拟地址,使用如下代码即可:

    #define APER_CLK_CTRL 0xF800012C 
    static void __iomem *aper_clk_ctrl_addr; 
    aper_clk_ctrl_addr = ioremap(APER_CLK_CTRL, 4); 
    

    宏定义APER_CLK_CTRL是寄存器物理地址,aper_clk_ctrl_addr是该物理地址映射后的虚拟地址。对于ZYNQ来说一个寄存器是 4字节(32位)的,因此映射的内存长度为4。映射完成以后直接对aper_clk_ctrl_addr进行读写操作即可。

  2. iounmap 函数
    卸载驱动的时候需要使用iounmap函数释放掉 ioremap函数所做的映射,iounmap函数原型如下:
    void iounmap (volatile void __iomem *addr)

    iounmap只有一个参数addr,此参数就是要取消映射的虚拟地址空间首地址。假如我们现在要取消掉APER_CLK_CTRL寄存器的地址映射,使用如下代码即可:
    iounmap(aper_clk_ctrl_addr);

I/O 内存访问函数

这里说的I/O是输入/输出的意思,并不是我们学习单片机的时候讲的 GPIO引脚。这里涉及到两个概念:I/O端口和I/O内存。当外部寄存器或内存映射到IO空间时,称为I/O 端口。当外部寄存器或内存映射到内存空间时,称为I/O内存。但是对于ARM来说没有I/O空间这个概念,因此ARM体系下只有I/O内存(可以直接理解为内存)。

使用ioremap函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作

  1. 读操作函数
    读操作函数有如下几个:
    u8 readb(const volatile void __iomem *addr)
    u16 readw(const volatile void __iomem *addr)
    u32 readl(const volatile void __iomem *addr)

    readbreadwreadl这三个函数分别对应8bit、16bit和32bit读操作,参数addr就是要读取的内存地址,返回值就是读取到的数据。

  2. 写操作函数
    写操作函数有如下几个:
    void writeb(u8 value, volatile void __iomem *addr)
    void writew(u16 value, volatile void __iomem *addr)
    void writel(u32 value, volatile void __iomem *addr)

    writebwritewwritel这三个函数分别对应8bit、16bit和32bit写操作,参数value是要写入的数值,addr是要写入的地址。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值