Commit f5fe475c authored by Léo Grange's avatar Léo Grange
Browse files

closed #5 (Merge branch 'memory_areas')

parents 84461507 d1dfd46a
Loading
Loading
Loading
Loading
+52 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <sys/memory.h>
#include <utils/log.h>
#include <sys/kdebug.h>
#include <sys/mem_area.h>


//void exception_handler() __attribute__ ((interrupt_handler, section(".handler.exception")));
@@ -83,7 +84,7 @@ void exception_handler()
		printk(LOG_ERR, "> TEA value = %p\n", (void*)tea);
		printk(LOG_ERR, ">   *TEA = (%p)\n", (void*)(*(int*)(tea-(tea%4))));
		printk(LOG_ERR, "> SPC Value = %p\n", spcval);
		if(EXP_CODE_BAD_SLOTINSTR)
		if(evt == EXP_CODE_BAD_SLOTINSTR)
			kdebug_oops("Illegal slot instruction");
		else
			kdebug_oops("Illegal instruction");
@@ -134,6 +135,9 @@ void exception_handler()
}


// used to avoid infinite tlb fault loops when exception are allowed inside
// a page fault resolving process...
static int _recurcive_tlbfault = 0;

/**
 * This handler is very important for Virtual Memory, it has to check
@@ -151,11 +155,57 @@ void tlbmiss_handler()
	// the process which cause the TLB miss should be the current one
	curpr = _proc_current;

	if(_recurcive_tlbfault) {
		void *spcval;

		asm volatile("stc spc, %0":"=r"(spcval));

		printk(LOG_EMERG, "> [%d] Page fault %p, PC=%p\n", MMU.PTEH.BIT.ASID,
				PM_PHYSICAL_ADDR(MMU.PTEH.BIT.VPN), spcval);
		kdebug_oops("Recurcive page fault");
	}
	
	// find the corresponding page, if exists
	vpn = MMU.PTEH.BIT.VPN; 
	page = mem_find_page(curpr->dir_list,
			(void*)(vpn << PM_PAGE_ORDER) );

	// if page is not in dir list (or is invalid), maybe it exists in memory
	// area (allocate it)
	// FIXME not working for shared pages
	if(page == NULL || !(page->private.flags & MEM_PAGE_VALID))  {
		struct mem_area *area;
		void *virtaddr;

		// allow exception to occurs (realy helpful for debugging...)
		sched_preempt_block();
		_recurcive_tlbfault = 1;
		arch_int_weak_atomic_block(1);
		interrupt_inhibit_all(0);
		
		virtaddr = PM_PHYSICAL_ADDR(vpn);
		area = mem_area_find(curpr, virtaddr);
		if(area != NULL) {
			union pm_page pmpage;

			// 'major' page fault, create and fill it
			pmpage = mem_area_pagefault(area, virtaddr);
			if(pmpage.private.ppn != 0) {
				mem_insert_page(& curpr->dir_list, &pmpage, virtaddr);

				// not optimized, but ensure page points to a valid page struct
				page = mem_find_page(curpr->dir_list, virtaddr);

				printk(LOG_DEBUG, "tlb major fault: page inserted (virt %p -> phy %p)\n",
						virtaddr, PM_PHYSICAL_ADDR(page->private.ppn));
			}
		}

		interrupt_inhibit_all(1);
		_recurcive_tlbfault = 0;
		sched_preempt_unblock();
	}

	if(page != NULL) {
		unsigned int flags;
		uint32	ppn = 0;
+141 −53
Original line number Diff line number Diff line
@@ -4,15 +4,24 @@
#include <fs/inode.h>
#include <interface/fixos/errno.h>
#include "smemfs_primitives_ng.h"
// used for memory areas...
#include <fs/vfs_file.h>


const struct file_operations smemfs_file_operations = {
	.release = smemfs_release,
	.read = smemfs_read,
	.lseek = smemfs_lseek
	.lseek = smemfs_lseek,
	.map_area = smemfs_map_area
};


const struct mem_area_ops smemfs_mem_ops = {
	.area_pagefault = smemfs_area_pagefault,
	.area_release = smemfs_area_release,
	.area_duplicate = smemfs_area_duplicate
};


int smemfs_release (struct file *filep) {
	// nothing special to do for now
@@ -21,23 +30,14 @@ int smemfs_release (struct file *filep) {
}




ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
	if(!(filep->flags & O_RDONLY)) {
		return -EBADF;
	}
	if(filep->inode->type_flags & INODE_TYPE_PARENT) {
		return -EISDIR;
	}

	if(len == 0) {
		return 0;
	}
	else {
/**
 * Internal helper for reading data from a file
 */
