X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fmem.c;h=29c41f4418c065d9aa8a447ae34f984e2cf3f321;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=418da028ccf337978f0253c8f248f853c9ca2278;hpb=f7ed79d23a47594e7834d66a8f14449796d4f3e6;p=linux-2.6.git diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 418da028c..29c41f441 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -36,22 +35,6 @@ # include #endif -static inline int range_is_allowed(unsigned long from, unsigned long to) -{ - unsigned long cursor; - - cursor = from >> PAGE_SHIFT; - while ((cursor << PAGE_SHIFT) < to) { - if (!devmem_is_allowed(cursor)) { - printk ("Program %s tried to read /dev/mem between %lx->%lx.\n", - current->comm, from, to); - return 0; - } - cursor++; - } - return 1; -} - /* * Architectures vary in how they handle caching for addresses * outside of main memory. @@ -105,21 +88,26 @@ 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) { - if (addr + count > __pa(high_memory)) + unsigned long end_mem; + + end_mem = __pa(high_memory); + if (addr >= end_mem) return 0; + if (*count > end_mem - addr) + *count = end_mem - addr; + return 1; } -static inline int valid_mmap_phys_addr_range(unsigned long addr, size_t size) +static inline int valid_mmap_phys_addr_range(unsigned long addr, size_t *size) { return 1; } #endif -#ifndef ARCH_HAS_DEV_MEM /* * This funcion reads the *physical* memory. The f_pos points directly to the * memory location. @@ -131,7 +119,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 @@ -169,8 +157,6 @@ static ssize_t read_mem(struct file * file, char __user * buf, */ ptr = xlate_dev_mem_ptr(p); - if (!range_is_allowed(p, p+count)) - return -EPERM; if (copy_to_user(buf, ptr, sz)) return -EFAULT; buf += sz; @@ -191,7 +177,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; @@ -228,13 +214,13 @@ static ssize_t write_mem(struct file * file, const char __user * buf, */ ptr = xlate_dev_mem_ptr(p); - if (!range_is_allowed(ptr, ptr+sz)) - return -EPERM; copied = copy_from_user(ptr, buf, sz); if (copied) { - written += sz - copied; - if (written) - break; + ssize_t ret; + + ret = written + (sz - copied); + if (ret) + return ret; return -EFAULT; } buf += sz; @@ -246,7 +232,6 @@ static ssize_t write_mem(struct file * file, const char __user * buf, *ppos += written; return written; } -#endif #ifndef __HAVE_PHYS_MEM_ACCESS_PROT static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, @@ -266,7 +251,7 @@ 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 << PAGE_SHIFT, size)) + if (!valid_mmap_phys_addr_range(vma->vm_pgoff << PAGE_SHIFT, &size)) return -EINVAL; vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, @@ -351,8 +336,6 @@ static ssize_t read_kmem(struct file *file, char __user *buf, ssize_t low_count, read, sz; char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ - return -EPERM; - read = 0; if (p < (unsigned long) high_memory) { low_count = count; @@ -429,6 +412,128 @@ static ssize_t read_kmem(struct file *file, char __user *buf, } +static inline ssize_t +do_write_kmem(void *p, unsigned long realp, const char __user * buf, + size_t count, loff_t *ppos) +{ + ssize_t written, sz; + unsigned long copied; + + written = 0; +#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED + /* we don't have page 0 mapped on sparc and m68k.. */ + if (realp < PAGE_SIZE) { + unsigned long sz = PAGE_SIZE - realp; + if (sz > count) + sz = count; + /* Hmm. Do something? */ + buf += sz; + p += sz; + realp += sz; + count -= sz; + written += sz; + } +#endif + + 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); + + /* + * On ia64 if a page has been mapped somewhere as + * uncached, then it must also be accessed uncached + * by the kernel or data corruption may occur + */ + ptr = xlate_dev_kmem_ptr(p); + + copied = copy_from_user(ptr, buf, sz); + if (copied) { + ssize_t ret; + + ret = written + (sz - copied); + if (ret) + return ret; + return -EFAULT; + } + buf += sz; + p += sz; + realp += sz; + count -= sz; + written += sz; + } + + *ppos += written; + return written; +} + + +/* + * This function writes to the *virtual* memory as seen by the kernel. + */ +static ssize_t write_kmem(struct file * file, const char __user * buf, + size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + ssize_t wrote = 0; + ssize_t virtr = 0; + ssize_t written; + char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ + + if (p < (unsigned long) high_memory) { + + wrote = count; + if (count > (unsigned long) high_memory - p) + wrote = (unsigned long) high_memory - p; + + written = do_write_kmem((void*)p, p, buf, wrote, ppos); + if (written != wrote) + return written; + wrote = written; + p += wrote; + buf += wrote; + count -= wrote; + } + + if (count > 0) { + kbuf = (char *)__get_free_page(GFP_KERNEL); + if (!kbuf) + return wrote ? wrote : -ENOMEM; + while (count > 0) { + int len = count; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + if (len) { + written = copy_from_user(kbuf, buf, len); + if (written) { + ssize_t ret; + + free_page((unsigned long)kbuf); + ret = wrote + virtr + (len - written); + return ret ? ret : -EFAULT; + } + } + len = vwrite(kbuf, (char *)p, len); + count -= len; + buf += len; + virtr += len; + p += len; + } + free_page((unsigned long)kbuf); + } + + *ppos = p; + return virtr + wrote; +} + #if defined(CONFIG_ISA) || !defined(__mc68000__) static ssize_t read_port(struct file * file, char __user * buf, size_t count, loff_t *ppos) @@ -458,11 +563,8 @@ 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 (tmp > buf) - break; + if (__get_user(c, tmp)) return -EFAULT; - } outb(c,i); i++; tmp++; @@ -484,18 +586,6 @@ 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. @@ -683,7 +773,6 @@ static int open_port(struct inode * inode, struct file * filp) #define open_kmem open_mem #define open_oldmem open_mem -#ifndef ARCH_HAS_DEV_MEM static struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, @@ -691,13 +780,11 @@ static struct file_operations mem_fops = { .mmap = mmap_mem, .open = open_mem, }; -#else -extern struct file_operations mem_fops; -#endif static struct file_operations kmem_fops = { .llseek = memory_lseek, .read = read_kmem, + .write = write_kmem, .mmap = mmap_kmem, .open = open_kmem, }; @@ -706,7 +793,6 @@ static 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__) @@ -821,9 +907,10 @@ static const struct { unsigned int minor; char *name; umode_t mode; - const struct file_operations *fops; + 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__) {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},