drm: i915: fix up irqflags arg
[safe/jmp/linux-2.6] / drivers / char / mem.c
index 947cb3c..e67eef4 100644 (file)
@@ -8,7 +8,6 @@
  *  Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
  */
 
-#include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/capability.h>
 #include <linux/smp_lock.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/ptrace.h>
 #include <linux/device.h>
+#include <linux/highmem.h>
+#include <linux/crash_dump.h>
 #include <linux/backing-dev.h>
+#include <linux/bootmem.h>
+#include <linux/pipe_fs_i.h>
+#include <linux/pfn.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 # include <linux/efi.h>
 #endif
 
-#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
-extern void tapechar_init(void);
-#endif
-
 /*
  * Architectures vary in how they handle caching for addresses
  * outside of main memory.
@@ -89,17 +88,16 @@ static inline int uncached_access(struct file *file, unsigned long addr)
 }
 
 #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
-static inline int valid_phys_addr_range(unsigned long addr, size_t *count)
+static inline int valid_phys_addr_range(unsigned long addr, size_t count)
 {
-       unsigned long end_mem;
-
-       end_mem = __pa(high_memory);
-       if (addr >= end_mem)
+       if (addr + count > __pa(high_memory))
                return 0;
 
-       if (*count > end_mem - addr)
-               *count = end_mem - addr;
+       return 1;
+}
 
+static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
+{
        return 1;
 }
 #endif
@@ -115,7 +113,7 @@ static ssize_t read_mem(struct file * file, char __user * buf,
        ssize_t read, sz;
        char *ptr;
 
-       if (!valid_phys_addr_range(p, &count))
+       if (!valid_phys_addr_range(p, count))
                return -EFAULT;
        read = 0;
 #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
@@ -173,7 +171,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
        unsigned long copied;
        void *ptr;
 
-       if (!valid_phys_addr_range(p, &count))
+       if (!valid_phys_addr_range(p, count))
                return -EFAULT;
 
        written = 0;
@@ -212,11 +210,9 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
 
                copied = copy_from_user(ptr, buf, sz);
                if (copied) {
-                       ssize_t ret;
-
-                       ret = written + (sz - copied);
-                       if (ret)
-                               return ret;
+                       written += sz - copied;
+                       if (written)
+                               break;
                        return -EFAULT;
                }
                buf += sz;
@@ -229,28 +225,65 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
        return written;
 }
 
-static int mmap_mem(struct file * file, struct vm_area_struct * vma)
+#ifndef __HAVE_PHYS_MEM_ACCESS_PROT
+static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+                                    unsigned long size, pgprot_t vma_prot)
 {
-#if defined(__HAVE_PHYS_MEM_ACCESS_PROT)
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+#ifdef pgprot_noncached
+       unsigned long offset = pfn << PAGE_SHIFT;
 
-       vma->vm_page_prot = phys_mem_access_prot(file, offset,
-                                                vma->vm_end - vma->vm_start,
-                                                vma->vm_page_prot);
-#elif defined(pgprot_noncached)
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       int uncached;
+       if (uncached_access(file, offset))
+               return pgprot_noncached(vma_prot);
+#endif
+       return vma_prot;
+}
+#endif
+
+#ifndef CONFIG_MMU
+static unsigned long get_unmapped_area_mem(struct file *file,
+                                          unsigned long addr,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags)
+{
+       if (!valid_mmap_phys_addr_range(pgoff, len))
+               return (unsigned long) -EINVAL;
+       return pgoff;
+}
 
-       uncached = uncached_access(file, offset);
-       if (uncached)
-               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+/* can't do an in-place private mapping if there's no MMU */
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+       return vma->vm_flags & VM_MAYSHARE;
+}
+#else
+#define get_unmapped_area_mem  NULL
+
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+       return 1;
+}
 #endif
 
+static int mmap_mem(struct file * file, struct vm_area_struct * vma)
+{
+       size_t size = vma->vm_end - vma->vm_start;
+
+       if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
+               return -EINVAL;
+
+       if (!private_mapping_ok(vma))
+               return -ENOSYS;
+
+       vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
+                                                size,
+                                                vma->vm_page_prot);
+
        /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
        if (remap_pfn_range(vma,
                            vma->vm_start,
                            vma->vm_pgoff,
-                           vma->vm_end-vma->vm_start,
+                           size,
                            vma->vm_page_prot))
                return -EAGAIN;
        return 0;
@@ -258,7 +291,11 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
 
 static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
 {
-        unsigned long long val;
+       unsigned long pfn;
+
+       /* Turn a pfn offset into an absolute pfn */
+       pfn = PFN_DOWN(virt_to_phys((void *)PAGE_OFFSET)) + vma->vm_pgoff;
+
        /*
         * RED-PEN: on some architectures there is more mapped memory
         * than available in mem_map which pfn_valid checks
@@ -266,13 +303,47 @@ static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
         *
         * RED-PEN: vmalloc is not supported right now.
         */
-       if (!pfn_valid(vma->vm_pgoff))
+       if (!pfn_valid(pfn))
                return -EIO;
-       val = (u64)vma->vm_pgoff << PAGE_SHIFT;
-       vma->vm_pgoff = __pa(val) >> PAGE_SHIFT;
+
+       vma->vm_pgoff = pfn;
        return mmap_mem(file, vma);
 }
 
