4 votes

Comment lire /dev/mem en utilisant read()

J'essaie de lire une adresse physique qui est mappée au périphérique PCIe en utilisant /dev/mem. Le périphérique PCIe est mappé sur 0x387ffa000000 :

bash# lspci -s 1a:00.0 -v | grep Memory
      Memory at 387ffa000000 (64-bit, prefetchable) [size=32M]

J'ai donc écrit un programme basé sur ce que j'ai trouvé ici à SO qui utilise mmap () :

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);

    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);
    if (fd < 0) {
        perror("Can't open");
        return 1;
    }

    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]);
    printf("\n");

    return 0;
}

Bien que cela fonctionne :

bash# ./mem_read.out 0x387ffa000000 0x10
00 1f 00 18 00 05 07 08 00 00 00 00 00 00 00 00

Je voulais essayer la même chose, mais avec lire ()/ écrire () au lieu de mmap().
Mais quand j'ai essayé ça :

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

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

    if (!len) {
        printf("size argument can't be 0\n");
        return 1;
    }

    int fd = open("/dev/mem", O_SYNC);
    if (fd < 0) {
        perror("Can't open");
        return 1;
    }

    if (lseek(fd, offset, SEEK_SET) < 0) {
        perror("Can't seek");
        return 1;
    }

    char* buff = (char*)malloc(len * sizeof(char) + 1);
    if (read(fd, buff, len) < 0) {
        perror("Can't read");
        return 1;
    }

    printf("%s\n", buff);

    return 0;
}

Il échoue avec :

bash# ./mem_read.out 0x387ffa000000 0x10
Can't read: Bad address

Ce même code fonctionne lorsqu'au lieu de /dev/mem J'utilise un fichier texte ordinaire.
Quelqu'un a-t-il une idée de la raison pour laquelle la première fonctionne mais pas la seconde ?

À titre d'information supplémentaire, le message "Mauvaise adresse" est imprimé lorsque errno == EFAULT qui est renvoyé par read() quand :

buf est en dehors de votre espace d'adressage accessible.

ce qui n'a aucun sens pour moi.

1voto

haggai_e Points 2143

En regardant le code de la /dev/mem dispositif, il semble vérifier que l'adresse est une adresse physique valide.

0voto

unwind Points 181987

Votre open() appeler :

int fd = open("/dev/mem", O_SYNC);

C'est ça la page du manuel dice:

L'argument flags doit inclure l'un des accès suivants d'accès suivants : O_RDONLY, O_WRONLY, ou O_RDWR. Ceux-ci demandent l'ouverture du fichier en lecture seule, en écriture seule, ou en lecture/écriture, respectivement.

Je m'attendrais donc à ce qu'il ait des problèmes de lecture, ou même d'ouverture. C'est podría être que O_RDONLY est à valeur 0, je n'ai pas vérifié mais formellement votre code manque le mode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X