This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] AM625:通过/dev/mem 共享内存时的 SIGBUS

Guru**** 2487425 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1438071/am625-sigbus-when-memset-shared-memory-through-dev-mem

器件型号:AM625

工具与软件:

假设我按如下方式创建了一个存储器映射:

inf mem_file = open("/dev/mem", O_RDWR | O_SYNC );
// choose some physical address that is reserved and safe to use
void * vaddr = mmap(0, 64*1024, PROT_READ | PROT_WRITE, MAP_SHARED, mem_file, 0x91000000);

在我的 AM62系统上、如果我 memset () vaddr、我的应用程序将被抛出 SIGBUS。 但是、对于循环和基于指针的访问、请使用带规则的 vaddr、没有此类错误。

虽然我可以使用基于对齐指针的访问与映射的存储器进行交互、但尝试将该区域设置成内存会触发一个 SIGBUS。

起初,我以为这可能是一个`memset ()`问题,但这并没有太大的意义,因为到现在,这将出现在其他用例。 另外、如果我尝试此操作:

char some_mem[64*1024];
memset(some_mem,0,64*1024);

正如预期的那样、它运行正常。

所以,我的直觉是,在内核层面的某个东西,在"/dev/mem "下面是导致一个对齐错误或触发了 SIGBUS 的东西。

如果上述少量信息不足以揭示问题、请告知我、我可以创建一个重现问题的最低示例。 唯一需要注意的是、它可能需要 devicetree tweak 来保留一些存储器、以便可以安全地执行测试。

此致、

