/*
|
/*
|
* linux/fs/proc/mem.c
|
* linux/fs/proc/mem.c
|
*
|
*
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*/
|
*/
|
|
|
/*
|
/*
|
* uClinux revisions for NO_MM
|
* uClinux revisions for NO_MM
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* The Silver Hammer Group, Ltd.
|
* The Silver Hammer Group, Ltd.
|
*/
|
*/
|
|
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
|
|
#include <asm/page.h>
|
#include <asm/page.h>
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
|
|
#ifndef NO_MM
|
#ifndef NO_MM
|
|
|
/*
|
/*
|
* mem_write isn't really a good idea right now. It needs
|
* 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
|
* to check a lot more: if the process we try to write to
|
* dies in the middle right now, mem_write will overwrite
|
* dies in the middle right now, mem_write will overwrite
|
* kernel memory.. This disables it altogether.
|
* kernel memory.. This disables it altogether.
|
*/
|
*/
|
#define mem_write NULL
|
#define mem_write NULL
|
|
|
static int check_range(struct mm_struct * mm, unsigned long addr, int count)
|
static int check_range(struct mm_struct * mm, unsigned long addr, int count)
|
{
|
{
|
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
int retval;
|
int retval;
|
|
|
vma = find_vma(mm, addr);
|
vma = find_vma(mm, addr);
|
if (!vma)
|
if (!vma)
|
return -EACCES;
|
return -EACCES;
|
if (vma->vm_start > addr)
|
if (vma->vm_start > addr)
|
return -EACCES;
|
return -EACCES;
|
if (!(vma->vm_flags & VM_READ))
|
if (!(vma->vm_flags & VM_READ))
|
return -EACCES;
|
return -EACCES;
|
while ((retval = vma->vm_end - addr) < count) {
|
while ((retval = vma->vm_end - addr) < count) {
|
struct vm_area_struct *next = vma->vm_next;
|
struct vm_area_struct *next = vma->vm_next;
|
if (!next)
|
if (!next)
|
break;
|
break;
|
if (vma->vm_end != next->vm_start)
|
if (vma->vm_end != next->vm_start)
|
break;
|
break;
|
if (!(next->vm_flags & VM_READ))
|
if (!(next->vm_flags & VM_READ))
|
break;
|
break;
|
vma = next;
|
vma = next;
|
}
|
}
|
if (retval > count)
|
if (retval > count)
|
retval = count;
|
retval = count;
|
return retval;
|
return retval;
|
}
|
}
|
|
|
static struct task_struct * get_task(int pid)
|
static struct task_struct * get_task(int pid)
|
{
|
{
|
struct task_struct * tsk = current;
|
struct task_struct * tsk = current;
|
|
|
if (pid != tsk->pid) {
|
if (pid != tsk->pid) {
|
int i;
|
int i;
|
tsk = NULL;
|
tsk = NULL;
|
for (i = 1 ; i < NR_TASKS ; i++)
|
for (i = 1 ; i < NR_TASKS ; i++)
|
if (task[i] && task[i]->pid == pid) {
|
if (task[i] && task[i]->pid == pid) {
|
tsk = task[i];
|
tsk = task[i];
|
break;
|
break;
|
}
|
}
|
/*
|
/*
|
* allow accesses only under the same circumstances
|
* allow accesses only under the same circumstances
|
* that we would allow ptrace to work
|
* that we would allow ptrace to work
|
*/
|
*/
|
if (tsk) {
|
if (tsk) {
|
if (!(tsk->flags & PF_PTRACED)
|
if (!(tsk->flags & PF_PTRACED)
|
|| tsk->state != TASK_STOPPED
|
|| tsk->state != TASK_STOPPED
|
|| tsk->p_pptr != current)
|
|| tsk->p_pptr != current)
|
tsk = NULL;
|
tsk = NULL;
|
}
|
}
|
}
|
}
|
return tsk;
|
return tsk;
|
}
|
}
|
|
|
static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
|
static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
|
{
|
{
|
pgd_t *page_dir;
|
pgd_t *page_dir;
|
pmd_t *page_middle;
|
pmd_t *page_middle;
|
pte_t pte;
|
pte_t pte;
|
char * page;
|
char * page;
|
struct task_struct * tsk;
|
struct task_struct * tsk;
|
unsigned long addr;
|
unsigned long addr;
|
char *tmp;
|
char *tmp;
|
int i;
|
int i;
|
|
|
if (count < 0)
|
if (count < 0)
|
return -EINVAL;
|
return -EINVAL;
|
tsk = get_task(inode->i_ino >> 16);
|
tsk = get_task(inode->i_ino >> 16);
|
if (!tsk)
|
if (!tsk)
|
return -ESRCH;
|
return -ESRCH;
|
addr = file->f_pos;
|
addr = file->f_pos;
|
count = check_range(tsk->mm, addr, count);
|
count = check_range(tsk->mm, addr, count);
|
if (count < 0)
|
if (count < 0)
|
return count;
|
return count;
|
tmp = buf;
|
tmp = buf;
|
while (count > 0) {
|
while (count > 0) {
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
break;
|
break;
|
page_dir = pgd_offset(tsk->mm,addr);
|
page_dir = pgd_offset(tsk->mm,addr);
|
if (pgd_none(*page_dir))
|
if (pgd_none(*page_dir))
|
break;
|
break;
|
if (pgd_bad(*page_dir)) {
|
if (pgd_bad(*page_dir)) {
|
printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
|
printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
|
pgd_clear(page_dir);
|
pgd_clear(page_dir);
|
break;
|
break;
|
}
|
}
|
page_middle = pmd_offset(page_dir,addr);
|
page_middle = pmd_offset(page_dir,addr);
|
if (pmd_none(*page_middle))
|
if (pmd_none(*page_middle))
|
break;
|
break;
|
if (pmd_bad(*page_middle)) {
|
if (pmd_bad(*page_middle)) {
|
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
|
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
|
pmd_clear(page_middle);
|
pmd_clear(page_middle);
|
break;
|
break;
|
}
|
}
|
pte = *pte_offset(page_middle,addr);
|
pte = *pte_offset(page_middle,addr);
|
if (!pte_present(pte))
|
if (!pte_present(pte))
|
break;
|
break;
|
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
|
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
|
i = PAGE_SIZE-(addr & ~PAGE_MASK);
|
i = PAGE_SIZE-(addr & ~PAGE_MASK);
|
if (i > count)
|
if (i > count)
|
i = count;
|
i = count;
|
memcpy_tofs(tmp, page, i);
|
memcpy_tofs(tmp, page, i);
|
addr += i;
|
addr += i;
|
tmp += i;
|
tmp += i;
|
count -= i;
|
count -= i;
|
}
|
}
|
file->f_pos = addr;
|
file->f_pos = addr;
|
return tmp-buf;
|
return tmp-buf;
|
}
|
}
|
|
|
#ifndef mem_write
|
#ifndef mem_write
|
|
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
|
{
|
{
|
pgd_t *page_dir;
|
pgd_t *page_dir;
|
pmd_t *page_middle;
|
pmd_t *page_middle;
|
pte_t pte;
|
pte_t pte;
|
char * page;
|
char * page;
|
struct task_struct * tsk;
|
struct task_struct * tsk;
|
unsigned long addr;
|
unsigned long addr;
|
char *tmp;
|
char *tmp;
|
int i;
|
int i;
|
|
|
if (count < 0)
|
if (count < 0)
|
return -EINVAL;
|
return -EINVAL;
|
addr = file->f_pos;
|
addr = file->f_pos;
|
tsk = get_task(inode->i_ino >> 16);
|
tsk = get_task(inode->i_ino >> 16);
|
if (!tsk)
|
if (!tsk)
|
return -ESRCH;
|
return -ESRCH;
|
tmp = buf;
|
tmp = buf;
|
while (count > 0) {
|
while (count > 0) {
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
break;
|
break;
|
page_dir = pgd_offset(tsk,addr);
|
page_dir = pgd_offset(tsk,addr);
|
if (pgd_none(*page_dir))
|
if (pgd_none(*page_dir))
|
break;
|
break;
|
if (pgd_bad(*page_dir)) {
|
if (pgd_bad(*page_dir)) {
|
printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
|
printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
|
pgd_clear(page_dir);
|
pgd_clear(page_dir);
|
break;
|
break;
|
}
|
}
|
page_middle = pmd_offset(page_dir,addr);
|
page_middle = pmd_offset(page_dir,addr);
|
if (pmd_none(*page_middle))
|
if (pmd_none(*page_middle))
|
break;
|
break;
|
if (pmd_bad(*page_middle)) {
|
if (pmd_bad(*page_middle)) {
|
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
|
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
|
pmd_clear(page_middle);
|
pmd_clear(page_middle);
|
break;
|
break;
|
}
|
}
|
pte = *pte_offset(page_middle,addr);
|
pte = *pte_offset(page_middle,addr);
|
if (!pte_present(pte))
|
if (!pte_present(pte))
|
break;
|
break;
|
if (!pte_write(pte))
|
if (!pte_write(pte))
|
break;
|
break;
|
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
|
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
|
i = PAGE_SIZE-(addr & ~PAGE_MASK);
|
i = PAGE_SIZE-(addr & ~PAGE_MASK);
|
if (i > count)
|
if (i > count)
|
i = count;
|
i = count;
|
memcpy_fromfs(page, tmp, i);
|
memcpy_fromfs(page, tmp, i);
|
addr += i;
|
addr += i;
|
tmp += i;
|
tmp += i;
|
count -= i;
|
count -= i;
|
}
|
}
|
file->f_pos = addr;
|
file->f_pos = addr;
|
if (tmp != buf)
|
if (tmp != buf)
|
return tmp-buf;
|
return tmp-buf;
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
|
static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
|
{
|
{
|
switch (orig) {
|
switch (orig) {
|
case 0:
|
case 0:
|
file->f_pos = offset;
|
file->f_pos = offset;
|
return file->f_pos;
|
return file->f_pos;
|
case 1:
|
case 1:
|
file->f_pos += offset;
|
file->f_pos += offset;
|
return file->f_pos;
|
return file->f_pos;
|
default:
|
default:
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* This isn't really reliable by any means..
|
* This isn't really reliable by any means..
|
*/
|
*/
|
int mem_mmap(struct inode * inode, struct file * file,
|
int mem_mmap(struct inode * inode, struct file * file,
|
struct vm_area_struct * vma)
|
struct vm_area_struct * vma)
|
{
|
{
|
struct task_struct *tsk;
|
struct task_struct *tsk;
|
pgd_t *src_dir, *dest_dir;
|
pgd_t *src_dir, *dest_dir;
|
pmd_t *src_middle, *dest_middle;
|
pmd_t *src_middle, *dest_middle;
|
pte_t *src_table, *dest_table;
|
pte_t *src_table, *dest_table;
|
unsigned long stmp, dtmp, mapnr;
|
unsigned long stmp, dtmp, mapnr;
|
struct vm_area_struct *src_vma = NULL;
|
struct vm_area_struct *src_vma = NULL;
|
|
|
/* Get the source's task information */
|
/* Get the source's task information */
|
|
|
tsk = get_task(inode->i_ino >> 16);
|
tsk = get_task(inode->i_ino >> 16);
|
|
|
if (!tsk)
|
if (!tsk)
|
return -ESRCH;
|
return -ESRCH;
|
|
|
/* Ensure that we have a valid source area. (Has to be mmap'ed and
|
/* 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
|
have valid page information.) We can't map shared memory at the
|
moment because working out the vm_area_struct & nattach stuff isn't
|
moment because working out the vm_area_struct & nattach stuff isn't
|
worth it. */
|
worth it. */
|
|
|
src_vma = tsk->mm->mmap;
|
src_vma = tsk->mm->mmap;
|
stmp = vma->vm_offset;
|
stmp = vma->vm_offset;
|
while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {
|
while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {
|
while (src_vma && stmp > src_vma->vm_end)
|
while (src_vma && stmp > src_vma->vm_end)
|
src_vma = src_vma->vm_next;
|
src_vma = src_vma->vm_next;
|
if (!src_vma || (src_vma->vm_flags & VM_SHM))
|
if (!src_vma || (src_vma->vm_flags & VM_SHM))
|
return -EINVAL;
|
return -EINVAL;
|
|
|
src_dir = pgd_offset(tsk->mm, stmp);
|
src_dir = pgd_offset(tsk->mm, stmp);
|
if (pgd_none(*src_dir))
|
if (pgd_none(*src_dir))
|
return -EINVAL;
|
return -EINVAL;
|
if (pgd_bad(*src_dir)) {
|
if (pgd_bad(*src_dir)) {
|
printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir));
|
printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir));
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
src_middle = pmd_offset(src_dir, stmp);
|
src_middle = pmd_offset(src_dir, stmp);
|
if (pmd_none(*src_middle))
|
if (pmd_none(*src_middle))
|
return -EINVAL;
|
return -EINVAL;
|
if (pmd_bad(*src_middle)) {
|
if (pmd_bad(*src_middle)) {
|
printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle));
|
printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle));
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
src_table = pte_offset(src_middle, stmp);
|
src_table = pte_offset(src_middle, stmp);
|
if (pte_none(*src_table))
|
if (pte_none(*src_table))
|
return -EINVAL;
|
return -EINVAL;
|
|
|
if (stmp < src_vma->vm_start) {
|
if (stmp < src_vma->vm_start) {
|
if (!(src_vma->vm_flags & VM_GROWSDOWN))
|
if (!(src_vma->vm_flags & VM_GROWSDOWN))
|
return -EINVAL;
|
return -EINVAL;
|
if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)
|
if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
stmp += PAGE_SIZE;
|
stmp += PAGE_SIZE;
|
}
|
}
|
|
|
src_vma = tsk->mm->mmap;
|
src_vma = tsk->mm->mmap;
|
stmp = vma->vm_offset;
|
stmp = vma->vm_offset;
|
dtmp = vma->vm_start;
|
dtmp = vma->vm_start;
|
|
|
flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end);
|
flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end);
|
flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
|
flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
|
while (dtmp < vma->vm_end) {
|
while (dtmp < vma->vm_end) {
|
while (src_vma && stmp > src_vma->vm_end)
|
while (src_vma && stmp > src_vma->vm_end)
|
src_vma = src_vma->vm_next;
|
src_vma = src_vma->vm_next;
|
|
|
src_dir = pgd_offset(tsk->mm, stmp);
|
src_dir = pgd_offset(tsk->mm, stmp);
|
src_middle = pmd_offset(src_dir, stmp);
|
src_middle = pmd_offset(src_dir, stmp);
|
src_table = pte_offset(src_middle, stmp);
|
src_table = pte_offset(src_middle, stmp);
|
|
|
dest_dir = pgd_offset(current->mm, dtmp);
|
dest_dir = pgd_offset(current->mm, dtmp);
|
dest_middle = pmd_alloc(dest_dir, dtmp);
|
dest_middle = pmd_alloc(dest_dir, dtmp);
|
if (!dest_middle)
|
if (!dest_middle)
|
return -ENOMEM;
|
return -ENOMEM;
|
dest_table = pte_alloc(dest_middle, dtmp);
|
dest_table = pte_alloc(dest_middle, dtmp);
|
if (!dest_table)
|
if (!dest_table)
|
return -ENOMEM;
|
return -ENOMEM;
|
|
|
if (!pte_present(*src_table))
|
if (!pte_present(*src_table))
|
do_no_page(tsk, src_vma, stmp, 1);
|
do_no_page(tsk, src_vma, stmp, 1);
|
|
|
if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
|
if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
|
do_wp_page(tsk, src_vma, stmp, 1);
|
do_wp_page(tsk, src_vma, stmp, 1);
|
|
|
set_pte(src_table, pte_mkdirty(*src_table));
|
set_pte(src_table, pte_mkdirty(*src_table));
|
set_pte(dest_table, *src_table);
|
set_pte(dest_table, *src_table);
|
mapnr = MAP_NR(pte_page(*src_table));
|
mapnr = MAP_NR(pte_page(*src_table));
|
if (mapnr < MAP_NR(high_memory))
|
if (mapnr < MAP_NR(high_memory))
|
mem_map[mapnr].count++;
|
mem_map[mapnr].count++;
|
|
|
stmp += PAGE_SIZE;
|
stmp += PAGE_SIZE;
|
dtmp += PAGE_SIZE;
|
dtmp += PAGE_SIZE;
|
}
|
}
|
|
|
flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end);
|
flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end);
|
flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
|
flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static struct file_operations proc_mem_operations = {
|
static struct file_operations proc_mem_operations = {
|
mem_lseek,
|
mem_lseek,
|
mem_read,
|
mem_read,
|
mem_write,
|
mem_write,
|
NULL, /* mem_readdir */
|
NULL, /* mem_readdir */
|
NULL, /* mem_select */
|
NULL, /* mem_select */
|
NULL, /* mem_ioctl */
|
NULL, /* mem_ioctl */
|
mem_mmap, /* mmap */
|
mem_mmap, /* mmap */
|
NULL, /* no special open code */
|
NULL, /* no special open code */
|
NULL, /* no special release code */
|
NULL, /* no special release code */
|
NULL /* can't fsync */
|
NULL /* can't fsync */
|
};
|
};
|
|
|
struct inode_operations proc_mem_inode_operations = {
|
struct inode_operations proc_mem_inode_operations = {
|
&proc_mem_operations, /* default base directory file-ops */
|
&proc_mem_operations, /* default base directory file-ops */
|
NULL, /* create */
|
NULL, /* create */
|
NULL, /* lookup */
|
NULL, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL /* permission */
|
NULL /* permission */
|
};
|
};
|
|
|
#else /* NO_MM */
|
#else /* NO_MM */
|
|
|
/*
|
/*
|
* mem_write isn't really a good idea right now. It needs
|
* 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
|
* to check a lot more: if the process we try to write to
|
* dies in the middle right now, mem_write will overwrite
|
* dies in the middle right now, mem_write will overwrite
|
* kernel memory.. This disables it altogether.
|
* kernel memory.. This disables it altogether.
|
*/
|
*/
|
#define mem_write NULL
|
#define mem_write NULL
|
|
|
static struct task_struct * get_task(int pid)
|
static struct task_struct * get_task(int pid)
|
{
|
{
|
struct task_struct * tsk = current;
|
struct task_struct * tsk = current;
|
|
|
if (pid != tsk->pid) {
|
if (pid != tsk->pid) {
|
int i;
|
int i;
|
tsk = NULL;
|
tsk = NULL;
|
for (i = 1 ; i < NR_TASKS ; i++)
|
for (i = 1 ; i < NR_TASKS ; i++)
|
if (task[i] && task[i]->pid == pid) {
|
if (task[i] && task[i]->pid == pid) {
|
tsk = task[i];
|
tsk = task[i];
|
break;
|
break;
|
}
|
}
|
/*
|
/*
|
* allow accesses only under the same circumstances
|
* allow accesses only under the same circumstances
|
* that we would allow ptrace to work
|
* that we would allow ptrace to work
|
*/
|
*/
|
if (tsk) {
|
if (tsk) {
|
if (!(tsk->flags & PF_PTRACED)
|
if (!(tsk->flags & PF_PTRACED)
|
|| tsk->state != TASK_STOPPED
|
|| tsk->state != TASK_STOPPED
|
|| tsk->p_pptr != current)
|
|| tsk->p_pptr != current)
|
tsk = NULL;
|
tsk = NULL;
|
}
|
}
|
}
|
}
|
return tsk;
|
return tsk;
|
}
|
}
|
|
|
static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
|
static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
|
{
|
{
|
struct task_struct * tsk;
|
struct task_struct * tsk;
|
unsigned long addr;
|
unsigned long addr;
|
char *tmp;
|
char *tmp;
|
int i;
|
int i;
|
|
|
if (count < 0)
|
if (count < 0)
|
return -EINVAL;
|
return -EINVAL;
|
tsk = get_task(inode->i_ino >> 16);
|
tsk = get_task(inode->i_ino >> 16);
|
if (!tsk)
|
if (!tsk)
|
return -ESRCH;
|
return -ESRCH;
|
addr = file->f_pos;
|
addr = file->f_pos;
|
tmp = buf;
|
tmp = buf;
|
while (count > 0) {
|
while (count > 0) {
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
break;
|
break;
|
i = count;
|
i = count;
|
memcpy_tofs(tmp, (void*)addr, i);
|
memcpy_tofs(tmp, (void*)addr, i);
|
addr += i;
|
addr += i;
|
tmp += i;
|
tmp += i;
|
count -= i;
|
count -= i;
|
}
|
}
|
file->f_pos = addr;
|
file->f_pos = addr;
|
return tmp-buf;
|
return tmp-buf;
|
}
|
}
|
|
|
#ifndef mem_write
|
#ifndef mem_write
|
|
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
|
{
|
{
|
struct task_struct * tsk;
|
struct task_struct * tsk;
|
unsigned long addr;
|
unsigned long addr;
|
char *tmp;
|
char *tmp;
|
int i;
|
int i;
|
|
|
if (count < 0)
|
if (count < 0)
|
return -EINVAL;
|
return -EINVAL;
|
addr = file->f_pos;
|
addr = file->f_pos;
|
tsk = get_task(inode->i_ino >> 16);
|
tsk = get_task(inode->i_ino >> 16);
|
if (!tsk)
|
if (!tsk)
|
return -ESRCH;
|
return -ESRCH;
|
tmp = buf;
|
tmp = buf;
|
while (count > 0) {
|
while (count > 0) {
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
break;
|
break;
|
i = count;
|
i = count;
|
memcpy_fromfs((void*)addr, tmp, i);
|
memcpy_fromfs((void*)addr, tmp, i);
|
addr += i;
|
addr += i;
|
tmp += i;
|
tmp += i;
|
count -= i;
|
count -= i;
|
}
|
}
|
file->f_pos = addr;
|
file->f_pos = addr;
|
if (tmp != buf)
|
if (tmp != buf)
|
return tmp-buf;
|
return tmp-buf;
|
if (current->signal & ~current->blocked)
|
if (current->signal & ~current->blocked)
|
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
int mem_mmap(struct inode * inode, struct file * file,
|
int mem_mmap(struct inode * inode, struct file * file,
|
struct vm_area_struct * vma)
|
struct vm_area_struct * vma)
|
{
|
{
|
vma->vm_start = vma->vm_offset;
|
vma->vm_start = vma->vm_offset;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
|
static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
|
{
|
{
|
switch (orig) {
|
switch (orig) {
|
case 0:
|
case 0:
|
file->f_pos = offset;
|
file->f_pos = offset;
|
return file->f_pos;
|
return file->f_pos;
|
case 1:
|
case 1:
|
file->f_pos += offset;
|
file->f_pos += offset;
|
return file->f_pos;
|
return file->f_pos;
|
default:
|
default:
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
}
|
}
|
|
|
static struct file_operations proc_mem_operations = {
|
static struct file_operations proc_mem_operations = {
|
mem_lseek,
|
mem_lseek,
|
mem_read,
|
mem_read,
|
mem_write,
|
mem_write,
|
NULL, /* mem_readdir */
|
NULL, /* mem_readdir */
|
NULL, /* mem_select */
|
NULL, /* mem_select */
|
NULL, /* mem_ioctl */
|
NULL, /* mem_ioctl */
|
mem_mmap, /* mmap */
|
mem_mmap, /* mmap */
|
NULL, /* no special open code */
|
NULL, /* no special open code */
|
NULL, /* no special release code */
|
NULL, /* no special release code */
|
NULL /* can't fsync */
|
NULL /* can't fsync */
|
};
|
};
|
|
|
struct inode_operations proc_mem_inode_operations = {
|
struct inode_operations proc_mem_inode_operations = {
|
&proc_mem_operations, /* default base directory file-ops */
|
&proc_mem_operations, /* default base directory file-ops */
|
NULL, /* create */
|
NULL, /* create */
|
NULL, /* lookup */
|
NULL, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL /* permission */
|
NULL /* permission */
|
};
|
};
|
|
|
#endif /* NO_MM */
|
#endif /* NO_MM */
|
|
|