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

Vir*_*mar 19 linux memory-management linux-device-driver linux-kernel

我已经看到它__iomem用于存储返回类型ioremap(),但我u32在ARM体系结构中使用它并且它运行良好.

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

eep*_*epp 39

很多类型演员都会"运作良好".但是,这不是很严格.没有什么可以阻止你将它u32转换为u32 *和取消引用它,但这不遵循内核API并且容易出错.

__iomemSparse使用的cookie,用于在内核中查找可能的编码错误的工具.如果您没有在启用Sparse的情况下编译内核代码,__iomem则无论如何都将被忽略.

首先安装Sparse使用Sparse,然后添加C=1到您的make呼叫中.例如,在构建模块时,使用:

make -C $KPATH M=$PWD C=1 modules
Run Code Online (Sandbox Code Playgroud)

__iomem 定义如下:

# define __iomem        __attribute__((noderef, address_space(2)))
Run Code Online (Sandbox Code Playgroud)

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

void __iomem *ioremap(phys_addr_t offset, unsigned long size);
Run Code Online (Sandbox Code Playgroud)

通常调用以获取offset指定长度(size以字节为单位)的I/O物理地址的虚拟地址.ioremap()返回一个带有__iomemcookie 的指针,因此现在可以使用内联函数,例如readl()/ writel()(尽管现在最好使用更明确的宏ioread32()/ iowrite32(),例如),它接受__iomem地址.

此外,noderefSparse使用该属性来确保您不取消引用__iomem指针.解除引用应该适用于I/O实际上是内存映射的某些体系结构,但是其他体系结构使用特殊指令来访问I/O,在这种情况下,解除引用将不起作用.

我们来看一个例子:

void *io = ioremap(42, 4);
Run Code Online (Sandbox Code Playgroud)

稀疏不开心:

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

要么:

u32 __iomem* io = ioremap(42, 4);
pr_info("%x\n", *io);
Run Code Online (Sandbox Code Playgroud)

稀疏也不开心:

warning: dereference of noderef expression
Run Code Online (Sandbox Code Playgroud)

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

这让Sparse很高兴:

void __iomem* io = ioremap(42, 4);
pr_info("%x\n", ioread32(io));
Run Code Online (Sandbox Code Playgroud)

底线:始终使用__iomem所需的位置(作为返回类型或参数类型),并使用Sparse确保您这样做.另外:不要取消引用__iomem指针.

编辑:这是一篇关于__iomem使用它的初始和函数的LWN文章.