2012-06-28 6 views
7

Ich schreibe einen Linux-Gerätetreiber, um ein FPGA (derzeit mit dem PC über PCI Express verbunden) zu ermöglichen, DMA-Daten direkt in CPU-RAM. Dies muss ohne Interaktion geschehen und der Benutzer muss Zugriff auf die Daten haben. Einige Details: - Running 64-Bit-Fedora 14 - System hat 8 GB RAM - Der FPGA (Cyclone IV) ist auf einer PCIe-KarteLinux-Gerätetreiber zu einem FPGA zu DMA direkt zu CPU RAM

In einem Versuch, dies zu erreichen, ich führte die folgenden: - Reserviert die oberen 2GB RAM in grub mit memmap 6GB $ 2GB (wird nicht booten ist ich hinzufügen mem = 2GB). Ich kann sehen, dass die oberen 2 GB RAM reserviert ist in/proc/meminfo - Mapped BAR0 zum Lesen und Schreiben in FPGA-Registern (das funktioniert perfekt) - Implementiert eine mmap-Funktion in meinem Treiber mit remap_pfn_range() - Verwenden Sie ioremap um die virtuelle Adresse des Puffers zu erhalten - ioctl-Aufrufe (zum Testen) hinzugefügt, um Daten in den Puffer zu schreiben - Getestet die mmap durch einen ioctl Aufruf zum Schreiben von Daten in den Puffer und verifiziert, dass die Daten im Puffer aus Benutzerraum waren

Das Problem, mit dem ich konfrontiert bin, ist, wenn das FPGA zu DMA-Daten zu der Pufferadresse beginnt, die ich zur Verfügung stelle. Ich bekomme ständig PTE-Fehler (von DMAR :) oder mit dem Code unten bekomme ich den folgenden Fehler: DMAR: [DMA Write] Gerät anfragen [01: 00.0] Fehleraddr 186dc5000 DMAR: [Fehlergrund 01] Vorhandenes Bit in root-Eintrag ist klar DRHD: Umgang mit Fehlerstatus reg 3

die Adresse in der ersten Zeile Schritten von 0x1000 jedes Mal auf der Basis der DMA aus dem FPGA

Hier ist meine init() Code:

#define IMG_BUF_OFFSET  0x180000000UL // Location in RAM (6GB) 
#define IMG_BUF_SIZE  0x80000000UL // Size of the Buffer (2GB) 

#define pci_dma_h(addr) ((addr >> 16) >> 16) 
#define pci_dma_l(addr) (addr & 0xffffffffUL) 

if((pdev = pci_get_device(FPGA_VEN_ID, FPGA_DEV_ID, NULL))) 
{ 
    printk("FPGA Found on the PCIe Bus\n"); 

    // Enable the device 
    if(pci_enable_device(pdev)) 
    { 
     printk("Failed to enable PCI device\n"); 
     return(-1); 
    } 
    // Enable bus master 
    pci_set_master(pdev); 

    pci_read_config_word(pdev, PCI_VENDOR_ID, &id); 
    printk("Vendor id: %x\n", id); 
    pci_read_config_word(pdev, PCI_DEVICE_ID, &id); 
    printk("Device id: %x\n", id); 
    pci_read_config_word(pdev, PCI_STATUS, &id); 
    printk("Device Status: %x\n", id); 
    pci_read_config_dword(pdev, PCI_COMMAND, &temp); 
    printk("Command Register : : %x\n", temp); 
    printk("Resources Allocated :\n"); 
    pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &temp); 
    printk("BAR0 : %x\n", temp); 

    // Get the starting address of BAR0 
    bar0_ptr = (unsigned int*)pcim_iomap(pdev, 0, FPGA_CONFIG_SIZE); 
    if(!bar0_ptr) 
    { 
    printk("Error mapping Bar0\n"); 
    return -1; 
    } 
    printk("Remapped BAR0\n"); 

    // Set DMA Masking 
    if(!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) 
    { 
    pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 
    printk("Device setup for 64bit DMA\n"); 
    } 
    else if(!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) 
    { 
    pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 
    printk("Device setup for 32bit DMA\n"); 
    } 
    else 
    { 
    printk(KERN_WARNING"No suitable DMA available.\n"); 
    return -1; 
    } 

    // Get a pointer to reserved lower RAM in kernel address space (virtual address) 
    virt_addr = ioremap(IMG_BUF_OFFSET, IMG_BUF_SIZE); 
    kernel_image_buffer_ptr = (unsigned char*)virt_addr; 
    memset(kernel_image_buffer_ptr, 0, IMG_BUF_SIZE); 
    printk("Remapped image buffer: 0x%p\n", (void*)virt_addr); 

}

Hier ist mein Mmap Code:

unsigned long image_buffer; 
unsigned int low; 
unsigned int high; 