António μ A

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Antonio:

    我无法解释 SIGBUS 错误、因为我从不直接从 Linux 用户空间操作 DDR、这在 Linux 中不是正常的操作方式。

    我的第一个问题是、由于您直接在0x91000000处映射64KB、因此您是否在内核 devicetre 中保留了此 DDR 区域?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    刘斌、您好!  

    感谢您的快速跳转。

    在上面的代码段中、我刚才提到了一些内容。 在实际应用中、实际上会通过 devicetree 保留它。

    我们的应用使用两个映射的存储器区域:PRU0的数据 RAM 和 DDR 的一个1MiB 块。  

    根据我的收集、这个 SIGBUS 在尝试使用 memset 填充这些区域时、将发生这种情况、而无论其是 DDR 还是外设存储器。

    此外、 简言之、只要访问对齐、使用指针访问填充这些存储器区域都可行、无论数据类型如何(1、2、4或8字节宽)。

    构建为在 BeagleBone Black 上运行的相同代码(可以调整地址)非常有效。

    此致!

    António μ A

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我想您会得到该存储器的严格排序(ARM64上的 nGnRnE)映射、因此它不是"正常"存储器、而是类似于器件寄存器(来自处理器的 POE)。 根据 memset 使用的指令(可能还取决于大小和对齐方式)、生成的访问不适合此类映射。

    我想将 memset 或 memcpy 之类的内存与这种"特殊"内存一起使用是错误的。

    问题是这个区域需要什么样的存储器映射、以及如何实现。

    此致、

    Dominic

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    谢谢,指出了那种内存映射,我们可以处理, Dominic。

    从你的话和非常快速的阅读在网站上,它似乎似乎有一个情景,这种类型的内存映射,与错误类型的指令,由 memset ,可能会运行到该 SIGBUS。

    不过、我不是这个主题的专家、我想了解是否可能要求一个不会有此类限制的存储器映射。

    应用程序从本质上将这些内存区域视为与 PRU 通信的缓冲区。 访问通常是逐成员执行的、成员通常是整数、最多为64位宽。 是否可以将适当的标志传递给 mmap() -或 open ("/dev/mem ")-以便建立一种更轻松的映射?

    此致、

    António μ A

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Antonio:

    我也不是此主题的专家、但我想知道您是否可以访问内核空间中的这一保留存储器、或者是否仍然发生这种总线错误。 我认为通过/dev/mem 直接访问用户空间中的内存有一定的限制。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    当与 PRU 通信时、您可能需要未缓存、否则您需要手动缓存维护。  

    我想这就是映射决定的地方:

    https://elixir.bootlin.com/linux/v6.11.6/source/arch/arm64/mm/mmu.c#L98

    如果存储器未映射(不确定具体需要什么)、您将获得 nGnRnE。 如果是、但在 O_SYNC 中传递时、您会得到未缓存、否则您会获得正常的存储器。 至少对于 PRU 数据存储器而言、您不会获得必要的映射。 对于 DDR 缓冲器、我想问题是它是保留的、因此是未映射的。

    你可以用 dma-buf 来寻找一个更干净的方法。 这应该会提供缓存的映射和手动一致性。 或者、您也可以编写自己的内核驱动程序、类似于 char/mem.c、该驱动程序映射了所需的属性(正常的未缓存应该可以正常运行)。

    所有这些都需要付出更多的努力、但您却"误用"界面来实现它们并非出于的目的。 不幸的是、我经常发现"适当"解决方案缺少一些东西。  

    也许 Andrew Davis 或  有更好的建议。 我已经看到了两个工作的相关的东西。  

    此致、Dominic

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Dominic、感谢您让我进去。

    您好、 António

    问题

    1)您正在使用哪个版本的 Linux 内核?

    2)您是否会授予用户空间 root 用户级别的最终产品访问权限? 我的假设是,大多数应用程序不想给用户空间提供如此多的控制。

    一些背景知识  

    我们在 AM335x 上提供了一个古怪的示例、例如使用 mmap 访问 DDR 共享存储器区域并在 Linux 和 PRU 之间来回传递数据:
    https://git.ti.com/cgit/apps/tida01555/tree/ARM_User_Space_App / arm_user_space_app.c
    https://www.ti.com/tool/TIDA-01555

    因此、我只希望 mmap 能够在用户空间具有提升权限的环境中工作。 内核开发人员将告诉您、他们通常不希望授予用户空间访问权限来直接公开存储器。

    有几种不同的方法将内存暴露给用户空间、但就我所能说的而言、更广泛的 Linux 社区还没有真正以一种"正确"的方式来做到这一点。

    对于 TI 示例、我们在零复制示例中分配共享存储器区域(目前没有为 PRU 编写、但相同的概念适用)。

    对于 Linux 内核5.10和6.1、我们修补了 dma-buf-phys 驱动程序、以启用共享内存(即、此驱动程序代码在这些内核版本上运行、但没有上流)。 请参阅分支 ti-linux-6.1:
    https://git.ti.com/cgit/rpmsg/rpmsg_char_zerocopy/?h=ti-linux-6.1

    对于 Linux 内核6.6、我们移至使用 remoteproc cdv 驱动程序来启用共享内存。 请注意、这需要 Remoteproc CDev 驱动程序的补丁、这些补丁在我上次检查时也没有进行上流处理。 更多信息、请参阅主分支上的 Linux README.md:
    https://git.ti.com/cgit/rpmsg/rpmsg_char_zerocopy/tree/linux/README.md

    此致、

    Nick

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Nick。

    1.我们运行的是 ti-linux v6.1.105 (来自最新 meta-ti-bsps kirkstone)。 对于 AM62和 AM33、我们都使用同一个内核。

    2.是的、应用程序以特权运行。 这段代码可以追溯到过去、我们避免对其进行返工、以依赖于 rpms 或类似的东西。

    一些附加信息:

    *如果我们使用 mmap ()而不是通过"memset()/dev/mem ,则所有内存访问都可以正常运行,包括 memset ()。 遗憾的是、这种映射在建立映射后会清除内存、这会妨碍应用的操作。

    *映射是否有理由与 MAP_ANON 配合使用,但 SIGBUS 在通过/dev/mem?时的流程 请注意、这种情况在 DDR 和外设区域都发生

    *我知道这种设置的安全影响,通过将未初始化的内存暴露给应用程序。 我们的系统仅运行我们的内部软件、因此风险在控制范围内、尽管我同意一个不依赖特权运行的解决方案会更好。

    现在有点晚了。 明天我会介绍这些链接。 感谢所有的帮助。 除了使这种内存映射工作之外、我们还在寻找使用简单匿名映射的潜在变通办法。

    此致、

    António μ A

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    大家好、感谢您的意见。

    作为一个更新,对于它是值得的,我在这个主题上取得了一些进展(其他的东西已经受到阻碍,我没有很多时间来进一步研究这个)。

    此时、我通过按如下所示修改 devicetre 保留存储器来摆脱 SIGBUS:

    	reserved-memory {
    		#address-cells = <2>;
    		#size-cells = <2>;
    		ranges;
    
    		input-pru@0x9c600000 {
    			/* no-map; <---- commented this property!! */
    			reg = <0x00000000 0x9c600000 0x000000 0x00100000>;
    		};
        };

    通过注释`no-map`属性、显示崩溃已消失。 从我在主机 CPU 和运行一些定制固件的 PRU 之间进行的一些测试来看、存储器访问看起来效果很好。

    为什么此 devicetree 更改会产生任何影响、这一点我无法理解、但我将继续介绍 PRU+Host 设置、并向您通报运行情况。

    同时、如果对于这一变化能带来什么不同、有任何反馈或解释、我很乐意了解。

    此致、