123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- /*
- * arch/s390/lib/uaccess_pt.c
- *
- * User access functions based on page table walks.
- *
- * Copyright IBM Corp. 2006
- * Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
- */
- #include <linux/errno.h>
- #include <linux/hardirq.h>
- #include <linux/mm.h>
- #include <asm/uaccess.h>
- #include <asm/futex.h>
- static inline int __handle_fault(struct mm_struct *mm, unsigned long address,
- int write_access)
- {
- struct vm_area_struct *vma;
- int ret = -EFAULT;
- if (in_atomic())
- return ret;
- down_read(&mm->mmap_sem);
- vma = find_vma(mm, address);
- if (unlikely(!vma))
- goto out;
- if (unlikely(vma->vm_start > address)) {
- if (!(vma->vm_flags & VM_GROWSDOWN))
- goto out;
- if (expand_stack(vma, address))
- goto out;
- }
- if (!write_access) {
- /* page not present, check vm flags */
- if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
- goto out;
- } else {
- if (!(vma->vm_flags & VM_WRITE))
- goto out;
- }
- survive:
- switch (handle_mm_fault(mm, vma, address, write_access)) {
- case VM_FAULT_MINOR:
- current->min_flt++;
- break;
- case VM_FAULT_MAJOR:
- current->maj_flt++;
- break;
- case VM_FAULT_SIGBUS:
- goto out_sigbus;
- case VM_FAULT_OOM:
- goto out_of_memory;
- default:
- BUG();
- }
- ret = 0;
- out:
- up_read(&mm->mmap_sem);
- return ret;
- out_of_memory:
- up_read(&mm->mmap_sem);
- if (is_init(current)) {
- yield();
- down_read(&mm->mmap_sem);
- goto survive;
- }
- printk("VM: killing process %s\n", current->comm);
- return ret;
- out_sigbus:
- up_read(&mm->mmap_sem);
- current->thread.prot_addr = address;
- current->thread.trap_no = 0x11;
- force_sig(SIGBUS, current);
- return ret;
- }
- static inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
- size_t n, int write_user)
- {
- struct mm_struct *mm = current->mm;
- unsigned long offset, pfn, done, size;
- pgd_t *pgd;
- pmd_t *pmd;
- pte_t *pte;
- void *from, *to;
- done = 0;
- retry:
- spin_lock(&mm->page_table_lock);
- do {
- pgd = pgd_offset(mm, uaddr);
- if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- goto fault;
- pmd = pmd_offset(pgd, uaddr);
- if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- goto fault;
- pte = pte_offset_map(pmd, uaddr);
- if (!pte || !pte_present(*pte) ||
- (write_user && !pte_write(*pte)))
- goto fault;
- pfn = pte_pfn(*pte);
- if (!pfn_valid(pfn))
- goto out;
- offset = uaddr & (PAGE_SIZE - 1);
- size = min(n - done, PAGE_SIZE - offset);
- if (write_user) {
- to = (void *)((pfn << PAGE_SHIFT) + offset);
- from = kptr + done;
- } else {
- from = (void *)((pfn << PAGE_SHIFT) + offset);
- to = kptr + done;
- }
- memcpy(to, from, size);
- done += size;
- uaddr += size;
- } while (done < n);
- out:
- spin_unlock(&mm->page_table_lock);
- return n - done;
- fault:
- spin_unlock(&mm->page_table_lock);
- if (__handle_fault(mm, uaddr, write_user))
- return n - done;
- goto retry;
- }
- size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
- {
- size_t rc;
- if (segment_eq(get_fs(), KERNEL_DS)) {
- memcpy(to, (void __kernel __force *) from, n);
- return 0;
- }
- rc = __user_copy_pt((unsigned long) from, to, n, 0);
- if (unlikely(rc))
- memset(to + n - rc, 0, rc);
- return rc;
- }
- size_t copy_to_user_pt(size_t n, void __user *to, const void *from)
- {
- if (segment_eq(get_fs(), KERNEL_DS)) {
- memcpy((void __kernel __force *) to, from, n);
- return 0;
- }
- return __user_copy_pt((unsigned long) to, (void *) from, n, 1);
- }
|