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&nbsp;no necesita un identificador de archivo para la asignación. Además, obviamente me equivoquéaddr&nbsp;argumento (pepi&nbsp;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.