2017-03-27 23:09:09 +02:00

172 lines
3.9 KiB
C

#include <scheduler.h>
#include <list.h>
#include <debug.h>
#include <int.h>
#include <cpu.h>
#include <registers.h>
procmm_mmap_t *procmm_new_map(process_t *proc, procmm_mmap_t *src)
{
procmm_mmap_t *map = kcalloc(1, sizeof(procmm_mmap_t));
map->proc = proc;
map->P4 = vmm_new_P4();
LIST_INIT(map, areas);
if(src)
{
LIST_FOREACH(src->areas, procmm_area_t, o, areas)
{
procmm_map(map, o->start, o->end, o->flags);
vmm_p4_memcpy(map->P4, (void *)o->start, src->P4, (void *)o->start, o->end-o->start);
}
}
return map;
}
void procmm_free_map(process_t *proc)
{
procmm_mmap_t *map = proc->mmap;
procmm_area_t *o = map->areas.next;
while(o)
{
procmm_area_t *next = o->areas.next;
procmm_unmap(o);
o = next;
}
map->brk = 0;
map->stack = 0;
vmm_free_P4(map->P4);
kfree(map);
}
void procmm_print_map(procmm_mmap_t *map)
{
debug_info("Procmm map (process:%d)\n", map->proc->pid);
LIST_FOREACH(map->areas, procmm_area_t, o, areas)
{
debug(" %x->%x (%x)", o->start, o->end, o->flags);
if(o == map->brk)
debug(" BRK");
if(o == map->stack)
debug(" STACK");
debug("\n");
}
}
procmm_area_t *procmm_map(procmm_mmap_t *map, uintptr_t start, uintptr_t end, uint64_t flags)
{
procmm_area_t *a = kcalloc(1, sizeof(procmm_area_t));
a->start = start;
a->end = end;
a->flags = flags;
a->map = map;
LIST_INIT(a, areas);
LIST_FOREACH(map->areas, procmm_area_t, o, areas)
{
if(!((o->end < start) || (o->start > a->end)))
{
// Overlapping areas
debug_error("PROCMM - Overlapping areas (unsupported for now)\n");
kfree(a);
return 0;
}
if(o->start > a->end)
{
LIST_INSERT_BEFORE(map->areas, a, areas, o);
}
}
if(LIST_EMPTY(a->areas))
LIST_APPEND(map->areas, a, areas);
uintptr_t p = a->start;
while(p < a->end)
{
vmm_set_page(map->P4, p, pmm_alloc(), PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
p += PAGE_SIZE;
}
return a;
}
void procmm_unmap(procmm_area_t *a)
{
uintptr_t p = a->start;
while(p < a->end)
{
uintptr_t ph = vmm_get_page(a->map->P4, p);
pmm_free(ph);
vmm_set_page(a->map->P4, p, 0, 0);
p += PAGE_SIZE;
}
LIST_REMOVE(a->map->areas, a, areas);
kfree(a);
}
size_t procmm_resize(process_t *p, procmm_area_t *a, size_t len)
{
procmm_mmap_t *map = p->mmap;
size_t old_len = a->end - a->start;
size_t old_end = a->end;
if(old_len < len)
{
procmm_area_t *next = a->areas.next;
uintptr_t max = next->start;
max = (max < a->start)?KERNEL_OFFSET:max;
if((a->start + len) > max)
len = max - a->start;
a->end = a->start + len;
uintptr_t page = (old_end + PAGE_SIZE) & ~(PAGE_SIZE-1);
while(page < a->end)
{
vmm_set_page(map->P4, page, pmm_alloc(), PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
page += PAGE_SIZE;
}
} else {
debug_error("Decreasing memory area size is not implemented yet\n");
for(;;);
}
return len;
}
int procmm_setup(process_t *proc, size_t brk_size)
{
procmm_mmap_t *map = proc->mmap;
procmm_area_t *last_a = map->areas.prev;
uintptr_t brk_start = (last_a->end + PAGE_SIZE) & ~(PAGE_SIZE-1);
map->brk = procmm_map(map, brk_start, brk_start + brk_size, 0);
map->stack = procmm_map(map, USERSPACE_TOP, USERSPACE_TOP, 0);
return brk_start;
}
registers_t *procmm_page_fault(registers_t *r)
{
procmm_mmap_t *map = get_current_process()->mmap;
uintptr_t addr = read_cr2();
// We don't want to handle stuff in the kernel
if(addr >= KERNEL_OFFSET)
return 0;
// Check if we're close to the stack
if(map->stack)
{
if(map->stack->start <= addr + PAGE_SIZE)
{
debug("Increase user stack\n");
map->stack->start = map->stack->start - PAGE_SIZE;
vmm_set_page(map->P4, map->stack->start, pmm_alloc(), PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
return r;
}
}
// If we reach this point, procmm couldn't handle the page fault
return 0;
}