Commit 100dfe86 authored by Léo Grange's avatar Léo Grange

add memory area operations for SMEM files and anonymous areas

parent e6d5a6cb
......@@ -168,18 +168,11 @@ void tlbmiss_handler()
area = mem_area_find(curpr, virtaddr);
if(area != NULL) {
union pm_page pmpage;
void *pmaddr;
// 'major' page fault, create and fill it
// 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;
pmpage = mem_area_pagefault(area, virtaddr);
if(pmpage.private.ppn != 0) {
mem_insert_page(& curpr->dir_list, &pmpage, virtaddr);
// FIXME get min(PM_PAGE_BYTES, max allowed) bytes
mem_area_copy_raw(area, (virtaddr - area->address), pmaddr, PM_PAGE_BYTES);
// not optimized, but ensure page points to a valid page struct
page = mem_find_page(curpr->dir_list, virtaddr);
......
......@@ -13,6 +13,10 @@ const struct file_operations smemfs_file_operations = {
};
const struct mem_area_ops smemfs_mem_ops = {
.area_pagefault = smemfs_area_pagefault
};
int smemfs_release (struct file *filep) {
// nothing special to do for now
......@@ -21,6 +25,63 @@ int smemfs_release (struct file *filep) {
}
/**
* 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;
ssize_t max_read;
struct smemfs_frag_header *frag;
size_t pos_frag;
size_t pos_tmp;
size_t pos_buf;
size_t file_size;
frag = (void*)(header+1);
file_size = smemfs_prim_get_file_size(header);
max_read = file_size - atpos;
max_read = max_read < len ? max_read : len;
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 <= atpos) {
pos_tmp += frag->data_size + 1;
frag++;
}
// compute offset inside the fragment
pos_frag = atpos - pos_tmp;
// read data fragment after fragment
pos_buf = 0;
while(j<n) {
// chunk_size is the number of bytes to read in the current fragment
size_t chunk_size = frag->data_size + 1 - pos_frag;
size_t toread = (j+chunk_size) < n ? chunk_size : n-j;
memcpy((char*)dest + pos_buf, (char*)(smemfs_prim_get_frag_data(frag)) + pos_frag, toread);
j += toread;
pos_buf += toread;
if(toread == chunk_size) {
// full fragment read, go to next
pos_frag = 0;
frag++;
}
else pos_frag += toread;
}
return max_read;
}
ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
......@@ -35,60 +96,13 @@ ssize_t smemfs_read (struct file *filep, void *dest, size_t len) {
return 0;
}
else {
int j, n;
size_t max_read;
struct smemfs_file_preheader *header;
struct smemfs_frag_header *frag;
size_t pos_frag;
size_t pos_tmp;
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 = max_read < len ? max_read : len;
ssize_t ret;
n = filep->pos + max_read;
j = filep->pos;
ret = smemfs_read_data(filep->inode->abstract, dest, len, filep->pos);
if(ret > 0)
filep->pos += ret;
// TODO check everything
// look for fragment containing the first byte
pos_tmp = 0;
while(pos_tmp + frag->data_size+1 <= filep->pos) {
pos_tmp += frag->data_size + 1;
frag++;
}
// compute offset inside the fragment
pos_frag = filep->pos - pos_tmp;
// read data fragment after fragment
pos_buf = 0;
while(j<n) {
// chunk_size is the number of bytes to read in the current fragment
size_t chunk_size = frag->data_size + 1 - pos_frag;
size_t toread = (j+chunk_size) < n ? chunk_size : n-j;
memcpy((char*)dest + pos_buf, (char*)(smemfs_prim_get_frag_data(frag)) + pos_frag, toread);
j += toread;
pos_buf += toread;
if(toread == chunk_size) {
// full fragment read, go to next
pos_frag = 0;
frag++;
}
else pos_frag += toread;
}
filep->pos += max_read;
//if(filep->pos >= file_size) filep->flags |= _FILE_EOF_REATCHED;
return max_read;
return ret;
}
}
......@@ -124,3 +138,51 @@ off_t smemfs_lseek (struct file *filep, off_t offset, int whence) {
return filep->pos;
}
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, const struct mem_area *new_area) {
return -1;
}
void smemfs_area_release(struct mem_area *area) {
}
......@@ -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,12 @@ 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
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);
#endif //_FS_SMEMFS_FILE_H
......@@ -186,6 +186,7 @@ int check_elf_header(struct elf_header *h) {
}
#include <fs/casio_smemfs/file.h>
int elfloader_load_segment(struct file *filep, void *offset,
const struct elf_prog_header *ph, struct process *dest)
......@@ -196,13 +197,15 @@ int elfloader_load_segment(struct file *filep, void *offset,
area = mem_area_alloc();
filep->count ++;
area->flags = MEM_AREA_TYPE_FILE;
area->flags = MEM_AREA_TYPE_FILE | MEM_AREA_PARTIAL;
area->file.filep = filep;
area->file.base_offset = ph->offset;
area->max_size = ph->memsz;
// allow to fill with 0 any 0-initialized sections (like .bss)
area->file.infile_size = ph->filesz;
area->address = offset + ph->vaddr;
// FIXME temporary
area->ops = & smemfs_mem_ops;
mem_area_insert(dest, area);
......@@ -235,6 +238,8 @@ int elfloader_load_dynlib(const char *soname, struct process *dest) {
struct elf_section_header symtab;
printk(LOG_DEBUG, "elfloader: library '%s' loaded!\n", absname);
// FIXME don't work anymore with the new memory area system, should
// be re-written!
if(elf_get_symtab(lib, &header, &symtab) == 0) {
struct elf_symbol sym;
uint32 *reloc_got_b = NULL;
......
......@@ -10,6 +10,19 @@
static struct pool_alloc _mem_area_pool = POOL_INIT(struct mem_area);
// anonymous area operations
static union pm_page anon_area_pagefault(struct mem_area *area, void *addr_fault);
static int anon_area_resize(struct mem_area *area, const struct mem_area *new_area);
static void anon_area_release(struct mem_area *area);
static struct mem_area_ops _anon_area_ops = {
.area_pagefault = anon_area_pagefault
};
void mem_area_init() {
printk(LOG_DEBUG, "mem_area: area/page=%d\n", _mem_area_pool.perpage);
}
......@@ -39,6 +52,7 @@ void mem_area_set_anon(struct mem_area *area, void *vmaddr, size_t size) {
area->flags = MEM_AREA_TYPE_ANON;
area->address = vmaddr;
area->max_size = size;
area->ops = &_anon_area_ops;
}
......@@ -92,64 +106,55 @@ int mem_area_insert(struct process *proc, struct mem_area *area) {
}
int mem_area_copy_raw(struct mem_area *area, size_t offset, void *dest, size_t size) {
int ret = -1;
if(area->flags & MEM_AREA_TYPE_ANON) {
// anonymous area, the current implementation do nothing...
if(offset + size > area->max_size) {
printk(LOG_ERR, "mem_area: attempt to copy extra bytes (anon)\n");
}
else {
ret = 0;
}
}
else if(area->flags & MEM_AREA_TYPE_FILE) {
// file mapped to memory, for now use a generic way to handle them
// TODO improve this with map_area_ops struct to allow "override"
size_t nbread;
size_t readsize = size;
size_t mem_area_fill_partial_page(struct mem_area *area, size_t offset, void *dest) {
size_t readsize = PM_PAGE_BYTES;
if(area->flags & MEM_AREA_TYPE_FILE && area->flags & MEM_AREA_PARTIAL) {
// fill with 0 if needed
if(area->file.infile_size < offset + size) {
size_t zeroed_offset;
if(area->file.infile_size < offset + readsize) {
size_t zeroed_size;
// partially
if(area->file.infile_size > offset) {
readsize = area->file.infile_size - offset;
zeroed_offset = readsize;
zeroed_size = (offset + size) - area->file.infile_size;
zeroed_size = (offset + PM_PAGE_BYTES) - area->file.infile_size;
}
else {
zeroed_offset = 0;
zeroed_size = size;
zeroed_size = readsize;
readsize = 0;
}
memset(dest + zeroed_offset, 0, zeroed_size);
memset(dest + readsize, 0, zeroed_size);
}
}
ret = 0;
if(readsize > 0) {
vfs_lseek(area->file.filep, area->file.base_offset + offset, SEEK_SET);
nbread = vfs_read(area->file.filep, dest, readsize);
ret = nbread == readsize ? 0 : -1;
if(ret) {
printk(LOG_ERR, "mem_area: failed loading %d bytes from offset 0x%x"
" [absolute 0x%x] (read returns %d)\n",
readsize, offset, area->file.base_offset + offset, nbread);
ret = -1;
}
else {
printk(LOG_DEBUG, "mem_area: loaded %d bytes @%p from file\n", readsize, dest);
}
}
return readsize;
}
// anonymous area operations :
static union pm_page anon_area_pagefault(struct mem_area *area, void *addr_fault) {
union pm_page pmpage = { .private.ppn = 0, .private.flags = MEM_PAGE_PRIVATE };
size_t offset = addr_fault - area->address;
//size_t size = PM_PAGE_BYTES;
//print_memory(LOG_DEBUG, dest, nbread);
// anonymous area, the current implementation only allocate a page
if(offset < area->max_size) {
void *pmaddr;
// 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?
}
else {
printk(LOG_ERR, "mem_area: attempt to copy extra bytes (anon)\n");
}
return ret;
return pmpage;
}
......@@ -3,6 +3,7 @@
#include <utils/types.h>
#include <utils/list.h>
#include <sys/memory.h>
/**
* Generic memory-mapping interface, using segmentation-based mapping.
......@@ -14,11 +15,16 @@
#define MEM_AREA_TYPE_FILE (1<<0)
#define MEM_AREA_TYPE_ANON (1<<1)
// some kind of area are allowed to grow
#define MEM_AREA_MAYGROW (1<<2)
// flag used if area grows bottom-up instead of top-down (which is the default)
#define MEM_AREA_GROW_UP (1<<3)
// protections, flag is set if given operation is permited
#define MEM_AREA_PROT_R (1<<3)
#define MEM_AREA_PROT_W (1<<4)
#define MEM_AREA_PROT_X (1<<5)
// flag used to implement .data and .bss in a single area : size from origin
// file is limited by infile_size, and if max_size is greater, fill with 0
#define MEM_AREA_PARTIAL (1<<6)
struct mem_area_ops;
struct mem_area {
int flags;
......@@ -27,6 +33,9 @@ struct mem_area {
void *address;
size_t max_size;
// callback functions to manage this area
struct mem_area_ops *ops;
// type-specific data (determined by flags)
union {
struct {
......@@ -53,6 +62,42 @@ struct mem_area {
};
/**
* mem_area_ops is used to map a specific implementation to a given memory area.
* It is expected to be specialized by each filesystem that support file
* mapping, and each device which need to do so.
* Any NULL field is interpreted as using the default implementation if any.
*
* Creating the area is done using device/filesystem specific call.
*/
struct mem_area_ops {
/**
* Called each time a page that should reside in the given area range is
* used, but not present in memory.
* This is the most important callback, which determine how this area works
* and what it initialy contains.
* The corresponding physical page is returned, or NULL to indicate and
* error.
*/
union pm_page (*area_pagefault)(struct mem_area *area, void *addr_fault);
/**
* Change the size of the given area, to the given new_size.
* Non-zero should be returned on error, 0 else (and area->max_pos should
* be set correctly).
* Any page which is out of the area after decreasing size will be removed
* from the corresponding process address space by the caller.
*/
int (*area_resize)(struct mem_area *area, const struct mem_area *new_area);
/**
* Called when the area is no longer used.
*/
void (*area_release)(struct mem_area *area);
};
struct process;
/**
......@@ -91,13 +136,29 @@ int mem_area_insert(struct process *proc, struct mem_area *area);
/**
* Copy a part of this area content to the given *physical* address.
* This function *DO NOT* deal with virtual addresses, and don't check for page
* boundaries. The caller should check if given address range still belong
* to the appropriate process, and is already "allocated" and usable!
* Offset is relative to the beginning of this area.
* Return non-zero in error case.
* Generic function call when a page fault occurs, inside a given area address
* range.
* The implementation specific operation is called to set the corresponding page.
*/
int mem_area_copy_raw(struct mem_area *area, size_t offset, void *dest, size_t size);
extern inline union pm_page mem_area_pagefault(struct mem_area *area,
void *addrfault)
{
if(area->ops != NULL && area->ops->area_pagefault != NULL) {
return area->ops->area_pagefault(area, addrfault);
}
else {
union pm_page page = {.private.ppn = 0, .private.flags = MEM_PAGE_PRIVATE};
return page;
}
}
/**
* Helper for filesystem implementations, to handle MEM_AREA_PARTIAL.
* Check for a given page to fill with 0 when needed, and return the number of
* bytes the underlying implementation should read from origin file.
* 0 may be returned, if all the page is filled with zeros...
*/
size_t mem_area_fill_partial_page(struct mem_area *area, size_t offset, void *dest);
#endif //_SYS_MEM_AREA_H
......@@ -424,6 +424,7 @@ int sys_execve(const char *filename, char *const argv[], char *const envp[]) {
args_pos = nbargs * sizeof(char*);
printk(LOG_DEBUG, "execve: %d args\n", nbargs);
// FIXME use a memory area to store that (maybe the stack/heap ones?)
for(i=0 ; i<nbargs; i++) {
char *copied_arg = args_page + args_pos;
size_t curarg_size;
......@@ -474,6 +475,9 @@ int sys_execve(const char *filename, char *const argv[], char *const envp[]) {
}
INIT_LIST_HEAD(& cur->mem_areas);
// unset heap area
cur->heap_area = NULL;
// use a new address space to avoid to use old TLB records
arch_adrsp_release(& cur->addr_space);
......
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