OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [mmnommu/] [mmap.c] - Rev 1777

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 *	linux/mm/mmap.c
 *
 * Written by obz.
 */
 
/*
 * uClinux revisions for NO_MM
 * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
 *                     The Silver Hammer Group, Ltd.
 * Copyright (C) 1999  D. Jeff Dionne <jeff@uclinux.org>,
 *                     Rt-Control, Inc.
 */  
 
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/pgtable.h>
 
/* If defined, warn whenever someone mmaps a block that has more then this
   number of bytes in slack space (due to kmalloc granularity). */
 
/*#define WARN_ON_SLACK 1024*/
 
/*
 * Combine the mmap "prot" and "flags" argument into one "vm_flags" used
 * internally. Essentially, translate the "PROT_xxx" and "MAP_xxx" bits
 * into "VM_xxx".
 */
static inline unsigned long vm_flags(unsigned long prot, unsigned long flags)
{
#define _trans(x,bit1,bit2) \
((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
 
	unsigned long prot_bits, flag_bits;
	prot_bits =
		_trans(prot, PROT_READ, VM_READ) |
		_trans(prot, PROT_WRITE, VM_WRITE) |
		_trans(prot, PROT_EXEC, VM_EXEC);
	flag_bits =
		_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN) |
		_trans(flags, MAP_DENYWRITE, VM_DENYWRITE) |
		_trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE);
	return prot_bits | flag_bits;
#undef _trans
}
 
asmlinkage unsigned long sys_brk(unsigned long brk)
{
	return -ENOSYS;
}
 
#ifdef DEBUG_MMAP
 
#undef do_mmap
unsigned long do_mmap_flf(struct file * file, unsigned long addr, unsigned long len,
	unsigned long prot, unsigned long flags, unsigned long off, char*filename, int line, char*function)
{
	unsigned long result;
	printk("%s @%s:%d: do_mmap by pid %d of %lu", function, filename, line, current->pid, len);
 
	result=do_mmap(file,addr,len,prot,flags,off);
 
	if (result >= -4096) /* so to speak... */
		printk(" = %lx\n", result);
	else
	if (is_in_rom(result))
		printk("-%lu = %lx\n", len, result);
	else
	printk("+%lu+%lu+%lu = %lx\n",
		ksize(result)-len,
		ksize(current->mm->tblock.next),
		ksize(current->mm->tblock.next->rblock),
		result);
	return result;
}
#endif /* DEBUG_MMAP */
 
#ifdef DEBUG
static void show_process_blocks(void)
{
	struct mm_tblock_struct * tblock, *tmp;
 
	printk("Process blocks %d:", current->pid);
 
	tmp = &current->mm->tblock;
	while (tmp) {
		printk(" %p: %p", tmp, tmp->rblock);
		if (tmp->rblock)
			printk(" (%d @%p #%d)", ksize(tmp->rblock->kblock), tmp->rblock->kblock, tmp->rblock->refcount);
		if (tmp->next)
			printk(" ->");
		else
			printk(".");
		tmp = tmp->next;
	}
	printk("\n");
}
#endif /* DEBUG */
 
extern unsigned long askedalloc, realalloc;
 
unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
	unsigned long prot, unsigned long flags, unsigned long off)
{
	void * result;
	struct mm_tblock_struct * tblock;
 
	if ((flags & MAP_SHARED) && (prot & PROT_WRITE) && (file)) {
		printk("MAP_SHARED not supported (cannot write mappings to disk)\n");
		return -EINVAL;
	}
 
	if ((prot & PROT_WRITE) && (flags & MAP_PRIVATE)) {
		printk("Private writable mappings not supported\n");
		return -EINVAL;
	}
 
	/*
	 * determine the object being mapped and call the appropriate
	 * specific mapper. 
	 */
 
	if (file) {
		struct vm_area_struct vma;
		int error;
 
 
		if (!file->f_op)
			return -ENODEV;
 
		vma.vm_start = addr;
		vma.vm_end = addr + len;
		vma.vm_flags = vm_flags(prot,flags) /*| mm->def_flags*/;
 
		if (file->f_mode & 1)
			vma.vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
		if (flags & MAP_SHARED) {
			vma.vm_flags |= VM_SHARED | VM_MAYSHARE;
			/*
			 * This looks strange, but when we don't have the file open
			 * for writing, we can demote the shared mapping to a simpler
			 * private mapping. That also takes care of a security hole
			 * with ptrace() writing to a shared mapping without write
			 * permissions.
			 *
			 * We leave the VM_MAYSHARE bit on, just to get correct output
			 * from /proc/xxx/maps..
			 */
			if (!(file->f_mode & 2))
				vma.vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
		}
		vma.vm_offset = off;
 
#ifdef MAGIC_ROM_PTR
		/* First, try simpler routine designed to give us a ROM pointer. */
 
		if (file->f_op->romptr && !(prot & PROT_WRITE)) {
			error = file->f_op->romptr(file->f_inode, file, &vma);
			/*printk("romptr mmap returned %d /%x\n", error, vma.vm_start);*/
 
			if (!error)
				return vma.vm_start;
			else if (error != -ENOSYS)
				return error;
		} else
#endif /* MAGIC_ROM_PTR */
		/* Then try full mmap routine, which might return a RAM pointer,
		   or do something truly complicated. */
 
		if (file->f_op->mmap) {
			error = file->f_op->mmap(file->f_inode, file, &vma);
 
			/*printk("mmap mmap returned %d /%x\n", error, vma.vm_start);*/
			if (!error)
				return vma.vm_start;
			else if (error != -ENOSYS)
				return error;
		} else
			return -ENODEV; /* No mapping operations defined */
 
		/* An ENOSYS error indicates that mmap isn't possible (as opposed to
		   tried but failed) so we'll fall through to the copy. */
	}
 
	tblock = (struct mm_tblock_struct *)
                        kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL);
	if (!tblock) {
		printk("Allocation of tblock for %lu byte allocation from process %d failed\n", len, current->pid);
		show_buffers();
		show_free_areas();
		return -ENOMEM;
	}
 
        tblock->rblock = (struct mm_rblock_struct *)
                        kmalloc(sizeof(struct mm_rblock_struct), GFP_KERNEL);
 
	if (!tblock->rblock) {
		printk("Allocation of rblock for %lu byte allocation from process %d failed\n", len, current->pid);
		show_buffers();
		show_free_areas();
		kfree(tblock);
		return -ENOMEM;
	}
 
 
	result = kmalloc(len, GFP_KERNEL);
	if (!result) {
		printk("Allocation of length %lu from process %d failed\n", len, current->pid);
		show_buffers();
		show_free_areas();
		kfree(tblock->rblock);
		kfree(tblock);
		return -ENOMEM;
	}
 
        tblock->rblock->refcount = 1;
        tblock->rblock->kblock = result;
        tblock->rblock->size = len;
 
	realalloc += ksize(result);
	askedalloc += len;
 
#ifdef WARN_ON_SLACK	
	if ((len+WARN_ON_SLACK) <= ksize(result))
		printk("Allocation of %lu bytes from process %d has %lu bytes of slack\n", len, current->pid, ksize(result)-len);
#endif
 
	if (file) {
		int error;
		int old_fs = get_fs();
        	set_fs(KERNEL_DS);
                error = file->f_op->read(file->f_inode, file, (char*)result, len);
                set_fs(old_fs);
                if (error < 0) {
                	kfree(result);
                	kfree(tblock->rblock);
                	kfree(tblock);
                	return error;
                }
                if (error<len)
	                memset(result+error, '\0', len-error);
        } else {
        	memset(result, '\0', len);
        }
 
 
        realalloc += ksize(tblock);
        askedalloc += sizeof(struct mm_tblock_struct);
 
        realalloc += ksize(tblock->rblock);
        askedalloc += sizeof(struct mm_rblock_struct);
 
        tblock->next = current->mm->tblock.next;
	current->mm->tblock.next = tblock;
 
#ifdef DEBUG
        printk("do_mmap:\n");
	show_process_blocks();
#endif	  
 
	return (unsigned long)result;
}
 
 
asmlinkage int sys_munmap(unsigned long addr, size_t len)
{
	return do_munmap(addr, len);
}
 
#ifdef DEBUG_MMAP
#undef do_munmap
int do_munmap_flf(unsigned long addr, size_t len, char * filename, int line, char*function)
{
	printk("do_munmap of %lx bytes at %x invoked from %s @%s:%d\n", ksize(addr), addr, function, filename, line);
	return do_munmap(addr,len);
}
 
#endif
 
 
/*
 * Munmap is split into 2 main parts -- this part which finds
 * what needs doing, and the areas themselves, which do the
 * work.  This now handles partial unmappings.
 * Jeremy Fitzhardine <jeremy@sw.oz.au>
 */
int do_munmap(unsigned long addr, size_t len)
{
	struct mm_tblock_struct * tblock, *tmp;
 
#ifdef MAGIC_ROM_PTR
	/* For efficiency's sake, if the pointer is obviously in ROM,
	   don't bother walking the lists to free it */
	if (is_in_rom(addr))
		return 0;
#endif
 
#ifdef DEBUG
        printk("do_munmap:\n");
#endif
 
	tmp = &current->mm->tblock;
	while ((tblock=tmp->next) && (tblock->rblock) && (tblock->rblock->kblock != (void*)addr)) 
		tmp = tblock;
 
	if (!tblock) {
	        printk("munmap of non-mmaped memory by process %d (%s): %p\n", current->pid, current->comm, (void*)addr);
	        return -EINVAL;
	}
	if(tblock->rblock)
		if(!--tblock->rblock->refcount) {
			if (tblock->rblock->kblock) {
				realalloc -= ksize(tblock->rblock->kblock);
				askedalloc -= tblock->rblock->size;
				kfree(tblock->rblock->kblock);
			}
 
			realalloc -= ksize(tblock->rblock);
			askedalloc -= sizeof(struct mm_rblock_struct);
			kfree(tblock->rblock);
		}
	tmp->next = tblock->next;
	realalloc -= ksize(tblock);
	askedalloc -= sizeof(struct mm_tblock_struct);
	kfree(tblock);
 
#ifdef DEBUG
	show_process_blocks();
#endif	  
 
	return -EINVAL;
}
 
/* Release all mmaps. */
void exit_mmap(struct mm_struct * mm)
{
        struct mm_tblock_struct * tmp = &mm->tblock;
        /*unsigned long flags;*/
 
        if (!mm)
                return;
 
	if (mm->executable)
		iput(mm->executable);
	mm->executable = 0;
 
        /*save_flags(flags); cli();*/
 
        if (mm->count > 1) {
                /*restore_flags(flags);*/
                return;
        }
 
#ifdef DEBUG
        printk("Exit_mmap:\n");
#endif
 
        while((tmp = tmp->next)) {
                if (tmp->rblock) {
                        if (!--tmp->rblock->refcount) {
                                if (tmp->rblock->kblock) {
                                        realalloc -= ksize(tmp->rblock->kblock);
                                        askedalloc -= tmp->rblock->size;
                                        kfree(tmp->rblock->kblock);
                                }
                                realalloc -= ksize(tmp->rblock);
                                askedalloc -= sizeof(struct mm_rblock_struct);
                                kfree(tmp->rblock);
                        }
                        tmp->rblock = 0;
                }
                mm->tblock.next = tmp->next;
                realalloc -= ksize(tmp);
                askedalloc -= sizeof(struct mm_tblock_struct);
                kfree(tmp);
        }
 
#ifdef DEBUG
	show_process_blocks();
#endif	  
 
        /*restore_flags(flags);*/
 
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.