linux – 为什么我不应该在ARMv6的系统内存上使用ioremap?
作者:互联网
我需要从内核中保留一个物理连续RAM的大缓冲区,并且能够保证缓冲区将始终使用特定的硬编码物理地址.此缓冲区应保留为内核的整个生命周期.我编写了一个chardev驱动程序作为用户空间中访问此缓冲区的接口.我的平台是一个嵌入式系统,ARMv7架构运行2.6 Linux内核.
有关该主题(第443页)的第15章(共Linux Device Drivers, Third Edition页):
Reserving the top of RAM is accomplished by passing a
mem=
argument to the kernel at boot time. For example, if you have 256 MB, the argumentmem=255M
keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:
dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
我已经完成了这个以及其他一些事情:
>除了mem之外,我还在使用memmap bootarg. kernel boot parameters documentation建议在使用mem时始终使用memmap以避免地址冲突.
>我在调用ioremap之前使用了request_mem_region,当然,在继续前进之前,我检查它是否成功.
这是我完成所有这些后系统的样子:
# cat /proc/cmdline
root=/dev/mtdblock2 console=ttyS0,115200 init=/sbin/preinit earlyprintk debug mem=255M memmap=1M$255M
# cat /proc/iomem
Reserving the top of RAM is accomplished by passing a mem=
argument to the kernel at boot time. For example, if you have 256 MB, the argument mem=255M
keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:
dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
000-0fffffff : PCIe Outbound Window, Port 0
Reserving the top of RAM is accomplished by passing a mem=
argument to the kernel at boot time. For example, if you have 256 MB, the argument mem=255M
keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:
dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
000-082fffff : PCI Bus 0001:01
Reserving the top of RAM is accomplished by passing a mem=
argument to the kernel at boot time. For example, if you have 256 MB, the argument mem=255M
keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:
dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
000-081fffff : 0001:01:00.0
08200000-08207fff : 0001:01:00.0
18000300-18000307 : serial
18000400-18000407 : serial
1800c000-1800cfff : dmu_regs
18012000-18012fff : pcie0
18013000-18013fff : pcie1
18014000-18014fff : pcie2
19000000-19000fff : cru_regs
1e000000-1fffffff : norflash
40000000-47ffffff : PCIe Outbound Window, Port 1
40000000-403fffff : PCI Bus 0002:01
40000000-403fffff : 0002:01:00.0
40400000-409fffff : PCI Bus 0002:01
40400000-407fffff : 0002:01:00.0
4
Reserving the top of RAM is accomplished by passing a mem=
argument to the kernel at boot time. For example, if you have 256 MB, the argument mem=255M
keeps the kernel from using the top megabyte. Your module could later use the following code to gain access to such memory:
dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
00-40807fff : 0002:01:00.0
80000000-8fefffff : System RAM
80052000-8045dfff : Kernel text
80478000-80500143 : Kernel data
8ff00000-8fffffff : foo
到目前为止,一切看起来都很好,我的司机工作得很好.我能够直接读取和写入我选择的特定物理地址.
但是,在启动过程中,触发了一个很大的可怕警告(™):
BUG: Your driver calls ioremap() on system memory. This leads
to architecturally unpredictable behaviour on ARMv6+, and ioremap()
will fail in the next kernel release. Please fix your driver.
------------[ cut here ]------------
WARNING: at arch/arm/mm/ioremap.c:211 __arm_ioremap_pfn_caller+0x8c/0x144()
Modules linked in:
[] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64)
[] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24)
[] (warn_slowpath_null+0x1c/0x24) from [] (__arm_ioremap_pfn_caller+0x8c/0x144)
[] (__arm_ioremap_pfn_caller+0x8c/0x144) from [] (__arm_ioremap_caller+0x50/0x58)
[] (__arm_ioremap_caller+0x50/0x58) from [] (foo_init+0x204/0x2b0)
[] (foo_init+0x204/0x2b0) from [] (do_one_initcall+0x30/0x19c)
[] (do_one_initcall+0x30/0x19c) from [] (kernel_init+0x154/0x218)
[] (kernel_init+0x154/0x218) from [] (kernel_thread_exit+0x0/0x8)
---[ end trace 1a4cab5dbc05c3e7 ]---
/*
* Don't allow RAM to be mapped - this causes problems with ARMv6+
*/
if (pfn_valid(pfn)) {
printk(KERN_WARNING "BUG: Your driver calls ioremap() on system memory. This leads\n"
KERN_WARNING "to architecturally unpredictable behaviour on ARMv6+, and ioremap()\n"
KERN_WARNING "will fail in the next kernel release. Please fix your driver.\n");
WARN_ON(1);
}
确切地说,这会导致什么问题?他们能减轻吗?我有什么选择?
解决方法:
So I’ve done exactly that, and it’s working.
提供内核命令行(例如/ proc / cmdline)和生成的内存映射(即/ proc / iomem)来验证这一点.
What problems, exactly, could this cause?
在系统内存上使用ioremap()的问题是,您最终会将冲突的属性分配给内存,从而导致“不可预测”的行为.
请参阅文章“ARM’s multiply-mapped memory mess”,该文章提供了您正在触发的警告的历史记录.
The ARM kernel maps RAM as normal memory with writeback caching; it’s also marked non-shared on uniprocessor systems. The ioremap() system call, used to map I/O memory for CPU use, is different: that memory is mapped as device memory, uncached, and, maybe, shared. These different mappings give the expected behavior for both types of memory. Where things get tricky is when somebody calls ioremap() to create a new mapping for system RAM.
The problem with these multiple mappings is that they will have differing attributes. As of version 6 of the ARM architecture, the specified behavior in that situation is “unpredictable.”
请注意,“系统内存”是由内核管理的RAM.
触发警告的事实表明您的代码正在为内存区域生成多个映射.
Can they be mitigated?
您必须确保您想要ioremap()的RAM不是“系统内存”,即由内核管理.
另见this answer.
附录
关注您的此警告是pfn_valid(pfn)返回TRUE而不是FALSE的结果.
基于您为2.6.37版提供的Linux交叉引用链接,
pfn_valid()只是返回结果
memblock_is_memory(pfn << PAGE_SHIFT);
这反过来只是返回结果
memblock_search(&memblock.memory, addr) != -1;
我建议破解内核代码,以便揭示冲突.
在调用ioremap()之前,将TRUE赋值给全局变量memblock_debug.
以下补丁应显示有关内存冲突的重要信息.
(memblock列表按基址排序,因此memblock_search()在此列表上执行二进制搜索,因此使用mid作为索引.)
static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
{
unsigned int left = 0, right = type->cnt;
do {
unsigned int mid = (right + left) / 2;
if (addr < type->regions[mid].base)
right = mid;
else if (addr >= (type->regions[mid].base +
type->regions[mid].size))
left = mid + 1;
- else
+ else {
+ if (memblock_debug)
+ pr_info("MATCH for 0x%x: m=0x%x b=0x%x s=0x%x\n",
+ addr, mid,
+ type->regions[mid].base,
+ type->regions[mid].size);
return mid;
+ }
} while (left < right);
return -1;
}
如果要查看所有内存块,请使用变量memblock_debug调用memblock_dump_all()为TRUE.
[有趣的是,这实际上是一个编程问题,但我们还没有看到你的任何代码.]
附录2
由于您可能正在使用ATAG(而不是设备树),并且您想要专用内存区域,请修复ATAG_MEM以反映这种较小的物理内存大小.
假设您对引导代码进行了零更改,ATAG_MEM仍然指定了完整的RAM,因此这可能是导致警告的系统内存冲突的根源.
见this answer about ATAGs和this related answer.
标签:linux,arm,embedded-linux,linux-kernel,ioremap 来源: https://codeday.me/bug/20190727/1552406.html