diff options
Diffstat (limited to 'fs/proc/mem.c')
-rw-r--r-- | fs/proc/mem.c | 241 |
1 files changed, 142 insertions, 99 deletions
diff --git a/fs/proc/mem.c b/fs/proc/mem.c index ae043bb0a..f44bd7fbc 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -13,6 +13,7 @@ #include <asm/page.h> #include <asm/segment.h> #include <asm/io.h> +#include <asm/pgtable.h> /* * mem_write isn't really a good idea right now. It needs @@ -24,42 +25,56 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int count) { - unsigned long addr, pid, cr3; + pgd_t *page_dir; + pmd_t *page_middle; + pte_t pte; + char * page; + struct task_struct * tsk; + unsigned long addr, pid; char *tmp; - unsigned long pte, page; int i; if (count < 0) return -EINVAL; pid = inode->i_ino; pid >>= 16; - cr3 = 0; + tsk = NULL; for (i = 1 ; i < NR_TASKS ; i++) if (task[i] && task[i]->pid == pid) { - cr3 = task[i]->tss.cr3; + tsk = task[i]; break; } - if (!cr3) + if (!tsk) 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)) + page_dir = pgd_offset(tsk,addr); + if (pgd_none(*page_dir)) break; - pte &= PAGE_MASK; - pte += PAGE_PTR(addr); - page = *(unsigned long *) pte; - if (!(page & 1)) + if (pgd_bad(*page_dir)) { + printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_clear(page_dir); break; - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; + } + page_middle = pmd_offset(page_dir,addr); + if (pmd_none(*page_middle)) + break; + if (pmd_bad(*page_middle)) { + printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_clear(page_middle); + break; + } + pte = *pte_offset(page_middle,addr); + if (!pte_present(pte)) + break; + page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - memcpy_tofs(tmp,(void *) page,i); + memcpy_tofs(tmp, page, i); addr += i; tmp += i; count -= i; @@ -72,9 +87,13 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou static int mem_write(struct inode * inode, struct file * file,char * buf, int count) { - unsigned long addr, pid, cr3; + pgd_t *page_dir; + pmd_t *page_middle; + pte_t pte; + char * page; + struct task_struct * tsk; + unsigned long addr, pid; char *tmp; - unsigned long pte, page; int i; if (count < 0) @@ -82,36 +101,44 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co addr = file->f_pos; pid = inode->i_ino; pid >>= 16; - cr3 = 0; + tsk = NULL; for (i = 1 ; i < NR_TASKS ; i++) if (task[i] && task[i]->pid == pid) { - cr3 = task[i]->tss.cr3; + tsk = task[i]; break; } - if (!cr3) + if (!tsk) return -EACCES; tmp = buf; while (count > 0) { if (current->signal & ~current->blocked) break; - pte = *PAGE_DIR_OFFSET(cr3,addr); - if (!(pte & PAGE_PRESENT)) + page_dir = pgd_offset(tsk,addr); + if (pgd_none(*page_dir)) + break; + if (pgd_bad(*page_dir)) { + printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_clear(page_dir); + break; + } + page_middle = pmd_offset(page_dir,addr); + if (pmd_none(*page_middle)) break; - pte &= PAGE_MASK; - pte += PAGE_PTR(addr); - page = *(unsigned long *) pte; - if (!(page & PAGE_PRESENT)) + if (pmd_bad(*page_middle)) { + printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_clear(page_middle); break; - if (!(page & 2)) { - do_wp_page(0,addr,current,0); - continue; } - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; + pte = *pte_offset(page_middle,addr); + if (!pte_present(pte)) + break; + if (!pte_write(pte)) + break; + page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - memcpy_fromfs((void *) page,tmp,i); + memcpy_fromfs(page, tmp, i); addr += i; tmp += i; count -= i; @@ -140,92 +167,108 @@ static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int } } -int -mem_mmap(struct inode * inode, struct file * file, +/* + * This isn't really reliable by any means.. + */ +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 */ + struct task_struct *tsk; + pgd_t *src_dir, *dest_dir; + pmd_t *src_middle, *dest_middle; + pte_t *src_table, *dest_table; + unsigned long stmp, dtmp; + struct vm_area_struct *src_vma = NULL; + int i; - 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; - } + /* Get the source's task information */ - if (!cr3) - return -EACCES; + tsk = NULL; + for (i = 1 ; i < NR_TASKS ; i++) + if (task[i] && task[i]->pid == (inode->i_ino >> 16)) { + tsk = task[i]; + break; + } -/* 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. */ + if (!tsk) + return -EACCES; - 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; + /* 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. */ - 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; + src_vma = tsk->mm->mmap; + 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; - 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_dir = pgd_offset(tsk, stmp); + if (pgd_none(*src_dir)) + return -EINVAL; + if (pgd_bad(*src_dir)) { + printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir)); + return -EINVAL; + } + src_middle = pmd_offset(src_dir, stmp); + if (pmd_none(*src_middle)) + return -EINVAL; + if (pmd_bad(*src_middle)) { + printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle)); + return -EINVAL; + } + src_table = pte_offset(src_middle, stmp); + if (pte_none(*src_table)) + return -EINVAL; - src_vma = task[i]->mm->mmap; - stmp = vma->vm_offset; - dtmp = vma->vm_start; + 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; + } - while (dtmp < vma->vm_end) { - while (src_vma && stmp > src_vma->vm_end) - src_vma = src_vma->vm_next; + src_vma = tsk->mm->mmap; + stmp = vma->vm_offset; + dtmp = vma->vm_start; - src_table = PAGE_DIR_OFFSET(cr3, stmp); - src_table = (unsigned long *)((*src_table & PAGE_MASK) + PAGE_PTR(stmp)); + while (dtmp < vma->vm_end) { + while (src_vma && stmp > src_vma->vm_end) + src_vma = src_vma->vm_next; - dest_table = PAGE_DIR_OFFSET(current->tss.cr3, dtmp); + src_dir = pgd_offset(tsk, stmp); + src_middle = pmd_offset(src_dir, stmp); + src_table = pte_offset(src_middle, stmp); - 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)); + dest_dir = pgd_offset(current, dtmp); + dest_middle = pmd_alloc(dest_dir, dtmp); + if (!dest_middle) + return -ENOMEM; + dest_table = pte_alloc(dest_middle, dtmp); + if (!dest_table) + return -ENOMEM; - if (!(*src_table & PAGE_PRESENT)) - do_no_page(src_vma, stmp, PAGE_PRESENT); + if (!pte_present(*src_table)) + do_no_page(src_vma, stmp, 1); - if ((vma->vm_flags & VM_WRITE) && !(*src_table & PAGE_RW)) - do_wp_page(src_vma, stmp, PAGE_RW | PAGE_PRESENT); + if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table)) + do_wp_page(src_vma, stmp, 1); - *src_table |= PAGE_DIRTY; - *dest_table = *src_table; - mem_map[MAP_NR(*src_table)]++; + *src_table = pte_mkdirty(*src_table); + *dest_table = *src_table; + mem_map[MAP_NR(pte_page(*src_table))]++; - stmp += PAGE_SIZE; - dtmp += PAGE_SIZE; - } + stmp += PAGE_SIZE; + dtmp += PAGE_SIZE; + } - invalidate(); - return 0; + invalidate(); + return 0; } static struct file_operations proc_mem_operations = { |