+#ifdef CONFIG_CRASH_DUMP
+/*
+ * Read memory corresponding to the old kernel.
+ */
+static ssize_t read_oldmem(struct file *file, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       unsigned long pfn, offset;
+       size_t read = 0, csize;
+       int rc = 0;
+
+       while (count) {
+               pfn = *ppos / PAGE_SIZE;
+               if (pfn > saved_max_pfn)
+                       return read;
+
+               offset = (unsigned long)(*ppos % PAGE_SIZE);
+               if (count > PAGE_SIZE - offset)
+                       csize = PAGE_SIZE - offset;
+               else
+                       csize = count;
+
+               rc = copy_oldmem_page(pfn, buf, csize, offset, 1);
+               if (rc < 0)
+                       return rc;
+               buf += csize;
+               *ppos += csize;
+               read += csize;
+               count -= csize;
+       }
+       return read;
+}
+#endif
+
 extern long vread(char *buf, char *addr, unsigned long count);
 extern long vwrite(char *buf, char *addr, unsigned long count);
 
@@ -406,11 +477,9 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
 
                copied = copy_from_user(ptr, buf, sz);
                if (copied) {
-                       ssize_t ret;
-
-                       ret = written + (sz - copied);
-                       if (ret)
-                               return ret;
+                       written += sz - copied;
+                       if (written)
+                               break;
                        return -EFAULT;
                }
                buf += sz;
@@ -464,11 +533,10 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
                        if (len) {
                                written = copy_from_user(kbuf, buf, len);
                                if (written) {
-                                       ssize_t ret;
-
+                                       if (wrote + virtr)
+                                               break;
                                        free_page((unsigned long)kbuf);
-                                       ret = wrote + virtr + (len - written);
-                                       return ret ? ret : -EFAULT;
+                                       return -EFAULT;
                                }
                        }
                        len = vwrite(kbuf, (char *)p, len);
@@ -484,7 +552,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
        return virtr + wrote;
 }
 
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
 static ssize_t read_port(struct file * file, char __user * buf,
                         size_t count, loff_t *ppos)
 {
@@ -513,8 +581,11 @@ static ssize_t write_port(struct file * file, const char __user * buf,
                return -EFAULT;
        while (count-- > 0 && i < 65536) {
                char c;
-               if (__get_user(c, tmp)) 
+               if (__get_user(c, tmp)) {
+                       if (tmp > buf)
+                               break;
                        return -EFAULT; 
+               }
                outb(c,i);
                i++;
                tmp++;
@@ -536,6 +607,18 @@ static ssize_t write_null(struct file * file, const char __user * buf,
        return count;
 }
 
+static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
+                       struct splice_desc *sd)
+{
+       return sd->len;
+}
+
+static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out,
+                                loff_t *ppos, size_t len, unsigned int flags)
+{
+       return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);
+}
+
 #ifdef CONFIG_MMU
 /*
  * For fun, we are using the MMU for this.
@@ -691,7 +774,7 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
 {
        loff_t ret;
 
-       down(&file->f_dentry->d_inode->i_sem);
+       mutex_lock(&file->f_dentry->d_inode->i_mutex);
        switch (orig) {
                case 0:
                        file->f_pos = offset;
@@ -706,7 +789,7 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
                default:
                        ret = -EINVAL;
        }
-       up(&file->f_dentry->d_inode->i_sem);
+       mutex_unlock(&file->f_dentry->d_inode->i_mutex);
        return ret;
 }
 
@@ -721,31 +804,35 @@ static int open_port(struct inode * inode, struct file * filp)
 #define read_full       read_zero
 #define open_mem       open_port
 #define open_kmem      open_mem
+#define open_oldmem    open_mem
 
-static struct file_operations mem_fops = {
+static const struct file_operations mem_fops = {
        .llseek         = memory_lseek,
        .read           = read_mem,
        .write          = write_mem,
        .mmap           = mmap_mem,
        .open           = open_mem,
+       .get_unmapped_area = get_unmapped_area_mem,
 };
 
-static struct file_operations kmem_fops = {
+static const struct file_operations kmem_fops = {
        .llseek         = memory_lseek,
        .read           = read_kmem,
        .write          = write_kmem,
        .mmap           = mmap_kmem,
        .open           = open_kmem,
+       .get_unmapped_area = get_unmapped_area_mem,
 };
 
-static struct file_operations null_fops = {
+static const struct file_operations null_fops = {
        .llseek         = null_lseek,
        .read           = read_null,
        .write          = write_null,
+       .splice_write   = splice_write_null,
 };
 
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
-static struct file_operations port_fops = {
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
+static const struct file_operations port_fops = {
        .llseek         = memory_lseek,
        .read           = read_port,
        .write          = write_port,
@@ -753,28 +840,39 @@ static struct file_operations port_fops = {
 };
 #endif
 
-static struct file_operations zero_fops = {
+static const struct file_operations zero_fops = {
        .llseek         = zero_lseek,
        .read           = read_zero,
        .write          = write_zero,
        .mmap           = mmap_zero,
 };
 
+/*
+ * capabilities for /dev/zero
+ * - permits private mappings, "copies" are taken of the source of zeros
+ */
 static struct backing_dev_info zero_bdi = {
        .capabilities   = BDI_CAP_MAP_COPY,
 };
 
