2016-10-18 13 views
3

Als ersten Level-Test meines PCI-Treibers hoffte ich, dass ich über die /sys/bus/pci/devices/0000:01:00.0/ Zugriff auf die Region pci_iomap erhalten konnte resource0 Datei von meiner Benutzeranwendung. Die Manpage für mmap, das Beispielprogramm, das ich gefunden habe, und andere Posts scheinen anzuzeigen, dass der Benutzerprozesszugriff funktionieren sollte. Einige Artikel scheinen jedoch darauf hinzuweisen, dass der Aufruf von mmap über einen ioctl-Accessor vom Kernel aus erfolgen muss.linux mmap Zugriff auf PCI-Speicherbereich von User-Space-Anwendung

Meine Frage ist, sollte mmap() der PCI sysfs Ressourcendatei von Anwendungsraum funktionieren?

Wenn ich meinen Code mmap zurückgibt, was aussieht wie eine gültige Adresse, aber ich bekomme einen Busfehler, wenn ich versuche, auf die virtuelle Adresse zuzugreifen. Ich glaube mein Endgerät eine PCI zu Xilinx AXI Brücke, die auf dem FPGA funktioniert ok, da ich R/W über ein Windows-PCIe-Dienstprogramm (Win Driver) Ich bin auf einem NXP LS1021A ARM7-Prozessor mit Linux ver 3.12 .37.

Dank Bill

Nicht, dass ich jemand meinen Code debuggen wollen, aber was ich tue am besten durch den Code erklärt werden kann, so habe ich es auch eingeschlossen. Ich entschuldige mich, wenn der eingefügte Code nicht korrekt angezeigt wird. Hoffentlich tut es das.

ich den Code unten laufen und root @ ls1021aiot erhalten: ~ # pcimem /sys/bus/pci/devices/0000:01:00.0/resource0 0 w

/sys/bus/pci/devices/0000: 01: 00.0/Ressource0 geöffnet. Ziel-Offset ist 0x0, Seitengröße ist 4096 Kartenmaske ist 0xFFF mmap (0, 4096, 0x3, 0x1, 3, 0x0) mmap (0, 4096, 0x3, 0x1, 3, 0x0) PCI-Speicher zugeordnet 4096 Byte Region zu map_base 0x76fb5000. PCI-Speicherkartenzugriff 0x 76FB5000. Busfehler

/* 
* pcimem.c: Simple program to read/write from/to a pci device from userspace. 
* 
* Copyright (C) 2010, Bill Farrow ([email protected]) 
* 
* Based on the devmem2.c code 
* Copyright (C) 2000, Jan-Derk Bakker ([email protected]) 
* 
* This program is free software; you can redistribute it and/or modify 
* it under the terms of the GNU General Public License as published by 
* the Free Software Foundation; either version 2 of the License, or 
* (at your option) any later version. 
* 
* This program is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
* GNU General Public License for more details. 
* 
* You should have received a copy of the GNU General Public License 
* along with this program; if not, write to the Free Software 
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
* 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <signal.h> 
#include <fcntl.h> 
#include <ctype.h> 
#include <termios.h> 
#include <sys/types.h> 
#include <sys/mman.h> 
#include <linux/pci.h> 


#define PRINT_ERROR \ 
    do { \ 
     fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \ 
     __LINE__, __FILE__, errno, strerror(errno)); exit(1); \ 
    } while(0) 

#define MAP_SIZE 4096UL 
#define MAP_MASK (MAP_SIZE - 1) 

int main(int argc, char **argv) { 
    int fd; 
    void *map_base, *virt_addr; 
    uint32_t read_result, writeval; 
    char *filename; 
    off_t target; 
    int access_type = 'w'; 

    if(argc < 3) { 
     // pcimem /sys/bus/pci/devices/0001\:00\:07.0/resource0 0x100 w 0x00 
     // argv[0] [1]           [2] [3] [4] 
     fprintf(stderr, "\nUsage:\t%s { sys file } { offset } [ type [ data ] ]\n" 
       "\tsys file: sysfs file for the pci resource to act on\n" 
       "\toffset : offset into pci memory region to act upon\n" 
       "\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n" 
       "\tdata : data to be written\n\n", 
       argv[0]); 
     exit(1); 
    } 
    filename = argv[1]; 
    target = strtoul(argv[2], 0, 0); 

    if(argc > 3) 
     access_type = tolower(argv[3][0]); 

    if((fd = open(filename, O_RDWR | O_SYNC)) == -1){ 
     PRINT_ERROR; 
    } 
    printf("%s opened.\n", filename); 
    printf("Target offset is 0x%x, page size is %ld map mask is 0x%lX\n", (int) target, sysconf(_SC_PAGE_SIZE), MAP_MASK); 
    fflush(stdout); 

    /* Map one page */ 
#if 0 
    //map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) (target & ~MAP_MASK)); 
    //map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK); 
