6

Abbiamo un sistema incorporato in cui è connesso un dispositivo mappato in memoria e una CPU ARM esegue Linux. Il dispositivo si trova all'indirizzo 0x40400000 e occupa un megabyte (la maggior parte non è supportato da una memoria effettiva, ma lo spazio degli indirizzi viene comunque mappato sul dispositivo). Al momento, non dispone di un driver di dispositivo per questo dispositivo.Mappatura di un dispositivo fisico a un puntatore nello spazio utente

Nel dispositivo è presente uno speciale registro di sola lettura (denominato CID) all'indirizzo 0x404f0704. Questo registro contiene il valore CID = 0x404. Sto cercando di leggere questo registro da un programma in esecuzione su ARM.

Ricerca in rete Ho imparato a conoscere la funzione mmap() che presumibilmente mi consente di accedere a un indirizzo fisico da userspace. Così, cercando di seguire un paio di esempi che ho trovato, ho scritto il seguente 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); 
} 
 

Compilare con ARM cross-compilatore:

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

non riesco a ottenere il risultato atteso. Quello che vedo è che:

pu = 0x40400000 
pcid = 0x404f0704 
CID = 0 

invece dei previsti

CID = 404 

Che cosa mi manca/facendo male qui?


UPDATE:

ho trovato un altro programma demo e seguendo il suo codice sono stato in grado di ottenere il mio codice di lavoro:

 

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

Eppure, io non sono così sicuro perché il 1 ° la versione non ha funzionato. La mia comprensione è stata che una volta che si utilizza MAP_ANONYMOUS non è necessario un handle di file per la mappatura. Inoltre, ho ovviamente scambiato l'argomento addr (pepi nella mia prima versione) come indirizzo fisico. Se sono in questo momento, questo è in realtà l'indirizzo virtuale.

risposta

5

Mmap è la funzione che di solito funziona con gli indirizzi virtuali. Quando chiami il numero mmap(... MAP_ANONYMOUS) (o mmap del file /dev/zero) ti darà un po 'di nuova memoria virtuale, piena di zero. L'indirizzo restituito sarà l'indirizzo della memoria virtuale.

È possibile mmap alcuni file (senza MAP_ANONYMOUS) e quindi mmap eseguirà il mapping del contenuto del file in un intervallo di memoria virtuale.

Il dispositivo si trova all'indirizzo 0x40400000

dispositivo MMIO si trova nella memoria fisica; qualsiasi processo può utilizzare l'indirizzo virtuale 0x40400000; ma saranno mappati (tradotti) in qualche pagina fisica libera da MMU (unità di gestione della memoria). Non puoi semplicemente chiedere al sistema operativo una certa memoria virtuale e aspettarti che venga assegnato alla portata del dispositivo (sarà una variante dell'inferno).

Ma esiste un dispositivo speciale,/dev/mem, che può essere utilizzato come file contenente tutta la memoria fisica. Quando si usa mmap s/dev/mem, si sta chiedendo al sistema operativo di creare una nuova mappatura di qualche memoria virtuale nell'intervallo fisico richiesto.

Nella vostra invocazione del mmap:

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

si chiede di mappare intervallo di memoria fisica [0x40400000 .. 0x4050000-1] (un megabyte, non compreso 0x40500000 byte) in qualche megabyte di memoria virtuale (il suo indirizzo di partenza viene restituito dalla mmap).