0

I'm browsing through LDD3 and I have difficulty understanding a chapter about mmap().

Here is a code snipped from the book:

static int simple_remap_mmap(struct file *file, struct vm_area_struct *vma)
{
    if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
                vma->vm_end - vma->vm_start,
                vma->vm_page_prot))
        return -EAGAIN;

    vma->vm_ops = &simple_remap_vm_ops;
    simple_vma_open(vma);
    return 0;
}

If I understand it correctly this piece of code is supposed to map my device's I/O memory into user space. But how? Basically this function does almost nothing, so how would it map some device's memory area into the userspace process if it does not use any physical addresses? Also, I've read that it is not able to map RAM - so what does this function actually map?

Here is another code snippet that basically should cover the same chapter from this book, but as you can see it is completely different. Why is that? Why in the second example there is an explicit page allocation whereas, in the first one, there is none?

static struct vm_operations_struct simple_remap_vm_ops = {
    .open =  simple_vma_open,
    .close = simple_vma_close,
};

static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int rv;
    size_t len = vma->vm_end - vma->vm_start;
    struct page *pages;

    /*
     * To prevent from memory over run. Limit the maximux mmap size here
     * to 10 * PAGE_SIZE
     */
    if (len >> PAGE_SHIFT > 10) {
        pr_err("Map size overflow! len=%ld pages\n", len >> PAGE_SHIFT);
        return -EFAULT;
    }

    /*
     * Allocate necessary pages and initialize it properly.
     * We initialize all memory to a special value: 0xdeadbeef
     */
    pages = alloc_pages(GFP_KERNEL, get_order(len));
    memset32(page_address(pages), 0xdeadbeef,
         (1 << get_order(len)) * PAGE_SIZE / 4);

    rv = remap_pfn_range(vma, vma->vm_start, page_to_pfn(pages),
                vma->vm_end - vma->vm_start,
                vma->vm_page_prot);
    if (rv) {
        return -EAGAIN;
    }

    pr_debug("vm_start: %#lx, vm_end: %#lx\n, len: %#lx, vm_pgoff: %#lx(%#lx), ops=%p\n",
         vma->vm_start, vma->vm_end, len,
         vma->vm_pgoff, vma->vm_pgoff << PAGE_SHIFT,
         vma->vm_ops);

    vma->vm_ops = &simple_remap_vm_ops;
    simple_vma_open(vma);

    return 0;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
bielu000
  • 1,826
  • 3
  • 21
  • 45
  • 1
    VMA stands for Virtual Memory Area. The `remap_pfn_range()` takes existing virtual mapping and maps it again into virtual address space. Something should first to map the MMIO into memory address space, before you can remap it. That's how I understand this. – 0andriy Jun 26 '23 at 21:17
  • Yes I get it, however I don't understand one thing. "remap_pfn_range() will map a contiguous physical address space into the virtual space represented by vm_area_struct:" What is physical address space in this case? Where is it specified? As far as I understand vma->vm_start and vma->vm_end refers to the virtual address range in the userspace process, so where are the addresses of the physical pages I want to map? Are they calculated using vma->vm_start and end (which are assigned by kernel before jumping into my mmap implementation) by translating virtual addresses into physical addresses? – bielu000 Jun 27 '23 at 19:34
  • And nobody knows what you want to map, don’t ask us _where the addresses of the physical pages are_, we do not know, but you. – 0andriy Jun 28 '23 at 09:46
  • It is a code snippet copied directly from LDD3, I guess it "does something" and it "maps" something so that is why I am asking about this particular example. So if there is no physical address used in this particular case, what does this code do? Don't ask me about the physical address I use, because I'm relying directly on the example from the book. If I was trying to mmap my custom device's address I would definitely mention this. Do you see the difference? – bielu000 Jun 28 '23 at 10:02
  • 1
    The "Remapping Specific I/O Regions" section of LDD3 is a bit more realistic for a hardware device with a single region to be mmaped, but for `simple_region_start` and `simple_region_size`, use values from the corresponding `struct resource` (the `.start` member, for the start address, and `resource_size(&resource)`, but adjusted to align to page boundaries. Also check the how it is done by the "uio" (userspace I/O device) drivers. – Ian Abbott Jun 30 '23 at 17:06

0 Answers0