Why shouldn't I use ioremap in system memory for ARMv6 +?

I need to reserve a large buffer of physically contiguous RAM from the kernel and be able to ensure that a specific, hard-coded physical address is always used in the buffer. This buffer must remain reserved for the entire life of the kernel. I wrote a chardev driver as an interface to access this buffer in user space. My platform is an embedded system with ARMv7 architecture with 2.6 Linux kernel.

Chapter 15 Linux Device Drivers, Third Edition has the right to say on this topic (p. 443):

Saving the top of the RAM is done by passing an argument to the mem=

kernel at boot time. For example, if you have 256 MB, the argument mem=255M

allows the kernel to use the top megabyte. Later, your module can use the following code to access such memory:     dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);

I did this and a couple of other things:

  • I am using memmap

    bootarg in addition to mem

    . The kernel boot documentation documentation suggests to always use memmap

    whenever you use mem

    to avoid address collisions.
  • I used request_mem_region

    before the call ioremap

    and of course I check that it succeeds before moving forward.

This is what the system looks like after I've done all this:

# cat /proc/cmdline 
root=/dev/mtdblock2 console=ttyS0,115200 init=/sbin/preinit earlyprintk debug mem=255M memmap=1M$255M
# cat /proc/iomem 
08000000-0fffffff : PCIe Outbound Window, Port 0
  08000000-082fffff : PCI Bus 0001:01
    08000000-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
    40800000-40807fff : 0002:01:00.0
80000000-8fefffff : System RAM
  80052000-8045dfff : Kernel text
  80478000-80500143 : Kernel data
8ff00000-8fffffff : foo

      

Everything looks good so far and my driver is working fine. I can read and write directly to the specific physical address I have chosen.

However, a big scary warning () was launched during boot :

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

Launched from: arc / arm / mm / ioremap.c

/*
 * 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);
}

      

What kind of problems can arise? Can they be mitigated? What are my alternatives?

+3


source to share


1 answer


So, I did just that and it works.

Provide a kernel command line (e.g. / proc / cmdline ) and the resulting memory map (i.e. / proc / iomem ) to test this.

What kind of problems can arise?

The problem with using ioremap () in system memory is that you end up assigning conflicting memory attributes, which causes "unpredictable" behavior.
See the ARM Multi-User Memory article for a history of the alert you are triggering.

The ARM core displays RAM as regular memory with write-back caching; it is also marked as one-way on uniprocessor systems. The ioremap () system call used to map I / O memory to CPU usage is different: this memory is mapped as device memory, inactive, and possibly shared. These different mappings give the expected behavior for both types of memory. Where things get tricky is when someone calls ioremap () to create a new mapping for the system RAM.

The problem with these multiple collations is that they will have different attributes. Starting with version 6 of the ARM architecture, this behavior is "unpredictable" in this situation.

Note that "system memory" is random access memory managed by the kernel.
The fact that you run the warning indicates that your code is generating multiple mappings for the memory area.

Can they be mitigated?

You must make sure that the RAM required for ioremap () is not "system memory", that is, managed by the kernel.
See also this answer .


ADDITION

This warning that concerns you is the result of pfn_valid (pfn) returning TRUE, not FALSE.
Based on the Linux cross reference you provided for version 2.6.37, pfn_valid () just returns the result



memblock_is_memory(pfn << PAGE_SHIFT);  

      

which in turn just returns the result

memblock_search(&memblock.memory, addr) != -1;  

      

I suggest that the kernel code be cracked to expose the conflict.
Before calling ioremap (), assign TRUE to a global variable memblock_debug

.
The next patch should display essential information about memory contention.
(The memblock list is ordered by base address, so memblock_search () does a binary search on that list, so use mid

as an index.)

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

      

If you want to see all blocks of memory, call memblock_dump_all () where the variable memblock_debug

is TRUE.

[Interestingly, this is essentially a programming question, but we haven't seen any of your code.]


APPENDIX 2

Since you are probably using ATAG (instead of a device tree) and want to allocate an area of ​​memory, correct ATAG_MEM to reflect this smaller physical memory size.
Assuming you made zero changes to your boot code, ATAG_MEM still indicates full RAM, so this could possibly be the cause of the system memory conflict causing the warning.
See this answer about ATAG and this related answer .

+6


source







All Articles