|
@@ -2751,6 +2751,86 @@ int in_gate_area_no_task(unsigned long addr)
|
|
|
|
|
|
#endif /* __HAVE_ARCH_GATE_AREA */
|
|
#endif /* __HAVE_ARCH_GATE_AREA */
|
|
|
|
|
|
|
|
+#ifdef CONFIG_HAVE_IOREMAP_PROT
|
|
|
|
+static resource_size_t follow_phys(struct vm_area_struct *vma,
|
|
|
|
+ unsigned long address, unsigned int flags,
|
|
|
|
+ unsigned long *prot)
|
|
|
|
+{
|
|
|
|
+ pgd_t *pgd;
|
|
|
|
+ pud_t *pud;
|
|
|
|
+ pmd_t *pmd;
|
|
|
|
+ pte_t *ptep, pte;
|
|
|
|
+ spinlock_t *ptl;
|
|
|
|
+ resource_size_t phys_addr = 0;
|
|
|
|
+ struct mm_struct *mm = vma->vm_mm;
|
|
|
|
+
|
|
|
|
+ VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP)));
|
|
|
|
+
|
|
|
|
+ pgd = pgd_offset(mm, address);
|
|
|
|
+ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
|
|
|
|
+ goto no_page_table;
|
|
|
|
+
|
|
|
|
+ pud = pud_offset(pgd, address);
|
|
|
|
+ if (pud_none(*pud) || unlikely(pud_bad(*pud)))
|
|
|
|
+ goto no_page_table;
|
|
|
|
+
|
|
|
|
+ pmd = pmd_offset(pud, address);
|
|
|
|
+ if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
|
|
|
|
+ goto no_page_table;
|
|
|
|
+
|
|
|
|
+ /* We cannot handle huge page PFN maps. Luckily they don't exist. */
|
|
|
|
+ if (pmd_huge(*pmd))
|
|
|
|
+ goto no_page_table;
|
|
|
|
+
|
|
|
|
+ ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
|
|
|
|
+ if (!ptep)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ pte = *ptep;
|
|
|
|
+ if (!pte_present(pte))
|
|
|
|
+ goto unlock;
|
|
|
|
+ if ((flags & FOLL_WRITE) && !pte_write(pte))
|
|
|
|
+ goto unlock;
|
|
|
|
+ phys_addr = pte_pfn(pte);
|
|
|
|
+ phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */
|
|
|
|
+
|
|
|
|
+ *prot = pgprot_val(pte_pgprot(pte));
|
|
|
|
+
|
|
|
|
+unlock:
|
|
|
|
+ pte_unmap_unlock(ptep, ptl);
|
|
|
|
+out:
|
|
|
|
+ return phys_addr;
|
|
|
|
+no_page_table:
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
|
|
|
|
+ void *buf, int len, int write)
|
|
|
|
+{
|
|
|
|
+ resource_size_t phys_addr;
|
|
|
|
+ unsigned long prot = 0;
|
|
|
|
+ void *maddr;
|
|
|
|
+ int offset = addr & (PAGE_SIZE-1);
|
|
|
|
+
|
|
|
|
+ if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ phys_addr = follow_phys(vma, addr, write, &prot);
|
|
|
|
+
|
|
|
|
+ if (!phys_addr)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot);
|
|
|
|
+ if (write)
|
|
|
|
+ memcpy_toio(maddr + offset, buf, len);
|
|
|
|
+ else
|
|
|
|
+ memcpy_fromio(buf, maddr + offset, len);
|
|
|
|
+ iounmap(maddr);
|
|
|
|
+
|
|
|
|
+ return len;
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Access another process' address space.
|
|
* Access another process' address space.
|
|
* Source/target buffer must be kernel space,
|
|
* Source/target buffer must be kernel space,
|
|
@@ -2760,7 +2840,6 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
|
|
{
|
|
{
|
|
struct mm_struct *mm;
|
|
struct mm_struct *mm;
|
|
struct vm_area_struct *vma;
|
|
struct vm_area_struct *vma;
|
|
- struct page *page;
|
|
|
|
void *old_buf = buf;
|
|
void *old_buf = buf;
|
|
|
|
|
|
mm = get_task_mm(tsk);
|
|
mm = get_task_mm(tsk);
|
|
@@ -2772,28 +2851,44 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
|
|
while (len) {
|
|
while (len) {
|
|
int bytes, ret, offset;
|
|
int bytes, ret, offset;
|
|
void *maddr;
|
|
void *maddr;
|
|
|
|
+ struct page *page = NULL;
|
|
|
|
|
|
ret = get_user_pages(tsk, mm, addr, 1,
|
|
ret = get_user_pages(tsk, mm, addr, 1,
|
|
write, 1, &page, &vma);
|
|
write, 1, &page, &vma);
|
|
- if (ret <= 0)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- bytes = len;
|
|
|
|
- offset = addr & (PAGE_SIZE-1);
|
|
|
|
- if (bytes > PAGE_SIZE-offset)
|
|
|
|
- bytes = PAGE_SIZE-offset;
|
|
|
|
-
|
|
|
|
- maddr = kmap(page);
|
|
|
|
- if (write) {
|
|
|
|
- copy_to_user_page(vma, page, addr,
|
|
|
|
- maddr + offset, buf, bytes);
|
|
|
|
- set_page_dirty_lock(page);
|
|
|
|
|
|
+ if (ret <= 0) {
|
|
|
|
+ /*
|
|
|
|
+ * Check if this is a VM_IO | VM_PFNMAP VMA, which
|
|
|
|
+ * we can access using slightly different code.
|
|
|
|
+ */
|
|
|
|
+#ifdef CONFIG_HAVE_IOREMAP_PROT
|
|
|
|
+ vma = find_vma(mm, addr);
|
|
|
|
+ if (!vma)
|
|
|
|
+ break;
|
|
|
|
+ if (vma->vm_ops && vma->vm_ops->access)
|
|
|
|
+ ret = vma->vm_ops->access(vma, addr, buf,
|
|
|
|
+ len, write);
|
|
|
|
+ if (ret <= 0)
|
|
|
|
+#endif
|
|
|
|
+ break;
|
|
|
|
+ bytes = ret;
|
|
} else {
|
|
} else {
|
|
- copy_from_user_page(vma, page, addr,
|
|
|
|
- buf, maddr + offset, bytes);
|
|
|
|
|
|
+ bytes = len;
|
|
|
|
+ offset = addr & (PAGE_SIZE-1);
|
|
|
|
+ if (bytes > PAGE_SIZE-offset)
|
|
|
|
+ bytes = PAGE_SIZE-offset;
|
|
|
|
+
|
|
|
|
+ maddr = kmap(page);
|
|
|
|
+ if (write) {
|
|
|
|
+ copy_to_user_page(vma, page, addr,
|
|
|
|
+ maddr + offset, buf, bytes);
|
|
|
|
+ set_page_dirty_lock(page);
|
|
|
|
+ } else {
|
|
|
|
+ copy_from_user_page(vma, page, addr,
|
|
|
|
+ buf, maddr + offset, bytes);
|
|
|
|
+ }
|
|
|
|
+ kunmap(page);
|
|
|
|
+ page_cache_release(page);
|
|
}
|
|
}
|
|
- kunmap(page);
|
|
|
|
- page_cache_release(page);
|
|
|
|
len -= bytes;
|
|
len -= bytes;
|
|
buf += bytes;
|
|
buf += bytes;
|
|
addr += bytes;
|
|
addr += bytes;
|