Чтение с USB-устройства и запись на физический адрес

У меня есть USB-устройство, которое выводит данные размером одного байта, и я хочу передать эти байты в компонент FPGA, который существует на мосте AXI, FPGA и CPU находятся на одном чипе... это SoC FPGA Altera Cyclone V CPU - ARM Cortex-A9. Версия ядра 3.7.0.

Существует программное обеспечение, которое считывает с устройства USB и записывает в файл дампа... он работает отлично. Я попытался использовать mmap() для сопоставления адреса FPGA с виртуальным пространством и записи в него из пользовательского пространства. При этом... после минутной минуты ядро, похоже, сбой.

Я написал драйвер для моего компонента FPGA, и я передал путь драйвера к этому программному обеспечению в виде файла, чтобы он записывал его, и в конечном итоге к моему компоненту FPGA, но тот же результат... Ядро снова сбой после случайное время.

Я также написал простую программу, которая считывает байты из локального файла и передает его в FPGA... это отлично работает в любом случае (с использованием mmap() или модуля драйвера), файл переходит к FPGA без проблем при все независимо от размера файла.

Таким образом, проблема заключается в переходе с USB-устройства на FPGA с использованием mmap() или модуля драйвера.

Вот пример аварийного сообщения:

  Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
  Modules linked in: ipv6
  CPU: 1    Not tainted  (3.7.0 #106)
  PC is at scheduler_ipi+0x8/0x4c
  LR is at handle_IPI+0x10c/0x19c
  pc : [<800521a0>]    lr : [<800140d4>]    psr: 80000193
  sp : bf87ff58  ip : 8056acc8  fp : 00000000
  r10: 00000000  r9 : 413fc090  r8 : 00000001
  r7 : 00000000  r6 : bf87e000  r5 : 80535018  r4 : 8053eec0
  r3 : 8056ac80  r2 : bf87ff58  r1 : 00000482  r0 : 00000481
  Flags: Nzcv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
  Control: 10c5387d  Table: 3f0c404a  DAC: 00000015
  Process swapper/1 (pid: 0, stack limit = 0xbf87e240)
  Stack: (0xbf87ff58 to 0xbf880000)
  ff40:                                                       00000000 800140d4
  ff60: fffec10c 8053e418 bf87ff90 fffec100 8000f6e0 8000851c 8000f708 8000f70c
  ff80: 60000013 ffffffff bf87ffc4 8000e180 00000000 00000000 00000001 00000000
  ffa0: bf87e000 80565688 803ddfb0 80541fc8 8000f6e0 413fc090 00000000 00000000
  ffc0: 8053e9b8 bf87ffd8 8000f708 8000f70c 60000013 ffffffff 00000020 8000f894
  ffe0: 3f86c06a 00000015 10c0387d 805658d8 0000406a 003d1ee8 31ca2085 5c1021c3
  Code: eaffffad 80564700 e92d4800 e1a0200d (4c4c9b50)
  ---[ end trace 9e492cde975c41f9 ]---

Другие сообщения о сбоях начинаются следующим образом:

  Unable to handle kernel paging request at virtual address 2a7a4390
  Internal error: Oops - bad syscall: ebcffb [#1] SMP ARM
  pgd = bf318000
  [2a7a4390] *pgd=00000000

и

 Internal error: Oops - undefined instruction: 0 [#2] SMP ARM
 Modules linked in: ipv6
 CPU: 1    Tainted: G      D       (3.7.0 #106)

Вот полный аварийный сигнал.

Я заметил, что все сообщения о сбоях, которые я получаю, пересекаются с местоположениями ПК и LR, но на самом деле у меня нет предыдущего опыта работы с ядром Linux. Я нашел похожие сообщения об ошибках в Интернете, но ни один из предлагаемых решений не работал у меня.

Исходный код:

Эта функция вызывается всякий раз, когда новый буфер байтов поступает с USB:

static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
{
    if (ctx) {
        if (do_exit)
            return;

        if ((bytes_to_read > 0) && (bytes_to_read < len)) {
            len = bytes_to_read;
            do_exit = 1;
            rtlsdr_cancel_async(dev);
        }

/*      if (fwrite(buf, 1, len, (FILE*)ctx) != len) {
            fprintf(stderr, "Short write, samples lost, exiting!\n");
            rtlsdr_cancel_async(dev);
        }
*/
        if (fm_receiver_addr == NULL)
        {
            virtual_base = mmap(NULL, HPS2FPGA_SPAN, PROT_WRITE, MAP_PRIVATE, fd, HPS2FPGA_BASE);
            if (virtual_base == MAP_FAILED)
            {
                perror("mmap");
                close(fd);
                exit(1);
            }

            fm_receiver_addr = (unsigned char*)(virtual_base + FM_DEMOD_OFFSET);
        }

        int i, j;
        for (i = 0; i < len; i++)
        {
            *fm_receiver_addr = buf[i];
            for (j = 0; j < 150; j++);
        }

        if (bytes_to_read > 0)
            bytes_to_read -= len;
    }
}

Вы видите, что я прокомментировал функцию fwrite() (она использовалась исходным кодом для записи в файлы) и заменила его моим кодом, который записывается в мой компонент FPGA: *fm_receiver_addr = buf[i];. До этого я проверяю адрес, чтобы узнать, действительно ли он и получил другой адрес, если он не был.

Другим способом, модулем драйвера, я написал этот код:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/io.h>

#define HPS2FPGA_BASE       0xC0000000
#define HPS2FPGA_SPAN       PAGE_SIZE

void* fm_demod_addr;
int i;

// Get a driver entry in Sysfs
static struct device_driver fm_demod_driver = 
{
    .name = "fm-demodulator",   // Name of the driver
    .bus = &platform_bus_type,  // Which bus does the device exist
};

// Function that is used when we read from the file in /sys, but we won't use it
ssize_t fm_demod_read(struct device_driver* drv, char* buf)
{ return 0; }

// Function that is called when we write to the file in /sys
ssize_t fm_demod_write_sample(struct device_driver* drv, const char* buf, size_t count)
{
    if (buf == NULL)
    {
        pr_err("Error! String must not be NULL!\n");
        return -EINVAL;
    }

    for (i = 0; i < count; i++)
    {
        iowrite8(buf[i], fm_demod_addr);
    }

    return count;
}

// Set our module pointers and set permissions mode
static DRIVER_ATTR(fm_demod, S_IWUSR, fm_demod_read, fm_demod_write_sample);

// Set module information
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Siraj Muhammad <[email protected]>");
MODULE_DESCRIPTION("Driver for FPGA component 'FM Demodulator'");

static int __init fm_demod_init(void)
{
    int ret;
    struct resource* res;

    // Register driver in kernel
    ret = driver_register(&fm_demod_driver);
    if (ret < 0)
        return ret;

    // Create file system in /sys
    ret = driver_create_file(&fm_demod_driver, &driver_attr_fm_demod);
    if (ret < 0)
    {
        driver_unregister(&fm_demod_driver);
        return ret;
    }

    // Request exclusive access to the memory region we want to write to
    res = request_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN, "fm-demodulator");
    if (res == NULL)
    {
        driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
        driver_unregister(&fm_demod_driver);
        return -EBUSY;
    }

    // Map the address into virtual memory
    fm_demod_addr = ioremap(HPS2FPGA_BASE, HPS2FPGA_SPAN);
    if (fm_demod_addr == NULL)
    {
        driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
        driver_unregister(&fm_demod_driver);
        release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
        return -EFAULT;
    }

    return 0;
}

static void __exit fm_demod_exit(void)
{
    // Remove file system from /sys
    driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
    // Unregister the driver
    driver_unregister(&fm_demod_driver);
    // Release requested memory
    release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
    // Un-map address
    iounmap(fm_demod_addr);

}

module_init(fm_demod_init);
module_exit(fm_demod_exit);

И я вернул код пользовательского пространства в исходное состояние и передал путь к драйверу: /sys/bus/platform/drivers/fm-demodulator/fm_demod в приложение пользовательского пространства, чтобы написать ему.

Любая мысль об этом?

Ответы

Ответ 1

  Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
  PC is at scheduler_ipi+0x8/0x4c
  LR is at handle_IPI+0x10c/0x19c
  pc : [<800521a0>]    lr : [<800140d4>]    psr: 80000193
  [snip]
  Code: eaffffad 80564700 e92d4800 e1a0200d (4c4c9b50)
  ---[ end trace 9e492cde975c41f9 ]---

Никто, вероятно, не может точно знать ответ. Примечание: undefined инструкция!

ПК находится в scheduler_ipi + 0x8/0x4c, это жесткое планирование ARM-Linux; межпроцессорное прерывание. Вы можете разобрать часть "Код:", чтобы помочь,

   0:   eaffffad        b       0xfffffebc
   4:   80564700        subshi  r4, r6, r0, lsl #14
   8:   e92d4800        push    {fp, lr}
   c:   e1a0200d        mov     r2, sp
  10:   4c4c9b50        mcrrmi  11, 5, r9, ip, cr0

Сбой происходит по команде mcrrmi, и это кажется нечувствительным. Если вы разобьете sched/core.o, вы увидите последовательность команд, но я уверен, что значение "4c4c9b50" повреждено. То есть, это не код, сгенерированный компилятором.

Таким образом, проблема заключается в переходе с USB-устройства на FPGA, используя mmap() или модуль драйвера.

Я буду использовать движение дзэн и немного подумаю. Устройство USB использует DMA? Вероятно, ваша ПЛИС также может контролировать управление шиной ARM/AXI. Я бы по крайней мере рассмотрел возможность того, что FPGA повреждает цикл шины и, возможно, переворачивает адресные биты и вызывает физическую запись в кодовое пространство ядра. Это может произойти, если вы используете невиновную стороннюю систему, такую ​​как периферийное устройство DMA. Процессор ARM будет использовать кеш и вспахивать все.

Что нужно проверить,

  • Адрес кода в (скобки) сообщается как созданный компилятор. Если нет, аппаратное обеспечение, вероятно, испортило ситуацию. Коду Linux это сложно сделать, поскольку страницы кода ядра обычно R/O.
  • Вы также должны создать дизассемблер для любого кода и посмотреть, какой регистр действует. Например, код (4c4c9b50) можно найти с помощью

    printf '\ x50\x9b\x4c\x4c' > arm.bin
     objdump -marm -b binary -D arm.bin

Вы можете просто objdump vmlinux найти ассемблер для процедуры scheduler_ipi, а затем определить, какой может быть указатель. Например, если this_rq() находилось в R9 и R9 является фиктивным, тогда у вас есть ключ.

Если код поврежден, вам нужен анализатор шины и/или какая-то рутина, чтобы отслеживать местоположение и сообщать о каждом изменении, чтобы попытаться найти источник повреждения.