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.