Comment accéder aux adresses physiques depuis L'espace utilisateur de Linux?
sur un système ARM fonctionnant sous Linux, j'ai un périphérique dont la mémoire est mappée à une adresse physique. À partir d'un programme d'espace utilisateur où toutes les adresses sont virtuelles, Comment puis-je lire le contenu à partir de cette adresse?
2 réponses
vous pouvez associer un fichier de périphérique à une mémoire de processus Utilisateur en utilisant mmap(2)
appel système. Habituellement, les fichiers de périphérique sont des correspondances de la mémoire physique au système de fichiers.
Sinon, vous devez écrire un module du noyau qui crée un fichier ou fournit un moyen de mapper la mémoire nécessaire à un processus utilisateur.
une autre façon est de remapper des parties de /dev/mem vers une mémoire utilisateur.
Edit: Exemple de mmaping / dev / mem (ce programme doit avoir accès à /dev/mem, par exemple, les droits 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
est un petit utilitaire CLI que mmaps /dev/mem
.
vous pouvez l'obtenir à Ubuntu avec: sudo apt-get install busybox
Usage: lire les 4 octets de l'adresse physique 0x12345678
:
sudo busybox devmem 0x12345678
écrivez 0x9abcdef0
à cette adresse:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Source: https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
MAP_SHARED
Quand mmapping /dev/mem
, il est probable que vous souhaitez utiliser:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
rend l'écriture aller à la mémoire physique immédiatement, ce qui le rend plus facile à observer, et fait plus de sens pour les Écritures de registre matériel.
CONFIG_STRICT_DEVMEM
et nopat
pour utiliser /dev/mem
pour afficher et modifier la RAM régulière sur le noyau v4.9, Vous devez poing:
- disable
CONFIG_STRICT_DEVMEM
(défini par défaut sur Ubuntu 17.04) - passez l'option
nopat
de la ligne de commande du noyau pour x86
les ports IO fonctionnent encore sans ceux-ci.
"1519280920 Cache" rinçage
si vous essayez D'écrire en RAM au lieu d'un registre, la mémoire peut être mise en cache par le CPU: comment vider le cache CPU pour une région d'adresse sous Linux? et je ne vois pas très portable / moyen facile de tirer la chasse ou de la marque de la région cache:
- comment vider le cache CPU pour une région d'adresse sous Linux?
- est-il possible d'allouer, dans l'espace utilisateur, un bloc de mémoire non cachable sur Linux?
alors peut-être que /dev/mem
ne peut pas être utilisé de manière fiable pour passer des tampons de mémoire à des appareils?
cela ne peut malheureusement pas être observé dans QEMU, car QEMU ne simule pas les caches.
comment le tester
maintenant pour la partie amusante. Voici quelques configurations fraîches:
- Userland mémoire
- allouer
volatile
variable sur un userland processus de - obtenir l'adresse physique avec
/proc/<pid>/maps
+/proc/<pid>/pagemap
- modifier la valeur à l'adresse physique avec
devmem
, et de regarder la userland processus réagir
- allouer
- Kernelland mémoire
- allouer la mémoire du noyau avec
kmalloc
- obtenir l'adresse physique avec
virt_to_phys
et passer à userland - modifier l'adresse physique avec
devmem
- interroger la valeur du module du noyau
- allouer la mémoire du noyau avec
- IO mem et QEMU plate-forme virtuelle de l'appareil
- créer un dispositif de plate-forme avec des adresses d'enregistrement physiques connues
- utiliser
devmem
pour écrire au registre - regarder
printf
s de sortir du périphérique virtuel en réponse