if(remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 
        vma->vm_end - vma->vm_start, 
        vma->vm_page_prot)) 
{ 
    return(-EAGAIN); 
} 

image_buffer = (vma->vm_pgoff << PAGE_SHIFT); 

if(0 > check_mem_region(IMG_BUF_OFFSET, IMG_BUF_SIZE)) 
{ 
    printk("Failed to check region...memory in use\n"); 
    return -1; 
} 

request_mem_region(IMG_BUF_OFFSET, IMG_BUF_SIZE, DRV_NAME); 

// Get the bus address from the virtual address above 
//dma_page = virt_to_page(addr); 
//dma_offset = ((unsigned long)addr & ~PAGE_MASK); 
//dma_addr = pci_map_page(pdev, dma_page, dma_offset, IMG_BUF_SIZE, PCI_DMA_FROMDEVICE);  
//dma_addr = pci_map_single(pdev, image_buffer, IMG_BUF_SIZE, PCI_DMA_FROMDEVICE); 
//dma_addr = IMG_BUF_OFFSET; 
//printk("DMA Address: 0x%p\n", (void*)dma_addr); 

// Write start or image buffer address to the FPGA 
low = pci_dma_l(image_buffer); 
low &= 0xfffffffc; 
high = pci_dma_h(image_buffer); 
if(high != 0) 
    low |= 0x00000001; 

*(bar0_ptr + (17024/4)) = 0; 

//printk("DMA Address LOW : 0x%x\n", cpu_to_le32(low)); 
//printk("DMA Address HIGH: 0x%x\n", cpu_to_le32(high)); 
*(bar0_ptr + (4096/4)) = cpu_to_le32(low); //2147483649; 
*(bar0_ptr + (4100/4)) = cpu_to_le32(high); 
*(bar0_ptr + (17052/4)) = cpu_to_le32(low & 0xfffffffe);//2147483648; 

printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 4096, *(bar0_ptr + (4096/4))); 
printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 4100, *(bar0_ptr + (4100/4))); 
printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 17052, *(bar0_ptr + (17052/4))); 
return(0); 

Danke für jede Hilfe Sie bieten kann.

Antwort

5

Steuern Sie den RTL-Code, der die TLP-Pakete selbst schreibt, oder können Sie die DMA-Engine und das PCIe BFM (Bus-Funktionsmodell) benennen, das Sie verwenden? Wie sehen Ihre Pakete im Simulator aus? Am ehesten sollte BFM dies auffassen, anstatt es nach der Bereitstellung mit einem PCIe-Hardware-Erfassungssystem zu finden.

Um die oberen 2 GB RAM anzusprechen, müssen Sie 2DW (64-Bit) -Adressen vom Gerät senden. Sind die Bits in Ihrem Fmt/Type dafür eingestellt? Die fehlerhafte Adresse sieht wie eine maskierte 32-Bit-Busadresse aus, daher ist etwas auf dieser Ebene wahrscheinlich falsch. Bedenken Sie auch, dass, da PCIe Big-Endian ist, beim Schreiben der Zieladressen auf den PCIe-Geräteendpunkt Vorsicht geboten ist. Sie könnten die unteren Bytes der Zieladresse in die Payload fallen lassen, wenn Fmt falsch ist - wieder sollte ein anständiger BFM die resultierende Paketlängenabweichung erkennen.

Wenn Sie ein aktuelles Motherboard/moderne CPU haben, sollte der PCIe-Endpunkt PCIe AER (erweiterte Fehlerberichterstattung) ausführen. Wenn Sie also ein Centos/RHEL 6.3 ausführen, sollten Sie einen dmesg Bericht über Endpunktfehler erhalten. Dies ist sehr nützlich, da der Bericht die erste Handvoll DWs des Pakets in speziellen Erfassungsregistern erfasst, so dass Sie das TLP wie erhalten betrachten können.

In Ihrem Kernel-Treiber, sehe ich Sie die DMA-Maske einrichten, das ist nicht ausreichend, da Sie die mmu nicht programmiert haben, um Schreibvorgänge auf den Seiten vom Gerät zu ermöglichen.Sehen Sie sich die Implementierung von pci_alloc_consistent() an, um zu sehen, was Sie sonst noch anrufen sollten, um dies zu erreichen.

+0

"Bedenken Sie auch, dass, weil PCIe ist Big-Endian" Ich hatte den Eindruck, dass PCI Little-Endian ist – cha5on

2

Wenn Sie immer noch nach einem Grund suchen, dann geht es so: Ihr Kernel hat DMA_REMAPPING Flags standardmäßig aktiviert, daher wirft IOMMU den obigen Fehler, da IOMMU Kontext/Domain Einträge für Ihr Gerät nicht programmiert sind.

Sie können versuchen, intel_iommu = off in der Kernel-Befehlszeile zu verwenden oder IOMMU in den Bypass-Modus für Ihr Gerät zu versetzen. Grüße, Samir