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