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?

21

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;
}
29
répondu Dmytro Sirenko 2015-06-15 16:33:34

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.

Voir aussi: mmap de /dev/mem échoue avec non valide argument pour l'adresse virt_to_phys, mais l'adresse est la page alignée

"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:

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
  • 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
  • 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
12