For kernel hackers: is this mmap() correct for PPC?
gbburkhardt at verizon.net
gbburkhardt at verizon.net
Wed Oct 13 09:29:01 EDT 2004
We're writing an application to run on a PowerPC with
a 2.4 embedded Linux kernel, and I want to make
device registers accessable from user space with mmap(). The physical address of the device is above the 4gb boundary, so a standard 'remap_page_range()'
call won't work.
I've come up with a solution that appears to work,
but never having worked with the page tables before,
I'm a bit nervous. So if anyone can give the following code a short "code review", I'd greatly appreciate it.
The physical address value passed to 'mk_pte_phys' has
data type 'phys_addr_t', and has a value like
0x150000000ULL. Our device has only 0x100 bytes of
addresses starting at this location, so a single page
table entry is enough (pages are 0x1000 long), and
we'll only make one mmap() call per device.
TNA!!
static inline pgprot_t pgprot_noncached(pgprot_t _prot)
{
unsigned long prot = pgprot_val(_prot);
#if defined(__powerpc__)
prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
#endif
return __pgprot(prot);
}
#endif /* !pgprot_noncached */
static int mmap_ebc(struct file * file, struct vm_area_struct * vma)
{
unsigned long address;
if (vma->vm_pgoff) {
printk(KERN_INFO "EBC invalid vma offset: %lx\n", vma->vm_pgoff);
return -EINVAL;
}
/*
* Use non-cached access.
*/
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/* Don't try to swap out physical pages..
* Don't dump addresses that are not real memory to a core file.
*/
vma->vm_flags |= VM_RESERVED|VM_IO;
/* remap_page_range64()
Create a page table entry for the Peripheral Bus physical
addresses, in user space. Need only a single page, since
Phoenix boards have only 0x100 bytes of addresses.
*/
struct mm_struct *mm = current->mm;
pgd_t * dir;
dir = pgd_offset(mm, vma->vm_start);
if (pgd_none(*dir) || pgd_bad(*dir)) return -EINVAL;
spin_lock(&mm->page_table_lock);
pmd_t *pmd = pmd_alloc(mm, dir, vma->vm_start);
if (!pmd) return -ENOMEM;
address = vma->vm_start & ~PGDIR_MASK;
pte_t * pte = pte_alloc(mm, pmd, address);
if (!pte) return -ENOMEM;
/* Should probably add in "vma->vm_pgoff << PAGE_SHIFT" to
physical address here, but count on only one mmap call
per process per EBC device.
*/
set_pte(pte,
mk_pte_phys(((struct EBC_DATA *)file->private_data)->phys_addr,
vma->vm_page_prot));
spin_unlock(&mm->page_table_lock);
return 0;
}
More information about the Discuss
mailing list