最近看了一下PowerPC(E500)的MMU部分,看了ioremap的介绍,比较有感触,所以简单比较了一下MIPS,PowerPC和ARM(这里只考虑32位版本的,MIPS64和PowerPC64不在此范围内)访问I/O的方式。首先这三种体系结构都使用存储器映射的I/O,都是32位物理地址空间(排除一些特殊的处理器,比如PowerPC E500 v2支持36位物理地址)。下面分别说明一下具体的访问方式:
MIPS:
由于MIPS的地址空间映射机制比较特殊,kernel在访问kseg0和kseg1段时,根本不需要经过MMU,虚拟地址直接减去一个偏移就是物理地址(虽说PowerPC和ARM看起来也是虚拟地址减一个偏移,但是实际上都是经过TLB的)。MIPS处理器一般把外设映射到虚拟地址0xA0000000-0xBFFFFFFF之间的512MB地址空间中,而且不通过Cache。所以只要知道外设的物理地址,则只需在代码中直接加上0xA0000000就是外设的虚拟地址,这样访问起来速度比较快。但是,缺点是灵活性比较差,主内存和外设都只能映射在512MB的地址空间中,限制比较多。
PowerPC(E500):
对于E500来说,不管访问什么地址都是要通过MMU的。kernel启动的时候,已经把kernel的虚拟地址空间的kmalloc区域在TLB1中建立了直接映射。而访问I/O则需要通过ioremap()函数,在页表中建立虚拟地址和物理地址的映射后才能正常访问。范围一般是ioremap_base(一般是0xFE000000)到ioremap_bot。这里需要注意的是,ioremap_bot是小于ioremap_base的。而这会影响vmalloc区域的使用,这段区域是从VMALLOC_START到ioremap_bot,也就是说PowerPC的vmalloc区域是随着ioremap_bot的变化而变化的。所以PowerPC访问外设要先建立页表(在TLB0中),比较麻烦,访问速度比较慢。访问结束后要使用iounmap()撤销映射。当然PowerPC也提供另外一种访问I/O的方式——io_block_mapping()。可以在TLB1中建立永久的块映射,最大可以到256MB,这样访问的时候就不需要调用ioremap()临时分配虚拟地址,并建立页表了。不过Linux PowerPC的maintainer并不推荐这种方式,原因是滥用io_block_mapping会破坏PowerPC内核虚拟地址空间的布局。
ARM:
ARM有点类似于PowerPC,访问任何地址都要通过MMU。但是ARM Linux的做法是为每一个平台定义一个静态数组standard_io_desc[],其中定义了每一个外设的物理地址和对应的虚拟地址,这是由程序员自己根据需要分配的,一般来讲是分配在虚拟地址的高端部分,比如在0xF0000000以上,并且不与vmalloc区域重叠(对于ARM来说,vmalloc区域在kmalloc区域+8MB——I/O区域之间,不同的平台地址不太一样)。然后调用xxx_map_io()函数建立页表,一般会根据不同设备的地址范围的大小使用1MB或者4KB的页,这样就建立了外设的物理地址和内核虚拟地址的映射关系。然后,一般还会在头文件中定义外设的各个寄存器的虚拟地址,在操作外设时就直接读写这些宏所定义的虚拟地址就可以了,不需要调用ioremap动态的建立页表。这种方法类似于PowerPC的io_block_mapping()。所以ARM访问外设的速度要比PowerPC快一点,因为省去了动态建立页表的步骤。
总得来说,MIPS,PowerPC和ARM访问I/O的方式各有千秋,MIPS是速度较快,操作简单,但是灵活性较差;PowerPC是灵活性大但是速度较慢;ARM算是在二者之间取了一个平衡。
阅读(1758) | 评论(0) | 转发(0) |