GNU/Linux >> Tutoriels Linux >  >> Linux

Comment accéder aux adresses physiques depuis l'espace utilisateur sous Linux ?

Vous pouvez mapper un fichier de périphérique sur une mémoire de processus utilisateur à l'aide de mmap(2) appel système. Habituellement, les fichiers de périphérique sont des mappages de mémoire physique sur le système de fichiers. Sinon, vous devez écrire un module de noyau qui crée un tel fichier ou fournit un moyen de mapper la mémoire nécessaire à un processus utilisateur.

Une autre méthode consiste à 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 avoir 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 qui mmaps /dev/mem .

Vous pouvez l'obtenir dans Ubuntu avec :sudo apt-get install busybox

Utilisation :lire 4 octets à partir de l'adresse physique 0x12345678 :

sudo busybox devmem 0x12345678

Ecrire 0x9abcdef0 à cette adresse :

sudo busybox devmem 0x12345678 w 0x9abcdef0

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

mmap MAP_SHARED

Lors du mmapping /dev/mem , vous souhaiterez probablement utiliser :

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

MAP_SHARED permet aux écritures d'aller immédiatement dans la mémoire physique, ce qui facilite l'observation et a 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 normale sur le noyau v4.9, vous devez d'abord :

  • désactiver CONFIG_STRICT_DEVMEM (défini par défaut sur Ubuntu 17.04)
  • passer le nopat option de ligne de commande du noyau pour x86

Les ports IO fonctionnent toujours sans ceux-ci.

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

Vidage du cache

Si vous essayez d'écrire dans la RAM au lieu d'un registre, la mémoire peut être mise en cache par le CPU :Comment vider le cache du CPU pour une région d'espace d'adressage sous Linux ? et je ne vois pas de moyen très portable/facile de le vider ou de marquer la région comme non cache :

  • Comment écrire l'espace mémoire du noyau (adresse physique) dans un fichier en utilisant O_DIRECT ?
  • Comment vider le cache du processeur pour une région d'espace d'adressage sous Linux ?
  • Est-il possible d'allouer, dans l'espace utilisateur, un bloc de mémoire non cacheable sous Linux ?

Alors peut-être /dev/mem ne peut pas être utilisé de manière fiable pour transmettre des mémoires tampons aux appareils ?

Cela ne peut malheureusement pas être observé dans QEMU, puisque QEMU ne simule pas les caches.

Comment le tester

Maintenant, pour la partie amusante. Voici quelques configurations intéressantes :

  • Mémoire Userland
    • allouer volatile variable sur un processus userland
    • obtenir l'adresse physique avec /proc/<pid>/maps + /proc/<pid>/pagemap
    • modifier la valeur à l'adresse physique avec devmem , et regardez le processus userland réagir
  • Mémoire de Kernelland
    • allouer de la mémoire noyau avec kmalloc
    • obtenir l'adresse physique avec virt_to_phys et renvoyez-le à userland
    • modifier l'adresse physique avec devmem
    • interroger la valeur du module du noyau
  • Périphérique de plate-forme virtuelle IO mem et QEMU
    • créer un appareil de plate-forme avec des adresses de registre physiques connues
    • utiliser devmem écrire au registre
    • regarder printf s sortent du périphérique virtuel en réponse

Bonus :déterminer l'adresse physique pour une adresse virtuelle

Existe-t-il une API pour déterminer l'adresse physique à partir de l'adresse virtuelle sous Linux ?


Linux
  1. Comment ajouter ou supprimer un utilisateur d'un groupe sous Linux

  2. Linux - Comment connecter l'utilisateur à Tty à partir de Ssh ?

  3. Comment supprimer un utilisateur d'un groupe sous Linux [Astuce rapide]

  4. Comment accéder (si possible) à l'espace noyau depuis l'espace utilisateur ?

  5. Comment trouver l'utilisation de la mémoire utilisateur sous Linux

Comment autoriser ou refuser l'accès SSH à un utilisateur ou à un groupe particulier sous Linux

Comment extraire des adresses e-mail d'un fichier texte sous Linux

Comment restreindre l'accès SSH à certains utilisateurs sous Linux

Comment changer d'utilisateur sous Linux

Comment changer l'adresse IP sous Linux

Linux - Comment un espace d'adressage virtuel de processus 64 bits est-il divisé sous Linux ?