在编写设备驱动程序时,linux中__iomem有什么用?

我已经看到__iomem用于存储ioremap()的返回types,但是我已经在ARM体系结构中使用了u32 ,它工作的很好。

那么__iomem在这里有什么不同? 在哪种情况下我应该使用它?

许多类型的演员将会“很好地工作”。 不过,这不是很严格。 没有什么能够阻止你将u32转换成u32 *并将其解引用,但是这不是跟随内核API,并且容易出错。

__iomem是Sparse所使用的一个cookie,用于在内核中查找可能的编码错误。 如果你没有启用Sparse编译你的内核代码, __iomem将被忽略。

先使用Sparse安装它,然后将C=1添加到make调用中。 例如,在构建模块时,请使用:

 make -C $KPATH M=$PWD C=1 modules 

__iomem被定义如下:

 # define __iomem __attribute__((noderef, address_space(2))) 

为所有I / O访问添加(并要求)像__iomem这样的cookie是更严格的一种方法,可以避免编程错误。 您不希望使用绝对地址从/向/到I / O内存区域进行读取/写入,因为您通常使用虚拟内存。 从而,

 void __iomem *ioremap(phys_addr_t offset, unsigned long size); 

通常被称为获取I / O物理地址offset的虚拟地址,以字节为单位的指定长度sizeioremap()返回一个带有__iomem cookie的指针,所以现在可以使用像readl() / writel()这样的内联函数(尽管现在最好使用更明确的宏ioread32() / iowrite32() ) ,它接受__iomem地址。

此外,Sparse使用__iomem属性来确保不取消引用__iomem指针。 解引用应该在一些I / O实际上是内存映射的体系结构上工作,而其他体系结构则使用特殊的指令访问I / O,在这种情况下,解引用将不起作用。

我们来看一个例子:

 void *io = ioremap(42, 4); 

稀疏不开心:

 warning: incorrect type in initializer (different address spaces) expected void *io got void [noderef] <asn:2>* 

要么:

 u32 __iomem* io = ioremap(42, 4); pr_info("%x\n", *io); 

稀疏也不高兴:

 warning: dereference of noderef expression 

在最后一个示例中,第一行是正确的,因为ioremap()将其值返回给__iomem变量。 但是,我们尊重它,我们不应该这样做。

这让稀疏感到开心:

 void __iomem* io = ioremap(42, 4); pr_info("%x\n", ioread32(io)); 

底线:在需要的地方总是使用__iomem (作为返回类型或参数类型),并使用Sparse确保您这样做。 另外:不要取消引用__iomem指针。

编辑 :这是一个伟大的LWN文章关于__iomem和函数使用它的开始。