Tenemos un sistema integrado donde se conecta un dispositivo mapeado de memoria, y una CPU ARM ejecuta Linux. El dispositivo se encuentra en la dirección 0x40400000
y ocupa un megabyte (la mayoría no está respaldado por una memoria real, pero el espacio de direcciones se asigna al dispositivo de todos modos). Actualmente no tenga un controlador de dispositivo para este dispositivo.Asignación de un dispositivo físico a un puntero en el espacio de usuario
En el dispositivo hay un registro especial de solo lectura (llamado CID) en la dirección 0x404f0704
. Este registro contiene el valor CID = 0x404
. Intento leer este registro de un programa que se ejecuta en ARM.
Buscando en la red Me enteré de la función mmap()
que supuestamente me permite acceder a una dirección física desde el espacio de usuario. Por lo tanto, tratando de seguir un par de ejemplos que encontré, escribí la siguiente prueba:
#include <sys/mman.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *pdev = (void *) 0x40400000;
size_t ldev = (1024*1024);
int *pu;
int volatile *pcid;
int volatile cid;
pu = mmap(pdev, ldev, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (pu == MAP_FAILED)
errx(1, "mmap failure");
pcid = (int *) (((void *) pu) + 0xf0704);
printf("pu = %08p\n", pu);
printf("pcid = %08p\n", pcid);
cid = *pcid;
printf("CID = %x\n", cid);
munmap(pu, ldev);
return (EXIT_SUCCESS);
}
Compilación con el brazo compilador cruzado:
a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c
no puede obtener el resultado esperado. Lo que veo es que:
pu = 0x40400000
pcid = 0x404f0704
CID = 0
en lugar del esperado
CID = 404
¿Cuál falto/haciendo mal aquí?
ACTUALIZACIÓN:
encontré otro programa de demostración y siguiendo su código pude conseguir mi código de trabajo:
int main(void)
{
off_t dev_base = 0x40400000;
size_t ldev = (1024 * 1024);
unsigned long mask = (1024 * 1024)-1;
int *pu;
void *mapped_base;
void *mapped_dev_base;
int volatile *pcid;
int volatile cid;
int memfd;
memfd = open("/dev/mem", O_RDWR | O_SYNC);
mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK);
if (mapped_base == MAP_FAILED)
errx(1, "mmap failure");
mapped_dev_base = mapped_base + (dev_base & MAP_MASK);
pu = mapped_dev_base;
pcid = (int *) (((void *) pu) + 0xf0704);
printf("pu = %08p\n", pu);
printf("pcid = %08p\n", pcid);
cid = *pcid;
printf("CID = %x\n", cid);
munmap(mapped_base, ldev);
close(memfd);
return (EXIT_SUCCESS);
}
Sin embargo, no estoy tan seguro de por qué la primera la versión no funcionó Según tengo entendido, una vez que utiliza MAP_ANONYMOUS
, no necesita un identificador de archivo para la asignación. Además, obviamente confundí el argumento addr (pepi
en mi primera versión) para que sea la dirección física. Si estoy en este momento, esta es en realidad la dirección virtual.