-static struct file_operations full_fops = {
+static const struct file_operations full_fops = {
        .llseek         = full_lseek,
        .read           = read_full,
        .write          = write_full,
 };
 
+#ifdef CONFIG_CRASH_DUMP
+static const struct file_operations oldmem_fops = {
+       .read   = read_oldmem,
+       .open   = open_oldmem,
+};
+#endif
+
 static ssize_t kmsg_write(struct file * file, const char __user * buf,
                          size_t count, loff_t *ppos)
 {
        char *tmp;
-       int ret;
+       ssize_t ret;
 
        tmp = kmalloc(count + 1, GFP_KERNEL);
        if (tmp == NULL)
@@ -783,12 +881,15 @@ static ssize_t kmsg_write(struct file * file, const char __user * buf,
        if (!copy_from_user(tmp, buf, count)) {
                tmp[count] = 0;
                ret = printk("%s", tmp);
+               if (ret > count)
+                       /* printk can add a prefix */
+                       ret = count;
        }
        kfree(tmp);
        return ret;
 }
 
-static struct file_operations kmsg_fops = {
+static const struct file_operations kmsg_fops = {
        .write =        kmsg_write,
 };
 
@@ -797,14 +898,18 @@ static int memory_open(struct inode * inode, struct file * filp)
        switch (iminor(inode)) {
                case 1:
                        filp->f_op = &mem_fops;
+                       filp->f_mapping->backing_dev_info =
+                               &directly_mappable_cdev_bdi;
                        break;
                case 2:
                        filp->f_op = &kmem_fops;
+                       filp->f_mapping->backing_dev_info =
+                               &directly_mappable_cdev_bdi;
                        break;
                case 3:
                        filp->f_op = &null_fops;
                        break;
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
                case 4:
                        filp->f_op = &port_fops;
                        break;
@@ -825,6 +930,11 @@ static int memory_open(struct inode * inode, struct file * filp)
                case 11:
                        filp->f_op = &kmsg_fops;
                        break;
+#ifdef CONFIG_CRASH_DUMP
+               case 12:
+                       filp->f_op = &oldmem_fops;
+                       break;
+#endif
                default:
                        return -ENXIO;
        }
@@ -833,7 +943,7 @@ static int memory_open(struct inode * inode, struct file * filp)
        return 0;
 }
 
-static struct file_operations memory_fops = {
+static const struct file_operations memory_fops = {
        .open           = memory_open,  /* just a selector for the real open */
 };
 
@@ -841,12 +951,12 @@ static const struct {
        unsigned int            minor;
        char                    *name;
        umode_t                 mode;
-       struct file_operations  *fops;
+       const struct file_operations    *fops;
 } devlist[] = { /* list of minor devices */
        {1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
        {2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
        {3, "null",    S_IRUGO | S_IWUGO,           &null_fops},
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
        {4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
 #endif
        {5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
@@ -854,9 +964,12 @@ static const struct {
        {8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
        {9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},
        {11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops},
+#ifdef CONFIG_CRASH_DUMP
+       {12,"oldmem",    S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops},
+#endif
 };
 
-static struct class_simple *mem_class;
+static struct class *mem_class;
 
 static int __init chr_dev_init(void)
 {
@@ -865,15 +978,12 @@ static int __init chr_dev_init(void)
        if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
                printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 
-       mem_class = class_simple_create(THIS_MODULE, "mem");
-       for (i = 0; i < ARRAY_SIZE(devlist); i++) {
-               class_simple_device_add(mem_class,
-                                       MKDEV(MEM_MAJOR, devlist[i].minor),
-                                       NULL, devlist[i].name);
-               devfs_mk_cdev(MKDEV(MEM_MAJOR, devlist[i].minor),
-                               S_IFCHR | devlist[i].mode, devlist[i].name);
-       }
-       
+       mem_class = class_create(THIS_MODULE, "mem");
+       for (i = 0; i < ARRAY_SIZE(devlist); i++)
+               device_create(mem_class, NULL,
+                             MKDEV(MEM_MAJOR, devlist[i].minor),
+                             devlist[i].name);
+
        return 0;
 }