Messages in this thread Patch in this message | | | Subject | [PATCH] drivers/char/mem.c, kernel 2.4.20: fix mmap /dev/kmem and vmalloc'd addresses | Date | Thu, 16 Jan 2003 13:08:15 -0500 | From | "Tony Dziedzic" <> |
| |
mmap'ing kernel virtual addresses above high_memory using /dev/kmem returns different results than reads of /dev/kmem. The problem occurs because the call to remap_page_range in mmap_mem assumes a physically-contiguous region of memory that is allocated below high_memory; this isn't the case for (e.g.) vmalloc'd memory.
The attached patch creates a unique mmap routine for /dev/kmem that uses the "nopage" vm_operations_struct function to construct a correct mapping to kernel memory when a page is accessed via the user virtual address. A simple test program verifies that the patch corrects the different values returned when reading /dev/kmem as compared to mmap'ing /dev/kmem.
Joseph "Tony" Dziedzic Storigen Systems, Inc.
--- linux-2.4.20/drivers/char/mem.c Fri Aug 2 20:39:43 2002 +++ linux-2.4.20-patch/drivers/char/mem.c Thu Jan 16 11:26:36 2003 @@ -520,7 +520,91 @@ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } -#define mmap_kmem mmap_mem +struct page *kmem_vm_nopage(struct vm_area_struct *vma, unsigned long address, int write) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long kaddr; + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + struct page *page = NULL; + + /* address is user VA; convert to kernel VA of desired page */ + kaddr = (address - vma->vm_start) + offset; + kaddr = VMALLOC_VMADDR(kaddr); + + spin_lock(&init_mm.page_table_lock); + + /* Lookup page structure for kernel VA */ + pgd = pgd_offset(&init_mm, kaddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + goto out; + pmd = pmd_offset(pgd, kaddr); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + goto out; + ptep = pte_offset(pmd, kaddr); + if (!ptep) + goto out; + pte = *ptep; + if (!pte_present(pte)) + goto out; + if (write && !pte_write(pte)) + goto out; + page = pte_page(pte); + if (!VALID_PAGE(page)) { + page = NULL; + goto out; + } + + /* Increment reference count on page */ + get_page(page); + +out: + spin_unlock(&init_mm.page_table_lock); + + return page; +} + +struct vm_operations_struct kmem_vm_ops = { + nopage: kmem_vm_nopage, +}; + +static int mmap_kmem(struct file * file, struct vm_area_struct * vma) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long size = vma->vm_end - vma->vm_start; + + /* + * If the user is not attempting to mmap a high memory address then + * the standard mmap_mem mechanism will work. High memory addresses + * need special handling, as remap_page_range expects a physically- + * contiguous range of kernel addresses (such as obtained in kmalloc). + */ + if ((offset + size) < (unsigned long) high_memory) + return mmap_mem(file, vma); + + /* + * Accessing memory above the top the kernel knows about or + * through a file pointer that was marked O_SYNC will be + * done non-cached. + */ + if (noncached_address(offset) || (file->f_flags & O_SYNC)) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Don't do anything here; "nopage" will fill the holes */ + vma->vm_ops = &kmem_vm_ops; + + /* Don't try to swap out physical pages.. */ + vma->vm_flags |= VM_RESERVED; + + /* + * Don't dump addresses that are not real memory to a core file. + */ + vma->vm_flags |= VM_IO; + + return 0; +} + #define zero_lseek null_lseek #define full_lseek null_lseek #define write_zero write_null - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |