summaryrefslogtreecommitdiffstats
path: root/fs/proc/mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/proc/mem.c')
-rw-r--r--fs/proc/mem.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
new file mode 100644
index 000000000..ae043bb0a
--- /dev/null
+++ b/fs/proc/mem.c
@@ -0,0 +1,260 @@
+/*
+ * linux/fs/proc/mem.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+/*
+ * mem_write isn't really a good idea right now. It needs
+ * to check a lot more: if the process we try to write to
+ * dies in the middle right now, mem_write will overwrite
+ * kernel memory.. This disables it altogether.
+ */
+#define mem_write NULL
+
+static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
+{
+ unsigned long addr, pid, cr3;
+ char *tmp;
+ unsigned long pte, page;
+ int i;
+
+ if (count < 0)
+ return -EINVAL;
+ pid = inode->i_ino;
+ pid >>= 16;
+ cr3 = 0;
+ for (i = 1 ; i < NR_TASKS ; i++)
+ if (task[i] && task[i]->pid == pid) {
+ cr3 = task[i]->tss.cr3;
+ break;
+ }
+ if (!cr3)
+ return -EACCES;
+ addr = file->f_pos;
+ tmp = buf;
+ while (count > 0) {
+ if (current->signal & ~current->blocked)
+ break;
+ pte = *PAGE_DIR_OFFSET(cr3,addr);
+ if (!(pte & PAGE_PRESENT))
+ break;
+ pte &= PAGE_MASK;
+ pte += PAGE_PTR(addr);
+ page = *(unsigned long *) pte;
+ if (!(page & 1))
+ break;
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
+ i = PAGE_SIZE-(addr & ~PAGE_MASK);
+ if (i > count)
+ i = count;
+ memcpy_tofs(tmp,(void *) page,i);
+ addr += i;
+ tmp += i;
+ count -= i;
+ }
+ file->f_pos = addr;
+ return tmp-buf;
+}
+
+#ifndef mem_write
+
+static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
+{
+ unsigned long addr, pid, cr3;
+ char *tmp;
+ unsigned long pte, page;
+ int i;
+
+ if (count < 0)
+ return -EINVAL;
+ addr = file->f_pos;
+ pid = inode->i_ino;
+ pid >>= 16;
+ cr3 = 0;
+ for (i = 1 ; i < NR_TASKS ; i++)
+ if (task[i] && task[i]->pid == pid) {
+ cr3 = task[i]->tss.cr3;
+ break;
+ }
+ if (!cr3)
+ return -EACCES;
+ tmp = buf;
+ while (count > 0) {
+ if (current->signal & ~current->blocked)
+ break;
+ pte = *PAGE_DIR_OFFSET(cr3,addr);
+ if (!(pte & PAGE_PRESENT))
+ break;
+ pte &= PAGE_MASK;
+ pte += PAGE_PTR(addr);
+ page = *(unsigned long *) pte;
+ if (!(page & PAGE_PRESENT))
+ break;
+ if (!(page & 2)) {
+ do_wp_page(0,addr,current,0);
+ continue;
+ }
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
+ i = PAGE_SIZE-(addr & ~PAGE_MASK);
+ if (i > count)
+ i = count;
+ memcpy_fromfs((void *) page,tmp,i);
+ addr += i;
+ tmp += i;
+ count -= i;
+ }
+ file->f_pos = addr;
+ if (tmp != buf)
+ return tmp-buf;
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ return 0;
+}
+
+#endif
+
+static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ return file->f_pos;
+ case 1:
+ file->f_pos += offset;
+ return file->f_pos;
+ default:
+ return -EINVAL;
+ }
+}
+
+int
+mem_mmap(struct inode * inode, struct file * file,
+ struct vm_area_struct * vma)
+{
+ unsigned long *src_table, *dest_table, stmp, dtmp, cr3;
+ struct vm_area_struct *src_vma = 0;
+ int i;
+
+ /* Get the source's task information */
+
+ cr3 = 0;
+ for (i = 1 ; i < NR_TASKS ; i++)
+ if (task[i] && task[i]->pid == (inode->i_ino >> 16)) {
+ cr3 = task[i]->tss.cr3;
+ src_vma = task[i]->mm->mmap;
+ break;
+ }
+
+ if (!cr3)
+ return -EACCES;
+
+/* Ensure that we have a valid source area. (Has to be mmap'ed and
+ have valid page information.) We can't map shared memory at the
+ moment because working out the vm_area_struct & nattach stuff isn't
+ worth it. */
+
+ stmp = vma->vm_offset;
+ while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {
+ while (src_vma && stmp > src_vma->vm_end)
+ src_vma = src_vma->vm_next;
+ if (!src_vma || (src_vma->vm_flags & VM_SHM))
+ return -EINVAL;
+
+ src_table = PAGE_DIR_OFFSET(cr3, stmp);
+ if (!*src_table)
+ return -EINVAL;
+ src_table = (unsigned long *)((*src_table & PAGE_MASK) + PAGE_PTR(stmp));
+ if (!*src_table)
+ return -EINVAL;
+
+ if (stmp < src_vma->vm_start) {
+ if (!(src_vma->vm_flags & VM_GROWSDOWN))
+ return -EINVAL;
+ if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)
+ return -EINVAL;
+ }
+ stmp += PAGE_SIZE;
+ }
+
+ src_vma = task[i]->mm->mmap;
+ stmp = vma->vm_offset;
+ dtmp = vma->vm_start;
+
+ while (dtmp < vma->vm_end) {
+ while (src_vma && stmp > src_vma->vm_end)
+ src_vma = src_vma->vm_next;
+
+ src_table = PAGE_DIR_OFFSET(cr3, stmp);
+ src_table = (unsigned long *)((*src_table & PAGE_MASK) + PAGE_PTR(stmp));
+
+ dest_table = PAGE_DIR_OFFSET(current->tss.cr3, dtmp);
+
+ if (!*dest_table) {
+ *dest_table = get_free_page(GFP_KERNEL);
+ if (!*dest_table) { oom(current); *dest_table=BAD_PAGE; }
+ else *dest_table |= PAGE_TABLE;
+ }
+
+ dest_table = (unsigned long *)((*dest_table & PAGE_MASK) + PAGE_PTR(dtmp));
+
+ if (!(*src_table & PAGE_PRESENT))
+ do_no_page(src_vma, stmp, PAGE_PRESENT);
+
+ if ((vma->vm_flags & VM_WRITE) && !(*src_table & PAGE_RW))
+ do_wp_page(src_vma, stmp, PAGE_RW | PAGE_PRESENT);
+
+ *src_table |= PAGE_DIRTY;
+ *dest_table = *src_table;
+ mem_map[MAP_NR(*src_table)]++;
+
+ stmp += PAGE_SIZE;
+ dtmp += PAGE_SIZE;
+ }
+
+ invalidate();
+ return 0;
+}
+
+static struct file_operations proc_mem_operations = {
+ mem_lseek,
+ mem_read,
+ mem_write,
+ NULL, /* mem_readdir */
+ NULL, /* mem_select */
+ NULL, /* mem_ioctl */
+ mem_mmap, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+struct inode_operations proc_mem_inode_operations = {
+ &proc_mem_operations, /* default base directory file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};