static ssize_t smemfs_read_data (struct smemfs_file_preheader *header,
		void *dest, size_t len, size_t atpos)
{
	int j, n;
		size_t max_read;
		struct smemfs_file_preheader *header;
	ssize_t max_read;
	struct smemfs_frag_header *frag;

	size_t pos_frag;
@@ -45,27 +45,26 @@ ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
	size_t pos_buf;
	size_t file_size;

		header = filep->inode->abstract;
	frag = (void*)(header+1);

	file_size = smemfs_prim_get_file_size(header);
		max_read = file_size - filep->pos;
	max_read = file_size - atpos;
	max_read = max_read < len ? max_read : len;

		n = filep->pos + max_read;
		j = filep->pos;
	n = atpos + max_read;
	j = atpos;

	// TODO check everything

	// look for fragment containing the first byte
	pos_tmp = 0;
		while(pos_tmp + frag->data_size+1 <= filep->pos) {
	while(pos_tmp + frag->data_size+1 <= atpos) {
		pos_tmp += frag->data_size + 1;
		frag++;
	}

	// compute offset inside the fragment
		pos_frag = filep->pos - pos_tmp;
	pos_frag = atpos - pos_tmp;

	// read data fragment after fragment
	pos_buf = 0;
@@ -86,10 +85,30 @@ ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
		else pos_frag += toread;
	}

		filep->pos += max_read;
		//if(filep->pos >= file_size) filep->flags |= _FILE_EOF_REATCHED;
	return max_read;
}


ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
	if(!(filep->flags & O_RDONLY)) {
		return -EBADF;
	}
	if(filep->inode->type_flags & INODE_TYPE_PARENT) {
		return -EISDIR;
	}

	if(len == 0) {
		return 0;
	}
	else {
		ssize_t ret;

		ret = smemfs_read_data(filep->inode->abstract, dest, len, filep->pos);
		if(ret > 0)
			filep->pos += ret;

		return ret;
	}
}


@@ -124,3 +143,72 @@ off_t smemfs_lseek (struct file *filep, off_t offset, int whence) {

	return filep->pos;
}



int smemfs_map_area(struct file *filep, struct mem_area *area) {
	// not a lot of stuff to do for now...
	area->ops = &smemfs_mem_ops;
	area->file.filep = filep;
	// increase file usage count (mirrored in smemfs_area_release)
	filep->count++;
	return 0;
}


union pm_page smemfs_area_pagefault(struct mem_area *area, void *addr_fault) {
	size_t readsize;
	void *pmaddr;
	union pm_page pmpage;
	size_t offset = addr_fault - area->address;

	// allocate a physical memory page
	// FIXME UNCACHED due to temporary hack to be sure nothing is retained in cache
	pmaddr = arch_pm_get_free_page(MEM_PM_UNCACHED);
	if(pmaddr != NULL) {
		pmpage.private.ppn = PM_PHYSICAL_PAGE(pmaddr);
		pmpage.private.flags = MEM_PAGE_PRIVATE | MEM_PAGE_VALID; // | MEM_PAGE_CACHED;
	}
	// FIXME what to do if out of memory?
	
	// fill with zeroes if needed
	readsize = mem_area_fill_partial_page(area, offset, pmaddr);
	
	if(readsize > 0) {
		struct inode *inode = area->file.filep->inode;
		size_t absoffset = area->file.base_offset + offset;
		ssize_t nbread;
		
		nbread = smemfs_read_data(inode->abstract, pmaddr, readsize, absoffset);

		if(nbread != readsize) {
			printk(LOG_ERR, "smemfs_area: failed loading %d bytes from offset 0x%x"
					" [absolute 0x%x] (read returns %d)\n",
					readsize, offset, absoffset, nbread);
		}
		else {
			printk(LOG_DEBUG, "smemfs_area: loaded %d bytes @%p from file\n",
					readsize, pmaddr);
		}
	}

	return pmpage;
}

/*
int smemfs_area_resize(struct mem_area *area, size_t new_size) {
	return -1;
}
*/

void smemfs_area_release(struct mem_area *area) {
	// release the file, by closing it at vfs level?
	vfs_close(area->file.filep);
}


int smemfs_area_duplicate(struct mem_area *orig, struct mem_area *copy) {
	copy->file.filep->count++;
	return 0;
}
+15 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <fs/file_operations.h>
#include <fs/file.h>
#include <interface/fixos/stat.h>
#include <sys/mem_area.h>

/**
 * Implementation of file operations for the Casio SMEM FS.
@@ -11,6 +12,9 @@

extern const struct file_operations smemfs_file_operations;

// for memory-mapped files
extern const struct mem_area_ops smemfs_mem_ops;


int smemfs_release (struct file *filep);

@@ -19,4 +23,15 @@ ssize_t smemfs_read (struct file *filep, void *dest, size_t len);
off_t smemfs_lseek (struct file *filep, off_t offset, int whence);


// memory-mapped operations
int smemfs_map_area(struct file *filep, struct mem_area *area);

union pm_page smemfs_area_pagefault(struct mem_area *area, void *addr_fault);

int smemfs_area_resize(struct mem_area *area, const struct mem_area *new_area);

void smemfs_area_release(struct mem_area *area);

int smemfs_area_duplicate(struct mem_area *orig, struct mem_area *copy);

#endif //_FS_SMEMFS_FILE_H
+20 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@

struct file;

struct mem_area;

struct file_operations {
	/**
	 * Release the file opened instance ("close" it).
@@ -47,6 +49,24 @@ struct file_operations {
	 * data is specific to command and device, may be not used.
	 */
	int (*ioctl) (struct file *filep, int cmd, void *data);


	/**
	 * Create a memory map of this object in memory.
	 * Devices may use it as they want to, for example to provide big buffers
	 * shared with userland.
	 * area should be set with all non-private fields having a valid value,
	 * which is not very well defined...
	 * At least field ops is not expected to be set, but the interface is not
	 * well designed for now.
	 * TODO either use a 'hints' argument with mem_area-like type, or define
	 * exactly what should be set and what is set by this function itself
	 *
	 * NULL if device of filesystem do not implements memory mapped areas.
	 *
	 * Return 0 if mapping is accepted, negative value else.
	 */
	int (*map_area) (struct file *filep, struct mem_area *area);
};

#endif //_FS_FILE_OPERATIONS_H
+36 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include "file_system.h"
#include "file_operations.h"
#include "vfs_directory.h"
#include <sys/mem_area.h>


// pool allocation for file struct
@@ -157,3 +158,38 @@ int vfs_fstat(struct file *filep, struct stat *buf) {
		return -1;
	}
}


int vfs_map_area(struct file *filep, size_t size, size_t offset, void *address,
		int flags, size_t infile_size, struct process *proc)
{
	int ret = -EINVAL;

	if(filep->op->map_area != NULL) {
		struct mem_area *area;
		area = mem_area_alloc();
		if(area == NULL) {
			ret = -ENOMEM;
		}
		else {
			// prepare area struct from arguments
			area->address = address;
			area->max_size = size;
			area->flags = flags | MEM_AREA_TYPE_FILE;
			area->file.base_offset = offset;
			area->file.infile_size = (flags & MEM_AREA_PARTIAL) ? infile_size : size;
			area->file.filep = filep;
			
			ret = filep->op->map_area(filep, area);
			if(ret == 0) {
				ret = mem_area_insert(proc, area);
			}
			else {
				// failed, free area (do not *release* it, free directly)
				mem_area_free(area);
			}
		}
	}
	
	return ret;
}
Loading