Domanda Come accedere agli indirizzi fisici dallo spazio utente in Linux?


Su un sistema basato su ARM che esegue Linux, ho un dispositivo che è mappato in memoria a un indirizzo fisico. Da un programma spaziale utente in cui tutti gli indirizzi sono virtuali, come posso leggere il contenuto da questo indirizzo?


22
2017-08-20 15:12


origine


risposte:


È possibile associare un file di dispositivo a una memoria di processo utente utilizzando mmap(2) chiamata di sistema. Di solito, i file di dispositivo sono mappature della memoria fisica sul file system. Altrimenti, devi scrivere un modulo del kernel che crea un tale file o fornisce un modo per mappare la memoria necessaria a un processo utente.

Un altro modo è di rimappare parti di / dev / mem in una memoria utente.

Modificare: Esempio di mmaping / dev / mem (questo programma deve avere accesso a / dev / mem, ad esempio avere i diritti di root):

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
        return 0;
    }

    off_t offset = strtoul(argv[1], NULL, 0);
    size_t len = strtoul(argv[2], NULL, 0);

    // Truncate offset to a multiple of the page size, or mmap will fail.
    size_t pagesize = sysconf(_SC_PAGE_SIZE);
    off_t page_base = (offset / pagesize) * pagesize;
    off_t page_offset = offset - page_base;

    int fd = open("/dev/mem", O_SYNC);
    unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
    if (mem == MAP_FAILED) {
        perror("Can't map memory");
        return -1;
    }

    size_t i;
    for (i = 0; i < len; ++i)
        printf("%02x ", (int)mem[page_offset + i]);

    return 0;
}

30
2017-08-20 16:18



busybox devmem

busybox devmem è una piccola utility CLI che mmaps /dev/mem.

Puoi farlo in Ubuntu con: sudo apt-get install busybox

Utilizzo: leggere 4 byte dall'indirizzo fisico 0x12345678:

sudo busybox devmem 0x12345678

Scrivi 0x9abcdef0 a questo indirizzo:

sudo busybox devmem 0x12345678 w 0x9abcdef0

Fonte: https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85

MAP_SHARED

Quando mmapping /dev/mem, probabilmente vorrai usare:

open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)

MAP_SHARED rende le scritture passate immediatamente alla memoria fisica, il che rende più semplice l'osservazione e ha più senso per le scritture del registro hardware.

CONFIG_STRICT_DEVMEM e nopat

Usare /dev/mem per vedere e modificare la RAM regolare sul kernel v4.9, devi pugno:

  • disattivare CONFIG_STRICT_DEVMEM (impostato di default su Ubuntu 17.04)
  • passa il nopat opzione della riga di comando del kernel per x86

Le porte di I / O funzionano ancora senza quelle.

Guarda anche: mmap di / dev / mem ha esito negativo con argomento non valido per l'indirizzo virt_to_phys, ma l'indirizzo è allineato alla pagina

Flusso della cache

Se si tenta di scrivere in RAM invece di un registro, la memoria potrebbe essere memorizzata nella cache dalla CPU: Come svuotare la cache della CPU per un'area di spazio indirizzo in Linux? e non vedo un modo molto portabile / semplice per svuotarlo o contrassegnare la regione come non memorizzabile:

Quindi forse /dev/mem non può essere usato in modo affidabile per passare i buffer di memoria ai dispositivi?

Questo non può essere osservato in QEMU, purtroppo, poiché QEMU non simula le cache.

Come testarlo

Adesso per la parte divertente. Ecco alcune impostazioni interessanti:

  • Userland memoria
    • assegnare volatile variabile su un processo userland
    • ottenere l'indirizzo fisico con /proc/<pid>/maps + /proc/<pid>/pagemap
    • modificare il valore all'indirizzo fisico con devmeme osserva la reazione del processo dell'utente
  • Memoria di Kernelland
    • allocare la memoria del kernel con kmalloc
    • ottenere l'indirizzo fisico con virt_to_phys e passarlo di nuovo al territorio dell'utente
    • modificare l'indirizzo fisico con devmem
    • interrogare il valore dal modulo del kernel
  • IO mem e dispositivo di piattaforma virtuale QEMU
    • creare un dispositivo con piattaforma con noti indirizzi di registri fisici
    • uso devmem scrivere al registro
    • orologio printfVengono fuori dal dispositivo virtuale in risposta

11
2017-07-16 11:12