Specifications
This is the Title of the Book, eMatter Edition
Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved.
The mmap Device Operation
|
429
Otherwise, pfn_to_page gets the necessary struct page pointer; we can increment its
reference count (with a call to get_page) and return it.
The nopage method normally returns a pointer to a
struct page. If, for some reason,
a normal page cannot be returned (e.g., the requested address is beyond the device’s
memory region),
NOPAGE_SIGBUS can be returned to signal the error; that is what the
simple code above does. nopage can also return NOPAGE_OOM to indicate failures caused
by resource limitations.
Note that this implementation works for ISA memory regions but not for those on
the PCI bus. PCI memory is mapped above the highest system memory, and there are
no entries in the system memory map for those addresses. Because there is no
struct
page
to return a pointer to, nopage cannot be used in these situations; you must use
remap_pfn_range instead.
If the nopage method is left
NULL, kernel code that handles page faults maps the zero
page to the faulting virtual address. The zero page is a copy-on-write page that reads
as
0 and that is used, for example, to map the BSS segment. Any process referencing
the zero page sees exactly that: a page filled with zeroes. If the process writes to the
page, it ends up modifying a private copy. Therefore, if a process extends a mapped
region by calling mremap, and the driver hasn’t implemented nopage, the process
ends up with zero-filled memory instead of a segmentation fault.
Remapping Specific I/O Regions
All the examples we’ve seen so far are reimplementations of /dev/mem; they remap
physical addresses into user space. The typical driver, however, wants to map only
the small address range that applies to its peripheral device, not all memory. In order
to map to user space only a subset of the whole memory range, the driver needs only
to play with the offsets. The following does the trick for a driver mapping a region of
simple_region_size bytes, beginning at physical address simple_region_start (which
should be page-aligned):
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned long physical = simple_region_start + off;
unsigned long vsize = vma->vm_end - vma->vm_start;
unsigned long psize = simple_region_size - off;
if (vsize > psize)
return -EINVAL; /* spans too high */
remap_pfn_range(vma, vma_>vm_start, physical, vsize, vma->vm_page_prot);
In addition to calculating the offsets, this code introduces a check that reports an
error when the program tries to map more memory than is available in the I/O region
of the target device. In this code,
psize is the physical I/O size that is left after the off-
set has been specified, and
vsize is the requested size of virtual memory; the func-
tion refuses to map addresses that extend beyond the allowed memory range.
,ch15.13676 Page 429 Friday, January 21, 2005 11:04 AM