#include "map_struct.h"
#include <node.h>
#include <stat_struct.h>
#include <map_info_struct.h>
#include <mapping.h>
#include <kmalloc.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <i386.h>
#include <util.h>
#include <sprintf.h>
#include <string.h>
#include <limits.h>
static char zero_page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
static void init_segment(struct segment_struct *segment, unsigned long start, unsigned long end)
{
segment->start = start;
segment->end = end;
}
static void grow_down_segment(struct segment_struct *segment, unsigned long laddr)
{
segment->start = PAGE(laddr);
}
static void resize_segment(struct segment_struct *segment, unsigned long end)
{
segment->end = end;
}
static int compare_segment_laddr(const struct segment_struct *segment, unsigned long laddr)
{
if (segment->end < laddr)
return -1;
if (segment->start > laddr)
return 1;
return 0;
}
static int compare_segments(const struct segment_struct *segment1, const struct segment_struct *segment2)
{
if (segment1->end < segment2->start)
return -1;
if (segment1->start > segment2->end)
return 1;
return 0;
}
static int intersect_segments(const struct segment_struct *segment1, const struct segment_struct *segment2, struct segment_struct *inter_segment)
{
if (segment1->end < segment2->start)
return -1;
if (segment1->start > segment2->end)
return 1;
inter_segment->start = max(segment1->start, segment2->start);
inter_segment->end = min(segment1->end, segment2->end);
return 0;
}
static int merge_segments(const struct segment_struct *segment1, const struct segment_struct *segment2, struct segment_struct *merge_segment)
{
if (segment2->start == 0 || segment1->end != segment2->start - 1)
return 0;
merge_segment->start = segment1->start;
merge_segment->end = segment2->end;
return 1;
}
static void init_map(struct map_struct *map, unsigned long start, unsigned long end, int prot, int grows_down, const struct map_operations_struct *operations)
{
init_segment(&map->segment, start, end);
map->prot = prot;
map->grows_down = grows_down;
map->operations = operations;
}
static inline unsigned long mem_map_get_mem_page_frame(const struct map_struct *mem_map, unsigned long page)
{
return (unsigned long)(mem_map->mem_start + (page - mem_map->segment.start));
}
static void mem_map_do_destroy(struct map_struct *mem_map) {}
static void mem_map_do_clone(const struct map_struct *mem_map, struct map_struct *dest_map)
{
dest_map->mem_start = mem_map->mem_start;
}
static int mem_map_do_is_mergeable(const struct map_struct *mem_map, const struct map_struct *mem_map1)
{
return mem_map1->mem_start == mem_map->mem_start;
}
static void mem_map_do_merge(struct map_struct *mem_map, struct map_struct *mem_map1) {}
static void mem_map_do_grow_down(struct map_struct *mem_map) {}
static int mem_map_do_fill_page(const struct map_struct *mem_map, unsigned long page)
{
memcpy((void *)page, (void *)mem_map_get_mem_page_frame(mem_map, page), PAGE_SIZE);
return 1;
}
static unsigned long mem_map_do_get_clean_page_frame(const struct map_struct *mem_map, unsigned long laddr)
{
return mem_map_get_mem_page_frame(mem_map, PAGE(laddr));
}
static void mem_map_do_set_clean_page_frame(const struct map_struct *mem_map, unsigned long laddr, unsigned long page_frame) {}
static void mem_map_do_dirty_page(const struct map_struct *mem_map, unsigned long laddr, unsigned long page_frame) {}
static int mem_map_get_info(const struct map_struct *mem_map, struct map_info_struct *map_info)
{
map_info->offset = 0;
map_info->dev = 0;
map_info->id = 0;
map_info->pathname = NULL;
return 0;
}
static const struct map_operations_struct mem_map_operations = {
.do_destroy = mem_map_do_destroy,
.do_clone = mem_map_do_clone,
.do_is_mergeable = mem_map_do_is_mergeable,
.do_merge = mem_map_do_merge,
.do_grow_down = mem_map_do_grow_down,
.do_fill_page = mem_map_do_fill_page,
.do_get_clean_page_frame = mem_map_do_get_clean_page_frame,
.do_set_clean_page_frame = mem_map_do_set_clean_page_frame,
.do_dirty_page = mem_map_do_dirty_page,
.do_get_info = mem_map_get_info
};
static void init_mem_map(struct map_struct *mem_map, unsigned long start, unsigned long length, int prot, void *mem_start)
{
init_map(mem_map, start, start + (length - 1), prot, 0, &mem_map_operations);
mem_map->mem_start = mem_start;
}
static void anon_map_do_destroy(struct map_struct *anon_map) {}
static void anon_map_do_clone(const struct map_struct *anon_map, struct map_struct *dest_map)
{
dest_map->name = anon_map->name;
}
static int anon_map_do_is_mergeable(const struct map_struct *anon_map, const struct map_struct *anon_map1)
{
if (!anon_map1->name || !anon_map->name)
return !anon_map1->name && !anon_map->name;
return !strcmp(anon_map1->name, anon_map->name);
}
static void anon_map_do_merge(struct map_struct *anon_map, struct map_struct *anon_map1) {}
static void anon_map_do_grow_down(struct map_struct *anon_map) {}
static int anon_map_do_fill_page(const struct map_struct *anon_map, unsigned long page)
{
memset((void *)page, 0, PAGE_SIZE);
return 1;
}
static unsigned long anon_map_do_get_clean_page_frame(const struct map_struct *anon_map, unsigned long laddr)
{
return (unsigned long)zero_page;
}
static void anon_map_do_set_clean_page_frame(const struct map_struct *anon_map, unsigned long laddr, unsigned long page_frame) {}
static void anon_map_do_dirty_page(const struct map_struct *anon_map, unsigned long laddr, unsigned long page_frame) {}
static int anon_map_get_info(const struct map_struct *anon_map, struct map_info_struct *map_info)
{
unsigned int size;
map_info->offset = 0;
map_info->dev = 0;
map_info->id = 0;
if (anon_map->name) {
size = strlen(anon_map->name) + 3;
if (!(map_info->pathname = kmalloc(size)))
return -ENOMEM;
snprintf(map_info->pathname, size, "[%s]", anon_map->name);
}
else
map_info->pathname = NULL;
return 0;
}
static const struct map_operations_struct anon_map_operations = {
.do_destroy = anon_map_do_destroy,
.do_clone = anon_map_do_clone,
.do_is_mergeable = anon_map_do_is_mergeable,
.do_merge = anon_map_do_merge,
.do_grow_down = anon_map_do_grow_down,
.do_fill_page = anon_map_do_fill_page,
.do_get_clean_page_frame = anon_map_do_get_clean_page_frame,
.do_set_clean_page_frame = anon_map_do_set_clean_page_frame,
.do_dirty_page = anon_map_do_dirty_page,
.do_get_info = anon_map_get_info
};
static void init_anon_map(struct map_struct *anon_map, unsigned long start, unsigned long length, int prot, int grows_down, const char *name)
{
init_map(anon_map, start, start + (length - 1), prot, grows_down, &anon_map_operations);
anon_map->name = name;
}
static inline unsigned long file_map_get_file_offset(const struct map_struct *file_map, unsigned long page)
{
return file_map->offset + (page - file_map->segment.start);
}
static void file_map_do_destroy(struct map_struct *file_map)
{
node_release_write_access(file_map->file);
node_release(file_map->file);
}
static void file_map_do_clone(const struct map_struct *file_map, struct map_struct *dest_map)
{
node_hold(file_map->file);
node_deny_write_access(file_map->file);
dest_map->file = file_map->file;
dest_map->offset = file_map_get_file_offset(file_map, dest_map->segment.start);
}
static int file_map_do_is_mergeable(const struct map_struct *file_map, const struct map_struct *file_map1)
{
return (file_map1->file == file_map->file
&& file_map_get_file_offset(file_map, file_map->segment.end + 1) == file_map1->offset);
}
static void file_map_do_merge(struct map_struct *file_map, struct map_struct *file_map1)
{
node_release_write_access(file_map1->file);
node_release(file_map1->file);
}
static void file_map_do_grow_down(struct map_struct *file_map) {}
static int file_map_do_fill_page(const struct map_struct *file_map, unsigned long page)
{
unsigned long foff;
unsigned int count;
int rv;
foff = file_map_get_file_offset(file_map, page);
if ((rv = read_node(file_map->file, foff, (void *)page, PAGE_SIZE)) < 0)
return 0;
count = rv;
if (count < PAGE_SIZE)
memset((void *)(page + count), 0, PAGE_SIZE - count);
return 1;
}
static unsigned long file_map_do_get_clean_page_frame(const struct map_struct *file_map, unsigned long laddr)
{
return mapping_get_page_frame(node_get_mapping(file_map->file), file_map_get_file_offset(file_map, laddr));
}
static void file_map_do_set_clean_page_frame(const struct map_struct *file_map, unsigned long laddr, unsigned long page_frame)
{
mapping_set_page_frame(node_get_mapping(file_map->file), file_map_get_file_offset(file_map, laddr), page_frame);
}
static void file_map_do_dirty_page(const struct map_struct *file_map, unsigned long laddr, unsigned long page_frame)
{
mapping_delete_page_frame(node_get_mapping(file_map->file), file_map_get_file_offset(file_map, laddr), page_frame);
}
static int file_map_get_info(const struct map_struct *file_map, struct map_info_struct *map_info)
{
struct stat_struct statbuf;
int rv;
if ((rv = stat_node(file_map->file, &statbuf)) < 0)
return rv;
map_info->offset = file_map->offset;
map_info->dev = statbuf.dev;
map_info->id = statbuf.id;
if ((rv = get_path(file_map->file, &map_info->pathname)) < 0)
return rv;
return 0;
}
static const struct map_operations_struct file_map_operations = {
.do_destroy = file_map_do_destroy,
.do_clone = file_map_do_clone,
.do_is_mergeable = file_map_do_is_mergeable,
.do_merge = file_map_do_merge,
.do_grow_down = file_map_do_grow_down,
.do_fill_page = file_map_do_fill_page,
.do_get_clean_page_frame = file_map_do_get_clean_page_frame,
.do_set_clean_page_frame = file_map_do_set_clean_page_frame,
.do_dirty_page = file_map_do_dirty_page,
.do_get_info = file_map_get_info
};
static int init_file_map(struct map_struct *file_map, unsigned long start, unsigned long length, int prot, struct file_struct *file, unsigned long offset)
{
int retval;
init_map(file_map, start, start + (length - 1), prot, 0, &file_map_operations);
node_hold(file);
if ((retval = node_deny_write_access(file)) < 0)
goto error_release;
file_map->file = file;
file_map->offset = offset;
return 0;
error_release:
node_release(file);
return retval;
}
void init_zero_page()
{
memset(zero_page, 0, PAGE_SIZE);
}
int alloc_mem_map(unsigned long start, unsigned long length, int prot, void *mem_start, struct map_struct **mem_map)
{
if (!(*mem_map = kmalloc(sizeof (struct map_struct))))
return -ENOMEM;
init_mem_map(*mem_map, start, length, prot, mem_start);
return 0;
}
int alloc_anon_map(unsigned long start, unsigned long length, int prot, int grows_down, const char *name, struct map_struct **anon_map)
{
if (!(*anon_map = kmalloc(sizeof (struct map_struct))))
return -ENOMEM;
init_anon_map(*anon_map, start, length, prot, grows_down, name);
return 0;
}
int alloc_file_map(unsigned long start, unsigned long length, int prot, struct file_struct *file, unsigned long offset, struct map_struct **file_map)
{
int retval;
if (!(*file_map = kmalloc(sizeof (struct map_struct)))) {
retval = -ENOMEM;
goto error;
}
if ((retval = init_file_map(*file_map, start, length, prot, file, offset)) < 0)
goto error_kfree;
return 0;
error_kfree:
kfree(*file_map, sizeof (struct map_struct));
error:
return retval;
}
void free_map(struct map_struct *map)
{
map->operations->do_destroy(map);
kfree(map, sizeof (struct map_struct));
}
int map_clone(const struct map_struct *map, unsigned long start, unsigned long length, int *status, struct map_struct **new_map)
{
struct segment_struct seg, inter_seg;
int st;
init_segment(&seg, start, start + (length - 1));
st = intersect_segments(&map->segment, &seg, &inter_seg);
if (st) {
*status = st;
return 0;
}
if (!(*new_map = kmalloc(sizeof (struct map_struct))))
return -ENOMEM;
init_map(*new_map, inter_seg.start, inter_seg.end, map->prot, map->grows_down, map->operations);
map->operations->do_clone(map, *new_map);
*status = 0;
return 0;
}
int map_split(struct map_struct *map, unsigned long start, struct map_struct **new_map)
{
if (start == map->segment.start) {
*new_map = NULL;
return 0;
}
if (!(*new_map = kmalloc(sizeof (struct map_struct))))
return -ENOMEM;
init_map(*new_map, start, map->segment.end, map->prot, map->grows_down, map->operations);
map->operations->do_clone(map, *new_map);
resize_segment(&map->segment, start - 1);
return 0;
}
int map_merge(struct map_struct *map, struct map_struct *map1)
{
struct segment_struct merge_seg;
if (!merge_segments(&map->segment, &map1->segment, &merge_seg))
return 0;
if (map1->prot != map->prot)
return 0;
if (map1->operations != map->operations)
return 0;
if (!map->operations->do_is_mergeable(map, map1))
return 0;
map->segment = merge_seg;
map->operations->do_merge(map, map1);
kfree(map1, sizeof (struct map_struct));
return 1;
}
void map_grow_down(struct map_struct *map, unsigned long laddr)
{
grow_down_segment(&map->segment, laddr);
map->operations->do_grow_down(map);
}
void map_protect(struct map_struct *map, int prot)
{
map->prot = prot;
}
int map_verify_access(const struct map_struct *map, int access)
{
switch (access) {
case PF_READ:
return 1;
case PF_WRITE:
default:
return map->prot == PROT_RDWR;
}
}
int map_grows_down(const struct map_struct *map)
{
return map->grows_down;
}
int map_fill_page(const struct map_struct *map, unsigned long page)
{
return map->operations->do_fill_page(map, page);
}
unsigned long map_get_clean_page_frame(const struct map_struct *map, unsigned long laddr)
{
return map->operations->do_get_clean_page_frame(map, laddr);
}
void map_set_clean_page_frame(const struct map_struct *map, unsigned long laddr, unsigned long page_frame)
{
map->operations->do_set_clean_page_frame(map, laddr, page_frame);
}
void map_dirty_page(const struct map_struct *map, unsigned long laddr, unsigned long page_frame)
{
map->operations->do_dirty_page(map, laddr, page_frame);
}
int map_get_info(const struct map_struct *map, struct map_info_struct *map_info)
{
map_info->start_addr = map->segment.start;
map_info->end_addr = map->segment.end + 1;
switch (map->prot) {
case PROT_RDONLY:
map_info->perms.read = 1;
map_info->perms.write = 0;
break;
case PROT_RDWR:
map_info->perms.read = 1;
map_info->perms.write = 1;
break;
}
return map->operations->do_get_info(map, map_info);
}
void map_info_destroy(struct map_info_struct *map_info)
{
if (map_info->pathname)
kfree(map_info->pathname, strlen(map_info->pathname) + 1);
}
int compare_map_laddr(const struct map_struct *map, unsigned long laddr)
{
return compare_segment_laddr(&map->segment, laddr);
}
int compare_map_prot(const struct map_struct *map, int prot)
{
return map->prot == prot;
}
int compare_maps(const struct map_struct *map1, const struct map_struct *map2)
{
return compare_segments(&map1->segment, &map2->segment);
}
int hole_intersect(const struct map_struct *map1, const struct map_struct *map2, unsigned long base, unsigned long size, unsigned long *start, unsigned long *length)
{
struct segment_struct hole, seg, inter;
int st;
if (map1 && map1->segment.end == ULONG_MAX)
return 1;
if (map2 && map2->segment.start == 0)
return -1;
init_segment(&hole, map1 ? map1->segment.end + 1 : 0, map2 ? map2->segment.start - 1 : ULONG_MAX);
init_segment(&seg, base, base + (size - 1));
st = intersect_segments(&hole, &seg, &inter);
if (st)
return st;
*start = inter.start;
*length = inter.end - inter.start + 1;
return 0;
}