In the Linux kernel, it is possible to map a kernel address space to a user address space. This eliminates the overhead due to the copying of user-space information into kernel-space and vice versa.
This can be done through a device driver and its device (/ dev) interface in user-space. This feature can be used by completing the mmap operation from the struct file_operations associated with the device and using the mmap call from user-space.
Before discussing the memory mapping mechanism on a device, we will present some of the basic structures related to the memory management subsystem in the Linux kernel. Some of the important structures are: struct page, struct vm_area_struct and struct mm_struct.
The struct page structure is used to embed information about all the physical pages in the system. The kernel has a struct page structure for all pages in the system.
Many functions interact with this structure:
– virt_to_page returns the page associated with a virtual address;
– pfn_to_page returns the associated page for a page frame number;
– page_address returns the virtual address of the transmitted page as a parameter.
The struct vm_area_struct holds information about a continuous virtual memory area. A start address characterizes a memory area, stop address, length, permissions. A vm_area_struct structure is created for each mmap call in the user-space. A driver that has support for the mmap operation must complete and initialize the associated vm_area_struct structure.
The most important fields of this structure are:
– vm_start, vm_end, represents the beginning, respectively, the end of the memory area (these fields also appear in / proc / * / maps);
– vm_file, the pointer to the associated file structure (if any);
– vm_pgoff, the offset of the area within the file;
– vm_flags, a set of indicators;
– vm_ops, a set of working functions on this area.
– vm_next, vm_prev, the vm_area of a process are linked through a list.
Structure struct mm_struct encompasses all memory areas associated with a process; using the mm field of the task_struct structure; you can get the mm_struct structure associated with the current process. Memory mapping is one of the most interesting features of a Unix system. From a driver’s point of view, the memory-mapping facility allows direct access to the memory of a device from user-space. To associate a mmap operation with a driver, the mmap field in the struct file_operations associated with the device must be used. The method thus associated is used in the case of an mmap call from user-space.
The mmap call from user-space maps between the address space of a process and a file and has the signature:
void * mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset);
To map the memory between a device and the user-space, the device will open with an open call and its descriptor will be passed to the mmap call.
The kernel-space mmap operation declares itself as a member of the struct file_operations structure of the form:
int (* mmap) (struct file * filp, struct vm_area_struct * vma)
To map a physical memory space in the user virtual space, represented by the struct structure vm_area_struct, the remap_pfn_range call is used.
It will map a contiguous physical address space in the virtual space represented by the struct vm_area_struct:
int remap_pfn_range (struct vm_area_struct * vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
In most cases, the implementation of the mmap function described above is reduced to a call to the remap_pfn_range function. The pages allocated using vmalloc are not physically contiguous, so you will need to map each page separately (if the memory area has multiple pages), calling remap_pfn_range for each virtual page.