fb:support 180 degree rotate
[firefly-linux-kernel-4.4.55.git] / drivers / char / mem.c
index a074fceb67d30109c08e862b9b775b6c058da904..ba1db9b601d2ba43ef7f7f5e84297f79a2761dfc 100644 (file)
 # include <linux/efi.h>
 #endif
 
+static inline unsigned long size_inside_page(unsigned long start,
+                                            unsigned long size)
+{
+       unsigned long sz;
+
+       if (-start & (PAGE_SIZE - 1))
+               sz = -start & (PAGE_SIZE - 1);
+       else
+               sz = PAGE_SIZE;
+
+       return min_t(unsigned long, sz, size);
+}
+
 /*
  * Architectures vary in how they handle caching for addresses
  * outside of main memory.
@@ -80,6 +93,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
 }
 #endif
 
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
 #ifdef CONFIG_STRICT_DEVMEM
 static inline int range_is_allowed(unsigned long pfn, unsigned long size)
 {
@@ -105,7 +119,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
        return 1;
 }
 #endif
+#endif
 
+#ifdef CONFIG_DEVMEM
 void __attribute__((weak)) unxlate_dev_mem_ptr(unsigned long phys, void *addr)
 {
 }
@@ -254,6 +270,9 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
        *ppos += written;
        return written;
 }
+#endif /* CONFIG_DEVMEM */
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
 
 int __attribute__((weak)) phys_mem_access_prot_allowed(struct file *file,
        unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
@@ -340,6 +359,7 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
        }
        return 0;
 }
+#endif /* CONFIG_DEVMEM */
 
 #ifdef CONFIG_DEVKMEM
 static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
@@ -408,6 +428,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
        unsigned long p = *ppos;
        ssize_t low_count, read, sz;
        char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
+       int err = 0;
 
        read = 0;
        if (p < (unsigned long) high_memory) {
@@ -430,15 +451,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
                }
 #endif
                while (low_count > 0) {
-                       /*
-                        * Handle first page in case it's not aligned
-                        */
-                       if (-p & (PAGE_SIZE - 1))
-                               sz = -p & (PAGE_SIZE - 1);
-                       else
-                               sz = PAGE_SIZE;
-
-                       sz = min_t(unsigned long, sz, low_count);
+                       sz = size_inside_page(p, low_count);
 
                        /*
                         * On ia64 if a page has been mapped somewhere as
@@ -462,16 +475,18 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
                if (!kbuf)
                        return -ENOMEM;
                while (count > 0) {
-                       int len = count;
+                       int len = size_inside_page(p, count);
 
-                       if (len > PAGE_SIZE)
-                               len = PAGE_SIZE;
+                       if (!is_vmalloc_or_module_addr((void *)p)) {
+                               err = -ENXIO;
+                               break;
+                       }
                        len = vread(kbuf, (char *)p, len);
                        if (!len)
                                break;
                        if (copy_to_user(buf, kbuf, len)) {
-                               free_page((unsigned long)kbuf);
-                               return -EFAULT;
+                               err = -EFAULT;
+                               break;
                        }
                        count -= len;
                        buf += len;
@@ -480,8 +495,8 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
                }
                free_page((unsigned long)kbuf);
        }
-       *ppos = p;
-       return read;
+       *ppos = p;
+       return read ? read : err;
 }
 
 
@@ -510,15 +525,8 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
 
        while (count > 0) {
                char *ptr;
-               /*
-                * Handle first page in case it's not aligned
-                */
-               if (-realp & (PAGE_SIZE - 1))
-                       sz = -realp & (PAGE_SIZE - 1);
-               else
-                       sz = PAGE_SIZE;
 
-               sz = min_t(unsigned long, sz, count);
+               sz = size_inside_page(realp, count);
 
                /*
                 * On ia64 if a page has been mapped somewhere as
@@ -557,6 +565,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
        ssize_t virtr = 0;
        ssize_t written;
        char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
+       int err = 0;
 
        if (p < (unsigned long) high_memory) {
 
@@ -578,20 +587,20 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
                if (!kbuf)
                        return wrote ? wrote : -ENOMEM;
                while (count > 0) {
-                       int len = count;
+                       int len = size_inside_page(p, count);
 
-                       if (len > PAGE_SIZE)
-                               len = PAGE_SIZE;
+                       if (!is_vmalloc_or_module_addr((void *)p)) {
+                               err = -ENXIO;
+                               break;
+                       }
                        if (len) {
                                written = copy_from_user(kbuf, buf, len);
                                if (written) {
-                                       if (wrote + virtr)
-                                               break;
-                                       free_page((unsigned long)kbuf);
-                                       return -EFAULT;
+                                       err = -EFAULT;
+                                       break;
                                }
                        }
-                       len = vwrite(kbuf, (char *)p, len);
+                       vwrite(kbuf, (char *)p, len);
                        count -= len;
                        buf += len;
                        virtr += len;
@@ -600,8 +609,8 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
                free_page((unsigned long)kbuf);
        }
 
-       *ppos = p;
-       return virtr + wrote;
+       *ppos = p;
+       return virtr + wrote ? : err;
 }
 #endif
 
@@ -730,6 +739,8 @@ static loff_t null_lseek(struct file * file, loff_t offset, int orig)
        return file->f_pos = 0;
 }
 
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
+
 /*
  * The memory devices use the full 32/64 bits of the offset, and so we cannot
  * check against negative addresses: they are ok. The return value is weird,
@@ -761,10 +772,14 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
        return ret;
 }
 
+#endif
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
 static int open_port(struct inode * inode, struct file * filp)
 {
        return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
+#endif
 
 #define zero_lseek     null_lseek
 #define full_lseek      null_lseek
@@ -774,6 +789,7 @@ static int open_port(struct inode * inode, struct file * filp)
 #define open_kmem      open_mem
 #define open_oldmem    open_mem
 
+#ifdef CONFIG_DEVMEM
 static const struct file_operations mem_fops = {
        .llseek         = memory_lseek,
        .read           = read_mem,
@@ -782,6 +798,7 @@ static const struct file_operations mem_fops = {
        .open           = open_mem,
        .get_unmapped_area = get_unmapped_area_mem,
 };
+#endif
 
 #ifdef CONFIG_DEVKMEM
 static const struct file_operations kmem_fops = {
@@ -820,10 +837,11 @@ static const struct file_operations zero_fops = {
 /*
  * capabilities for /dev/zero
  * - permits private mappings, "copies" are taken of the source of zeros
+ * - no writeback happens
  */
 static struct backing_dev_info zero_bdi = {
        .name           = "char/mem",
-       .capabilities   = BDI_CAP_MAP_COPY,
+       .capabilities   = BDI_CAP_MAP_COPY | BDI_CAP_NO_ACCT_AND_WRITEBACK,
 };
 
 static const struct file_operations full_fops = {
@@ -870,7 +888,9 @@ static const struct memdev {
        const struct file_operations *fops;
        struct backing_dev_info *dev_info;
 } devlist[] = {
+#ifdef CONFIG_DEVMEM
         [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
+#endif
 #ifdef CONFIG_DEVKMEM
         [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
 #endif