Mapeando um dispositivo físico para um ponteiro no espaço do usuário

Temos um sistema embarcado no qual um dispositivo de memória mapeada está conectado e uma CPU ARM executa o Linux. O dispositivo está localizado no endereço0x40400000 e ocupa um megabyte (a maior parte não é suportada por uma memória real, mas o espaço de endereço é mapeado para o dispositivo de qualquer maneira). Nós atualmentenão faça tem um driver de dispositivo para este dispositivo.

No dispositivo, há um registro especial somente leitura (chamado CID) no endereço0x404f0704. Este registro contém o valorCID = 0x404. Eu estou tentando ler este registro de um programa em execução no ARM.

Pesquisando na net eu aprendi sobre ommap() função que supostamente me permite acessar um endereço físico do userspace. Então, tentando seguir alguns exemplos que encontrei, escrevi o seguinte teste:


#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 com o compilador cruzado ARM:

a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c

Eu não posso obter o resultado esperado. O que eu vejo é que:

pu   = 0x40400000
pcid = 0x404f0704
CID  = 0

em vez do esperado

CID  = 404

O que estou perdendo / fazendo errado aqui?

ATUALIZAR:

Eu encontrei outro programa de demonstração e seguindo seu código, consegui fazer meu código funcionar:


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);
}

Ainda assim, não tenho certeza do motivo pelo qual a primeira versão não funcionou. Meu entendimento foi que uma vez que você usaMAP_ANONYMOUS você não precisa de um identificador de arquivo para o mapeamento. Além disso, eu obviamente confundi oaddr argumento (pepi na minha primeira versão) para ser o endereço físico. Se estou agora, então este é realmente o endereço virtual.

questionAnswers(1)

yourAnswerToTheQuestion