Mapowanie urządzenia fizycznego na wskaźnik w przestrzeni użytkownika

Mamy wbudowany system, w którym podłączone jest urządzenie mapowane pamięcią, a procesor ARM działa pod kontrolą systemu Linux. Urządzenie znajduje się pod adresem0x40400000 i zajmuje megabajt (większość nie jest wspierana przez rzeczywistą pamięć, ale przestrzeń adresowa i tak jest mapowana na urządzenie). Obecnie jesteśmynie mieć sterownik urządzenia dla tego urządzenia.

W urządzeniu znajduje się specjalny rejestr tylko do odczytu (zwany CID) pod adresem0x404f0704. Ten rejestr zawiera wartośćCID = 0x404. Próbuję odczytać ten rejestr z programu uruchomionego na ARM.

Przeszukując sieć dowiedziałem się ommap() funkcja, która podobno pozwala mi uzyskać dostęp do adresu fizycznego z przestrzeni użytkownika. Więc próbując śledzić kilka przykładów, które znalazłem, napisałem następujący test:


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

Kompilowanie za pomocą kompilatora krzyżowego ARM:

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

Nie mogę uzyskać oczekiwanego rezultatu. Widzę, że:

pu   = 0x40400000
pcid = 0x404f0704
CID  = 0

zamiast oczekiwanego

CID  = 404

Czego mi tutaj brakuje / robię źle?

AKTUALIZACJA:

Znalazłem inny program demonstracyjny i zgodnie z jego kodem udało mi się uruchomić mój kod:


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

Mimo to nie jestem pewien, dlaczego pierwsza wersja nie działa. Moje zrozumienie było takie, jak się używaMAP_ANONYMOUS nie potrzebujesz uchwytu pliku do mapowania. Oczywiście myliłem sięaddr argument (pepi w mojej pierwszej wersji) jako adres fizyczny. Jeśli jestem teraz, to w rzeczywistości jest to adres wirtualny.

questionAnswers(1)

yourAnswerToTheQuestion