简体   繁体   English

从USB设备读取并写入物理地址

[英]Reading from USB device and writing to physical address

I have a USB device that outputs data of size of one byte, and I want to pass these bytes to FPGA component that exists on AXI bridge, FPGA and CPU are on the same chip... it's SoC FPGA Altera Cyclone V. CPU is ARM Cortex-A9. 我有一个USB设备,可以输出一个字节大小的数据,我想将这些字节传递给AXI桥接器上存在的FPGA组件,FPGA和CPU在同一芯片上……这是SoC FPGA Altera CycloneV。 ARM Cortex-A9。 Kernel version 3.7.0. 内核版本3.7.0。

There is a software that reads from the USB device and writes to a dump file... it works just fine. 有一种软件可以从USB设备读取数据并写入转储文件...它可以正常工作。 I tried to use mmap() to map the FPGA address to the virtual space and write to it from the userspace. 我试图使用mmap()将FPGA地址映射到虚拟空间并从用户空间写入它。 When doing so... after say a minute, the kernel seem to crash. 这样做时...一分钟后,内核似乎崩溃了。

I wrote a driver for my FPGA component and I passed the driver path to that software as a file, so that it writes to it, and eventually to my FPGA component, but the same result... kernel crashes again after a random time. 我为我的FPGA组件编写了一个驱动程序,然后将该驱动程序路径作为文件传递给该软件,以便它可以写入该文件,并最终写入我的FPGA组件,但是结果相同……内核在随机时间后再次崩溃。

I also wrote a simple program that reads bytes from a local file and pass it to FPGA... this works fine either ways (using mmap() or driver module), the file passes through to the FPGA with no problems at all no matter how big is the file. 我还编写了一个简单的程序,该程序从本地文件中读取字节并将其传递给FPGA ...无论哪种方式(使用mmap()或驱动程序模块)都可以正常工作,该文件可以毫无问题地传递至FPGA文件有多大。

So the problem is when passing from USB device to FPGA, either using mmap() or a driver module. 因此,问题在于使用mmap()或驱动程序模块将USB设备传递到FPGA时。

Here is a sample crash message: 这是一个示例崩溃消息:

  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 ]---

Other crash messages start like: 其他崩溃消息开始如下:

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

And: 和:

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

Here is the full crash messages . 这是完整的崩溃消息

I noticed that all the crash messages I get intersect with the PC and LR locations, but actually I don't have previous experience with Linux kernel. 我注意到我收到的所有崩溃消息都与PC和LR位置相交,但实际上我以前没有使用Linux内核的经验。 I found similar error messages online but none of the proposed solutions worked for me. 我在网上发现了类似的错误消息,但是没有一个建议的解决方案对我有用。

Source Code: 源代码:

This is function is called whenever a new buffer of bytes arrives from USB: 每当新的字节缓冲区从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;
    }
}

You see I commented fwrite() function (it's used by the original code to write to files) and replaced it with my code that writes to my FPGA component: *fm_receiver_addr = buf[i]; 您会看到我评论了fwrite()函数(原始代码用于写入文件),并将其替换为写入我的FPGA组件的代码: *fm_receiver_addr = buf[i]; . Before that I check the address to see if it's valid and obtain another address if it's not. 在此之前,我检查该地址以查看其是否有效,如果无效,请获取另一个地址。

For the other way, the driver module, I wrote this code: 对于另一种方法,驱动程序模块,我编写了以下代码:

#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's 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 <sirajmuhammad@outlook.com>");
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);

And I revert the userspace code to its original state, and pass the driver path: /sys/bus/platform/drivers/fm-demodulator/fm_demod to the userspace app to write to it. 然后,我将用户空间代码还原为其原始状态,并将驱动程序路径: /sys/bus/platform/drivers/fm-demodulator/fm_demod至用户空间应用以对其进行写入。

Any thought about it? 有什么想法吗?

  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 ]---

No one can probably absolutely know the answer. 没有人可能绝对知道答案。 Note: undefined instruction ! 注意: 未定义的指令

The PC is at scheduler_ipi+0x8/0x4c , this is hardcore ARM-Linux scheduling; PC位于scheduler_ipi + 0x8 / 0x4c ,这是ARM-Linux的核心调度; an inter-processor interrupt. 处理器间中断。 You can disassemble the 'Code:' part to help, 您可以反汇编“代码:”部分以提供帮助,

   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

The crash is at the instruction mcrrmi and this appears to be non-sense. 崩溃发生在mcrrmi指令mcrrmi ,这似乎是mcrrmi If you disassemble sched/core.o you will see the instruction sequence, but I bet that the '4c4c9b50' value is corrupt. 如果您反汇编sched / core.o ,将看到指令序列,但是我敢打赌'4c4c9b50'值已损坏。 Ie, this is not the code the compiler generated. 即,这不是编译器生成的代码。

So the problem is when passing from USB device to FPGA, either using mmap() or a driver module. 因此,问题在于使用mmap()或驱动程序模块将USB设备传递到FPGA时。

I will use a zen move and think a little. 我会用禅宗的动作思考一下。 The USB device use DMA? USB设备使用DMA吗? Your FPGA is probably also some how in control of the ARM/AXI bus. 您的FPGA可能还可以控制ARM / AXI总线。 I would at least consider the possibility that the FPGA is corrupting a bus cycle and perhaps flipping address bits and causing a phycial write to kernel code space. 我至少会考虑FPGA破坏总线周期并可能翻转地址位并导致对内核代码空间进行物理写入的可能性。 This can happen when you use an innocent by-stander like a DMA peripheral. 当您使用无辜的旁观者(例如DMA外设)时,可能会发生这种情况。 The ARM CPU will use cache and burst everything. ARM CPU将使用缓存并突发所有内容。

Things to check, 事情要检查,

  1. The code address in (brackets) is reported as the compiler produced. (括号中)的代码地址报告为编译器生成的结果。 If not, hardware has probably corrupted things. 如果不是,则硬件可能损坏了东西。 It is hard for Linux code to do this as the kernel code pages are typically R/O . Linux代码很难做到这一点,因为内核代码页通常是R / O。
  2. You should also produce disassembler for any code and see what register is in effect. 您还应该为任何代码生成反汇编程序,并查看有效的寄存器。 For instance, the (4c4c9b50) code can be found with, 例如,可以找到(4c4c9b50)代码,

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

You can just objdump vmlinux to find the assembler for the scheduler_ipi routine and then determine what a pointer might be. 您可以仅将objdump vmlinux查找为scheduler_ipi例程的汇编程序,然后确定指针可能是什么。 For instance, if this_rq() was in R9 and R9 is bogus, then you have a clue. 例如,如果this_rq()R9并且R9是伪造的,那么您就有了线索。

If the code is corrupt, you need a bus analyzer and/or some routine to monitor the location and report whenever it changes to try and locate the source of corruption. 如果代码已损坏,则需要总线分析器和/或某个例程来监视位置,并在代码更改时进行报告,以尝试查找损坏源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM