Commit a3245879 authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[PATCH] Support /dev/kmem access to vmalloc space (Marc Boucher)

From 2.4.17
parent 16d6f850
......@@ -273,6 +273,8 @@ static ssize_t read_kmem(struct file *file, char *buf,
return virtr + read;
}
extern long vwrite(char *buf, char *addr, unsigned long count);
/*
* This function writes to the *virtual* memory as seen by the kernel.
*/
......@@ -280,12 +282,46 @@ static ssize_t write_kmem(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
ssize_t wrote = 0;
ssize_t virtr = 0;
char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
if (p >= (unsigned long) high_memory)
return 0;
if (count > (unsigned long) high_memory - p)
count = (unsigned long) high_memory - p;
return do_write_mem(file, (void*)p, p, buf, count, ppos);
if (p < (unsigned long) high_memory) {
wrote = count;
if (count > (unsigned long) high_memory - p)
wrote = (unsigned long) high_memory - p;
wrote = do_write_mem(file, (void*)p, p, buf, wrote, ppos);
p += wrote;
buf += wrote;
count -= wrote;
}
if (count > 0) {
kbuf = (char *)__get_free_page(GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
while (count > 0) {
int len = count;
if (len > PAGE_SIZE)
len = PAGE_SIZE;
if (len && copy_from_user(kbuf, buf, len)) {
free_page((unsigned long)kbuf);
return -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(__mc68000__)
......
......@@ -6,6 +6,7 @@
* SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000
*/
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/spinlock.h>
......@@ -163,6 +164,7 @@ inline int vmalloc_area_pages (unsigned long address, unsigned long size,
ret = 0;
} while (address && (address < end));
spin_unlock(&init_mm.page_table_lock);
flush_cache_all();
return ret;
}
......@@ -282,3 +284,40 @@ long vread(char *buf, char *addr, unsigned long count)
read_unlock(&vmlist_lock);
return buf - buf_start;
}
long vwrite(char *buf, char *addr, unsigned long count)
{
struct vm_struct *tmp;
char *vaddr, *buf_start = buf;
unsigned long n;
/* Don't allow overflow */
if ((unsigned long) addr + count < count)
count = -(unsigned long) addr;
read_lock(&vmlist_lock);
for (tmp = vmlist; tmp; tmp = tmp->next) {
vaddr = (char *) tmp->addr;
if (addr >= vaddr + tmp->size - PAGE_SIZE)
continue;
while (addr < vaddr) {
if (count == 0)
goto finished;
buf++;
addr++;
count--;
}
n = vaddr + tmp->size - PAGE_SIZE - addr;
do {
if (count == 0)
goto finished;
*addr = *buf;
buf++;
addr++;
count--;
} while (--n > 0);
}
finished:
read_unlock(&vmlist_lock);
return buf - buf_start;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment