2012-06-28 11 views
7

Piszę sterownik urządzenia linuksowego, aby umożliwić FPGA (obecnie podłączony do PC przez PCI express) dane DMA bezpośrednio do pamięci RAM procesora. Musi to nastąpić bez interakcji, a przestrzeń użytkownika musi mieć dostęp do danych. Niektóre szczegóły: - przebiegu 64 bit Fedora 14 - System posiada 8GB pamięci RAM - FPGA (Cyclone IV) na karcie PCIeSterownik urządzenia linuksowego pozwalający FPGA na DMA bezpośrednio na CPU RAM

Chcąc osiągnąć ten Wykonałem następujące: - Reserved górny 2GB pamięci RAM w grobie z memmapem 6GB 2GB (nie uruchomię się, dodaję mem = 2GB). Widzę, że górne 2 GB pamięci RAM jest zarezerwowane w/proc/meminfo - Mapowane BAR0, aby umożliwić odczyt i zapis do rejestrów FPGA (to działa doskonale) - Zaimplementowałem funkcję mmap w moim sterowniku z remap_pfn_range() - Użyj ioremap aby uzyskać adres wirtualny bufora - Dodano wywołania ioctl (do testowania) w celu zapisu danych do bufora - Przetestowano mmap, wykonując wywołanie ioctl, aby zapisać dane do bufora i zweryfikować, że dane znajdowały się w buforze z przestrzeni użytkownika

Problem, przed którym stoję, to moment, w którym układ FPGA zaczyna dane DMA na podany przeze mnie adres bufora. Ciągle dostaję błędy PTE (z DMAR :) lub z poniższym kodem otrzymuję następujący błąd: DMAR: [DMA Write] Request device [01: 00.0] fault addr 186dc5000
DMAR: [przyczyna błędu 01] Obecny bit w wpis korzeń jest jasne DRHD: status przenoszenia winy reg 3

adres w odstępach pierwszych linii przez 0x1000 każdorazowo na podstawie DMA z FPGA

Oto mój init() kod:

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

}

Oto mój kod mmap:

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

Dziękuję za wszelką pomoc można dostarczyć.

Odpowiedz

5

Czy kontrolujesz kod RTL, który sam zapisuje pakiety TLP, czy potrafisz nazwać silnik DMA i PCIe BFM (model funkcyjny magistrali), którego używasz? Jak wyglądają twoje pakiety w symulatorze? Najbardziej przyzwoity BFM powinien przechwycić to, zamiast pozwolić ci go znaleźć po wdrożeniu za pomocą systemu przechwytywania sprzętu PCIe.

Aby skierować do 2 GB pamięci RAM, konieczne będzie wysyłanie adresów 2DW (64-bitowych) z urządzenia. Czy bity w zestawie Fmt/Type to robią? Błędny adres wygląda jak maskowany 32-bitowy adres magistrali, więc coś na tym poziomie jest prawdopodobnie nieprawidłowe. Należy również pamiętać, że ponieważ PCIe jest typu big-endian, należy zachować ostrożność podczas zapisywania docelowych adresów do punktu końcowego urządzenia PCIe. Możliwe, że mniej bajtów adresu docelowego spadnie na ładunek, jeśli Fmt jest niepoprawny - ponownie przyzwoity BFM powinien wykryć wynikowe niedopasowanie długości pakietu.

Jeśli masz najnowszą płytę główną/nowoczesny procesor, punkt końcowy PCIe powinien wykonać PCIe AER (zaawansowane zgłaszanie błędów), więc jeśli używasz najnowszego Centos/RHEL 6.3, powinieneś otrzymać raport o punktach końcowych o wartości dmesg. Jest to bardzo przydatne, ponieważ raport przechwytuje pierwszą garść DW pakietu do specjalnych rejestrów przechwytywania, dzięki czemu można przejrzeć TLP w otrzymanej postaci.

W Twoim sterowniku jądra widzę, że ustawiłeś maskę DMA, co nie jest wystarczające, ponieważ nie zaprogramowałeś mmu, aby umożliwić zapisywanie na stronach urządzenia.Sprawdź implementację pci_alloc_consistent(), aby sprawdzić, do czego jeszcze należy dzwonić, aby to osiągnąć.

+0

"Należy również pamiętać, że ponieważ PCIe jest typu big-endian" miałem wrażenie, że PCI jest mało-endian – cha5on

2

Jeśli nadal szukasz przyczyny, to wygląda to następująco: Twoje jądro ma domyślnie włączone flagi DMA_REMAPPING, dlatego IOMMU rzuca powyższy błąd, ponieważ wpisy kontekstu/domeny IOMMU nie są zaprogramowane dla twojego urządzenia.

Możesz spróbować użyć intel_iommu = off w linii poleceń jądra lub ustawić IOMMU w trybie bypass dla twojego urządzenia. Pozdrowienia, Samir

Powiązane problemy