6

Wir verfügen über ein eingebettetes System, in das ein Speichergerät eingebunden ist, und auf einer ARM-CPU wird Linux ausgeführt. Das Gerät befindet sich unter der Adresse 0x40400000 und belegt ein Megabyte (das meiste davon ist nicht durch einen tatsächlichen Speicher belegt, aber der Adressraum ist dem Gerät trotzdem zugeordnet). Wir haben derzeit nicht haben einen Gerätetreiber für dieses Gerät.Zuordnung eines physischen Geräts zu einem Zeiger im Benutzerbereich

Im Gerät gibt es ein spezielles Nur-Lese-Register (CID) unter der Adresse 0x404f0704. Dieses Register enthält den Wert CID = 0x404. Ich versuche, dieses Register von einem Programm zu lesen, das auf dem ARM läuft.

Suche im Internet habe ich über die mmap() Funktion, die angeblich ich auf eine physische Adresse aus dem Benutzerbereich zugreifen erfahren. Also, versuchte ein paar Beispiele zu folgen, ich fand, schrieb ich den folgenden 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); 
} 
 

Kompilieren mit den ARM-Cross-Compiler:

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

Ich kann nicht das erwartete Ergebnis. Was ich sehe, ist, dass:

pu = 0x40400000 
pcid = 0x404f0704 
CID = 0 

statt der

erwartet
CID = 404 

Was bin ich fehlt/falsch hier?


UPDATE:

ich ein weiteres Demo-Programm gefunden und seinen Code folgende konnte ich meinen Code zum Laufen bringen:

 

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

Aber ich bin nicht so sicher, warum die erste Version hat nicht funktioniert. Mein Verständnis war, dass, sobald Sie MAP_ANONYMOUS verwenden, Sie kein Dateihandle für das Mapping benötigen. Außerdem verwechselte ich offensichtlich das Adr Argument (pepi in meiner 1. Version), um die physikalische Adresse zu sein. Wenn ich gerade bin, dann ist das eigentlich die virtuelle Adresse.

Antwort

5

Mmap ist die Funktion, die normalerweise mit virtuellen Adressen funktioniert. Wenn Sie mmap(... MAP_ANONYMOUS) (oder mmap von /dev/zero Datei aufrufen) wird es Ihnen eine gewisse Menge an neuen virtuellen Speicher, mit Null gefüllt. Die zurückgegebene Adresse ist die Adresse des virtuellen Speichers.

Sie können eine Datei mappen (ohne MAP_ANONYMOUS) und dann wird mmap den Inhalt der Datei in einen virtuellen Speicherbereich abbilden.

Das Gerät wird an der Adresse 0x40400000 angeordnet

Geräte MMIO im physischen Speicher befindet; Jeder Prozess kann die virtuelle Adresse 0x40400000 verwenden; aber sie werden von einer MMU (Speicherverwaltungseinheit) auf eine freie physikalische Seite abgebildet (übersetzt). Sie können nicht einfach OS nach virtuellem Speicher fragen und erwarten, dass dieser in den Gerätebereich passt (es wird eine Variante der Hölle sein).

Aber es gibt ein spezielles Gerät,/dev/mem, das als Datei verwendet werden kann, die den gesamten physischen Speicher enthält. Wenn Sie mmap s/dev/mem fragen, bitten Sie das Betriebssystem, ein neues Mapping von virtuellem Speicher in den angeforderten physischen Bereich zu erstellen.

In Ihrem Aufruf von mmap:

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

Sie fragen physischen Speicherbereich abzubilden [0x40400000 .. 0x4050000-1] (ein Megabyte, nicht einschließlich Byte 0x40500000) in einigen Megabyte des virtuellen Speichers (seine Startadresse von mmap zurückgegeben wird).