#include "memory.h"
#include "page_fault.h"
#include "maps.h"
#include "map.h"
#include "paging.h"
#include <vm_struct.h>
#include <map_info_struct.h>
#include <gdt.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <config.h>
#include <util.h>
#include <string.h>
#include <limits.h>
#define USER_LIMIT (0xfffff - USER_BASE / PAGE_SIZE)
#define STACK_MARGIN (65536 + 32 * sizeof (unsigned long))
static inline int check_for_addition(unsigned long i1, unsigned long i2)
{
return i1 <= ULONG_MAX - i2;
}
static inline int in_user_segment(unsigned long start, unsigned long length)
{
if (!check_for_addition(start, length - PAGE_SIZE))
return 0;
if (start + (length - PAGE_SIZE) > USER_LIMIT * PAGE_SIZE)
return 0;
return 1;
}
static inline int in_stack(unsigned long addr, unsigned long stack_top)
{
return stack_top < STACK_MARGIN || addr >= stack_top - STACK_MARGIN;
}
void mm_init()
{
init_zero_page();
gdt_set_user_code_seg_descr(USER_BASE, USER_LIMIT);
gdt_set_user_data_seg_descr(USER_BASE, USER_LIMIT);
}
int vm_copy(const struct vm_struct *vm, struct vm_struct *dest_vm, unsigned long *dest_page_directory)
{
int rv;
init_page_directory(dest_page_directory);
init_maps(&dest_vm->maps);
if (!vm)
return 0;
else {
if ((rv = copy_memory(&vm->maps, &dest_vm->maps, dest_page_directory, USER_BASE, (USER_LIMIT + 1) * PAGE_SIZE)) < 0)
free_memory(&dest_vm->maps, USER_BASE, (USER_LIMIT + 1) * PAGE_SIZE);
return rv;
}
}
void vm_free(struct vm_struct *vm)
{
free_memory(&vm->maps, USER_BASE, (USER_LIMIT + 1) * PAGE_SIZE);
}
int vm_map_mem(struct vm_struct *vm, unsigned long start, unsigned long end)
{
unsigned long length;
length = end - start;
if (!in_user_segment(start, length))
return -EINVAL;
return map_mem(&vm->maps, USER_BASE + start, length, PROT_RDWR, (void *)start);
}
int vm_map_anon(struct vm_struct *vm, unsigned long start, unsigned long length, int prot, int grows_down, const char *name)
{
if (!in_user_segment(start, length))
return -EINVAL;
return map_anon(&vm->maps, USER_BASE + start, length, prot, grows_down, name);
}
int vm_map_file(struct vm_struct *vm, unsigned long start, unsigned long length, int prot, struct file_struct *file, unsigned long offset)
{
if (!in_user_segment(start, length))
return -EINVAL;
return map_file(&vm->maps, USER_BASE + start, length, prot, file, offset);
}
int vm_unmap(struct vm_struct *vm, unsigned long start, unsigned long length)
{
if (!in_user_segment(start, length))
return -EINVAL;
return free_memory(&vm->maps, USER_BASE + start, length);
}
int vm_protect(struct vm_struct *vm, unsigned long start, unsigned long length, int prot)
{
if (!in_user_segment(start, length))
return -EINVAL;
return protect_memory(&vm->maps, USER_BASE + start, length, prot);
}
unsigned long vm_get_free_area(const struct vm_struct *vm, unsigned long base, unsigned long size, unsigned long length, int side)
{
unsigned long start;
if (!in_user_segment(base, size))
return 0;
if (!(start = get_free_area(&vm->maps, USER_BASE + base, size, length, side)))
return 0;
return start - USER_BASE;
}
int vm_verify_area(const struct vm_struct *vm, unsigned long start, unsigned long length, int access)
{
unsigned long poff;
poff = PAGE_OFFSET(start);
start -= poff;
if (!check_for_addition(length, poff))
return 0;
length += poff;
if (!check_for_addition(length, PAGE_SIZE - 1))
return 0;
length = PAGE_ALIGN(length);
if (!in_user_segment(start, length))
return 0;
return verify_memory(&vm->maps, USER_BASE + start, length, access);
}
static int scan_page(unsigned long start, unsigned int n, unsigned int *len)
{
const void *p;
n = min(n, PAGE_SIZE - PAGE_OFFSET(start));
if ((p = memchr((void *)(USER_BASE + start), 0, n))) {
if (len)
*len += p - (void *)(USER_BASE + start);
return 1;
}
else {
if (len)
*len += n;
return 0;
}
}
int vm_verify_str(const struct vm_struct *vm, unsigned long start, unsigned int n, unsigned int *len)
{
unsigned long poff;
poff = PAGE_OFFSET(start);
start -= poff;
if (!in_user_segment(start, PAGE_SIZE))
return 0;
if (!verify_memory(&vm->maps, USER_BASE + start, PAGE_SIZE, PF_READ))
return 0;
if (len)
*len = 0;
if (!scan_page(start + poff, n, len) && n >= PAGE_SIZE)
do {
n -= PAGE_SIZE;
if (!check_for_addition(start, PAGE_SIZE))
return 0;
start += PAGE_SIZE;
if (start > USER_LIMIT * PAGE_SIZE)
return 0;
if (!verify_memory(&vm->maps, USER_BASE + start, PAGE_SIZE, PF_READ))
return 0;
}
while (!scan_page(start, n, len) && n >= PAGE_SIZE);
return 1;
}
int vm_get_maps(const struct vm_struct *vm, struct map_info_struct **map_info_array, unsigned int *count)
{
int rv;
unsigned int i;
if ((rv = get_maps(&vm->maps, map_info_array, count)) < 0)
return rv;
for (i = 0; i < *count; i++) {
(*map_info_array)[i].start_addr -= USER_BASE;
(*map_info_array)[i].end_addr -= USER_BASE;
}
return 0;
}
int vm_handle_page_fault(const struct vm_struct *vm, int cause, int access, unsigned long stack_top, unsigned long laddr)
{
return _handle_page_fault(laddr, cause, access, in_stack(laddr - USER_BASE, stack_top), &vm->maps);
}