Asignación de un dispositivo físico a un puntero en el espacio de usuario
Tenemos un sistema integrado donde se conecta un dispositivo asignado en memoria, y una CPU ARM ejecuta Linux. El dispositivo se encuentra en la dirección.0x40400000
y ocupa un megabyte (la mayor parte no está respaldada por una memoria real, pero el espacio de direcciones se asigna al dispositivo de todos modos). Nosotros actualmenteno hacer tener un controlador de dispositivo para este dispositivo.
En el dispositivo hay un registro especial de solo lectura (llamado CID) en la dirección0x404f0704
. Este registro contiene el valor.CID = 0x404
. Estoy tratando de leer este registro de un programa que se ejecuta en el ARM.
Buscando en la red aprendí sobre elmmap()
función que supuestamente me permite acceder a una dirección física desde el espacio de usuario. Entonces, 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);
}
Compilando con el compilador cruzado ARM:
a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c
No puedo obtener el resultado esperado. Lo que veo es que:
pu = 0x40400000
pcid = 0x404f0704
CID = 0
en lugar de lo esperado
CID = 404
¿Qué me estoy perdiendo / haciendo mal aquí?
ACTUALIZAR:
Encontré otro programa de demostración y siguiendo su código pude hacer funcionar mi código:
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 versión no funcionó. Mi entendimiento fue que una vez que usasMAP_ANONYMOUS
no necesita un identificador de archivo para la asignación. Además, obviamente me equivoquéaddr argumento (pepi
en mi 1ra versión) para ser la dirección física. Si estoy en este momento, entonces esta es realmente la dirección virtual.