#include <linux/crash_dump.h>
#include <linux/backing-dev.h>
#include <linux/bootmem.h>
-#include <linux/pipe_fs_i.h>
#include <asm/uaccess.h>
#include <asm/io.h>
# include <linux/efi.h>
#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.
}
#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.
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
*/
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;
unsigned long copied;
void *ptr;
- if (!valid_phys_addr_range(p, count))
+ if (!valid_phys_addr_range(p, &count))
return -EFAULT;
written = 0;
*/
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;
*ppos += written;
return written;
}
-#endif
#ifndef __HAVE_PHYS_MEM_ACCESS_PROT
static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
{
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,
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;
}
+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)
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++;
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.
#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,
.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,
};
.llseek = null_lseek,
.read = read_null,
.write = write_null,
- .splice_write = splice_write_null,
};
#if defined(CONFIG_ISA) || !defined(__mc68000__)
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},