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.
"Bedenken Sie auch, dass, weil PCIe ist Big-Endian" Ich hatte den Eindruck, dass PCI Little-Endian ist – cha5on