Home
| Calendar
| Mail Lists
| List Archives
| Desktop SIG
| Hardware Hacking SIG
Wiki | Flickr | PicasaWeb | Video | Maps & Directions | Installfests | Keysignings Linux Cafe | Meeting Notes | Linux Links | Bling | About BLU |
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; }
BLU is a member of BostonUserGroups | |
We also thank MIT for the use of their facilities. |