¿Cuál es el uso de__iomem en linux al escribir controladores de dispositivo?
memory-management linux-kernel (1)
He visto que __iomem
se usa para almacenar el tipo de retorno de ioremap()
, pero he usado u32
en la arquitectura ARM para ello y funciona bien.
Entonces, ¿qué diferencia hace __iomem
aquí? ¿Y en qué circunstancias debo usarlo exactamente?
Muchos de los tipos de fundición van a "funcionar bien". Sin embargo, esto no es muy estricto. Nada le impide lanzar un u32
a un u32 *
y u32 *
referencia, pero esto no está siguiendo la API del kernel y es propenso a errores.
__iomem
es una cookie utilizada por Sparse , una herramienta que se utiliza para encontrar posibles fallas de codificación en el kernel. Si no compila el código del kernel con Sparse habilitado, __iomem
se ignorará de todos modos.
Use Sparse primero instalándolo y luego agregando C=1
a su llamada de make
. Por ejemplo, al construir un módulo, use:
make -C $KPATH M=$PWD C=1 modules
__iomem
se define así:
# define __iomem __attribute__((noderef, address_space(2)))
Agregar (y requerir) una cookie como __iomem
para todos los accesos de E / S es una forma de ser más estricto y evitar errores de programación. No desea leer / escribir desde / a las regiones de memoria de E / S con direcciones absolutas porque usualmente usa memoria virtual. Así,
void __iomem *ioremap(phys_addr_t offset, unsigned long size);
normalmente se llama para obtener la dirección virtual de un offset
dirección física de E / S, para un size
longitud especificado en bytes. ioremap()
devuelve un puntero con una cookie __iomem
, por lo que ahora se puede usar con funciones en línea como readl()
/ writel()
(aunque ahora es preferible usar las macros más explícitas ioread32()
/ iowrite32()
, por ejemplo) , que aceptan direcciones __iomem
.
Además, noderef
utiliza el atributo noderef
para asegurarse de no desreferir un puntero __iomem
. La desreferenciación debería funcionar en algunas arquitecturas donde la E / S está realmente asignada a la memoria, pero otras arquitecturas usan instrucciones especiales para acceder a las E / S y, en este caso, la desreferenciación no funciona.
Veamos un ejemplo:
void *io = ioremap(42, 4);
Sparse no es feliz:
warning: incorrect type in initializer (different address spaces)
expected void *io
got void [noderef] <asn:2>*
O:
u32 __iomem* io = ioremap(42, 4);
pr_info("%x/n", *io);
Sparse tampoco es feliz:
warning: dereference of noderef expression
En el último ejemplo, la primera línea es correcta, porque ioremap()
devuelve su valor a una variable __iomem
. Pero entonces, lo deferimos, y se supone que no debemos hacerlo.
Esto hace feliz a Sparse:
void __iomem* io = ioremap(42, 4);
pr_info("%x/n", ioread32(io));
Línea inferior: siempre use __iomem
donde sea necesario (como un tipo de retorno o como un tipo de parámetro), y use Sparse para asegurarse de que lo hizo. También: no desreferenciar un puntero __iomem
.
Edición : Aquí hay un gran artículo de __iomem
sobre el inicio de __iomem
y las funciones que lo utilizan.