6

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.

Respuesta

5

Mmap es la función que generalmente funciona con direcciones virtuales. Cuando llame al mmap(... MAP_ANONYMOUS) (o al mmap del archivo /dev/zero), obtendrá una cantidad de memoria virtual nueva, que se llenará con cero. La dirección devuelta será la dirección de la memoria virtual.

Puede mmap algunos archivos (sin MAP_ANONYMOUS) y luego mmap mapeará los contenidos del archivo en algún rango de memoria virtual.

El dispositivo se encuentra en la dirección 0x40400000

Dispositivo MMIO se encuentra en la memoria física; cualquier proceso puede usar la dirección virtual 0x40400000; pero serán mapeados (traducidos) a alguna página física gratuita por MMU (unidad de gestión de memoria). No se puede simplemente pedirle al sistema operativo algo de memoria virtual y esperar que se mmapeará al rango del dispositivo (será una variante del infierno).

Pero hay un dispositivo especial,/dev/mem, que se puede utilizar como archivo que contiene toda la memoria física. Cuando mmap s/dev/mem le está pidiendo al sistema operativo que cree una nueva asignación de alguna memoria virtual en el rango físico solicitado.

En su invocación de mmap:

mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, 
    MAP_SHARED, memfd, dev_base & ~MAP_MASK); 

le preguntas al mapa de distribución de la memoria física [0x40400000 .. 0x4050000-1] (un megabyte; sin incluir 0x40500000 byte) en algún megabyte de memoria virtual (su dirección de partida es devuelto por mmap).

Cuestiones relacionadas