#endif 
    printf("mmap(%d, %ld, 0x%x, 0x%x, %d, 0x%x)\n", 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (int) (target & ~MAP_MASK)); 
    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (target & ~MAP_MASK)); 
    if(map_base == (void *) -1){ 
     printf("PCI Memory mapped ERROR.\n"); 
     PRINT_ERROR; 
     close(fd); 
     return 1; 
    } 

    printf("mmap(%d, %ld, 0x%x, 0x%x, %d, 0x%x)\n", 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (int) (target & ~MAP_MASK)); 
    printf("PCI Memory mapped %ld byte region to map_base 0x%08lx.\n", MAP_SIZE, (unsigned long) map_base); 
    fflush(stdout); 

    virt_addr = map_base + (target & MAP_MASK); 
    printf("PCI Memory mapped access 0x %08X.\n", (uint32_t) virt_addr); 
    switch(access_type) { 
     case 'b': 
       read_result = *((uint8_t *) virt_addr); 
       break; 
     case 'h': 
       read_result = *((uint16_t *) virt_addr); 
       break; 
     case 'w': 
       read_result = *((uint32_t *) virt_addr); 
         printf("READ Value at offset 0x%X (%p): 0x%X\n", (int) target, virt_addr, read_result); 
       break; 
     default: 
       fprintf(stderr, "Illegal data type '%c'.\n", access_type); 
       exit(2); 
    } 
    fflush(stdout); 

    if(argc > 4) { 
     writeval = strtoul(argv[4], 0, 0); 
     switch(access_type) { 
       case 'b': 
         *((uint8_t *) virt_addr) = writeval; 
         read_result = *((uint8_t *) virt_addr); 
         break; 
       case 'h': 
         *((uint16_t *) virt_addr) = writeval; 
         read_result = *((uint16_t *) virt_addr); 
         break; 
       case 'w': 
         *((uint32_t *) virt_addr) = writeval; 
         read_result = *((uint32_t *) virt_addr); 
         break; 
     } 
     printf("Written 0x%X; readback 0x%X\n", writeval, read_result); 
     fflush(stdout); 
    } 

    if(munmap(map_base, MAP_SIZE) == -1) { PRINT_ERROR;} 
    close(fd); 
    return 0; 
} 
+0

Ich kompilierte meine Treiber und die pci_debug App für x86_64 (Linux 3.16.7) und sie funktionierten korrekt. Dies lässt mich glauben, dass ich etwas vermisse, was für den 32-Bit-Arm-Prozessor (40-Bit-interne Adressierung) erforderlich ist. Das PCI-Gerät ist in der Adressbasis 0x40_0000_0000 abgebildet. cat/proc/cpuinfo zeigt die lpae-Funktion an. –

+0

Ich vergaß zu erwähnen, die pci_debug App für den ARM-Prozessor erfüllt Busfehler wie meine Test-App tat. Die mmap gibt void * zurück, zu dem ich einen Offset hinzufüge, ist dies in LPAE erlaubt? Ich werde weiter graben. Danke –

Antwort

2

Sie einen Blick auf pci_debug Programmcode verfügbar here nehmen. Wenn Sie einen Busfehler haben, ist es vielleicht ein FPGA-Design-Problem. Ist Ihr IP-AXI-Bus mit 32 Bit/16 Bit/8 Bit kompatibel? Achten Sie darauf, auf den Speicher mit der richtigen Adresse zuzugreifen (wenn die 32-Bit-Adresse durch 4 teilbar ist, wenn 16 Bit durch 2).

+0

Danke Fabien. Dies ist ein nützliches Werkzeug. Ich habe den Bus-Fehler gesehen, während ich ihn wie meine Test-App benutzt habe. Ich werde meinen Treiber und die Test-App neu aufbauen und versuchen, die PCI-Karte an ein Intel-System anzuschließen. Wenn ich den Busfehler erhalte, denke ich nicht, dass TLP-Pakete tatsächlich auf dem PCI-Bus gesendet werden. Wir hatten einen PCI-Analyzer an einem Punkt. In meinen Augen würde dies den AXI IP-Core entlasten. Würdest du zustimmen? –

+0

Immer noch keine Lösung dafür gefunden. Ich habe am Ende nur einen IOCT-Treiber geschrieben und die Lese- und Schreibroutinen im Kernelraum verwendet, um auf die pci_iomap() zuzugreifen. –

2

Immer noch keine Lösung für diese auf dem NXP LS1021A-Prozessor gefunden, aber ich habe auf einer x86_64-Maschine getestet und dort arbeitete die mmap auf dem gleichen PCIe Edge-Ziel. Der NXP ARMv7-Prozessor, den ich habe, ist ein interner 40-Bit-Adressraum, ist aber ein 32-Bit-Prozessor. Dies muss den virtuellen Adresstabellen einen Sonderfall bieten, der nicht korrekt behandelt wird. Die 2 PCIe-Root-Geräte werden in den höheren Adressraum abgebildet. Ich habe am Ende nur einen IOCTL-Treiber geschrieben und die Lese- und Schreibroutinen im Kernel-Bereich verwendet, um auf den Zeiger pci_iomap() zuzugreifen, den ich im Cache gespeichert habe, als das Gerät getestet wurde. NXP bot einen Vorschlag, den ich unten zitieren, aber ich habe immer noch den gleichen Busfehler. Wenn sie es jemals lösen, werde ich aktualisieren.

"In Bezug auf den mmap. Hier ist eine Antwort von dem Software-Team. Verwendung mmap64() 40bit phy_address auf ein 32 abzubilden virt_addr oder mit mmap fortsetzen, aber hinzufügen‚-D_FILE_OFFSET_BITS = 64‘beim Kompilieren."

UPDATE. Ich habe eine neue Version der Toolchain von NXP erhalten und mmap64 war darin verfügbar. Ich nehme an, dass die mmap64-Unterstützung nur auf 32-Bit-Systemen verwendet werden kann, wenn Sie auf Dateien zugreifen und größere> 4 GB-Dateien ausgleichen möchten. Durch Ersetzen der Aufrufe von mmap durch mmap64 laufen die Tests, die ich verwendet habe, jetzt korrekt.