Puoi mappare un file di dispositivo a una memoria di processo utente usando mmap(2)
chiamata di sistema. Di solito, i file di dispositivo sono mappature della memoria fisica al file system. Altrimenti, devi scrivere un modulo del kernel che crei tale file o fornisca un modo per mappare la memoria necessaria a un processo utente.
Un altro modo è rimappare parti di /dev/mem in una memoria utente.
Modifica: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;
}
busybox devmem
busybox devmem
è una piccola utility CLI che mmaps /dev/mem
.
Puoi ottenerlo in Ubuntu con:sudo apt-get install busybox
Utilizzo:legge 4 byte dall'indirizzo fisico 0x12345678
:
sudo busybox devmem 0x12345678
Scrivi 0x9abcdef0
a quell'indirizzo:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Fonte:https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
mmap MAP_SHARED
Durante la mappatura di /dev/mem
, probabilmente vorrai usare:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
fa sì che le scritture vadano immediatamente nella memoria fisica, il che rende più facile l'osservazione e ha più senso per le scritture del registro hardware.
CONFIG_STRICT_DEVMEM
e nopat
Per usare /dev/mem
per visualizzare e modificare la normale RAM sul kernel v4.9, devi pugno:
- disattiva
CONFIG_STRICT_DEVMEM
(impostato per impostazione predefinita su Ubuntu 17.04) - passa il
nopat
opzione della riga di comando del kernel per x86
Le porte IO funzionano ancora senza quelle.
Vedi anche:mmap di /dev/mem fallisce con argomento non valido per l'indirizzo virt_to_phys, ma l'indirizzo è allineato alla pagina
Svuotamento della cache
Se provi a scrivere sulla RAM anziché su un registro, la memoria potrebbe essere memorizzata nella cache dalla CPU:come svuotare la cache della CPU per una regione dello spazio degli indirizzi in Linux? e non vedo un modo molto portatile/semplice per scaricarlo o contrassegnare la regione come non memorizzabile nella cache:
- Come scrivere la memoria dello spazio del kernel (indirizzo fisico) in un file usando O_DIRECT?
- Come svuotare la cache della CPU per una regione dello spazio degli indirizzi in Linux?
- È possibile allocare, nello spazio utente, un blocco di memoria non memorizzabile nella cache su Linux?
Quindi forse /dev/mem
non può essere utilizzato in modo affidabile per passare i buffer di memoria ai dispositivi?
Questo purtroppo non può essere osservato in QEMU, poiché QEMU non simula le cache.
Come provarlo
Adesso per la parte divertente. Ecco alcune fantastiche configurazioni:
- Memoria utente
- allocare
volatile
variabile su un processo userland - ottieni l'indirizzo fisico con
/proc/<pid>/maps
+/proc/<pid>/pagemap
- modificare il valore all'indirizzo fisico con
devmem
e osserva la reazione del processo userland
- allocare
- Memoria del kernel
- allocare la memoria del kernel con
kmalloc
- ottieni l'indirizzo fisico con
virt_to_phys
e passalo di nuovo a userland - modifica l'indirizzo fisico con
devmem
- interroga il valore dal modulo del kernel
- allocare la memoria del kernel con
- IO mem e dispositivo della piattaforma virtuale QEMU
- creare un dispositivo di piattaforma con indirizzi di registro fisici noti
- usa
devmem
scrivere al registro - guarda
printf
escono dal dispositivo virtuale in risposta
Bonus:determina l'indirizzo fisico per un indirizzo virtuale
Esiste qualche API per determinare l'indirizzo fisico dall'indirizzo virtuale in Linux?