diff options
Diffstat (limited to 'kern/syscall_memory.c')
| -rw-r--r-- | kern/syscall_memory.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/kern/syscall_memory.c b/kern/syscall_memory.c new file mode 100644 index 0000000..288d6df --- /dev/null +++ b/kern/syscall_memory.c @@ -0,0 +1,137 @@ +/** @file syscall_memory.c + * + * @brief memory syscalls. + * + * @author Hanjie Wu (hanjiew) + * @bug No functional bugs + */ + +#include <string.h> +#include <common_kern.h> +#include <elf/elf_410.h> +#include <ureg.h> + +#include <x86/asm.h> +#include <x86/cr.h> +#include <x86/eflags.h> +#include <x86/seg.h> + +#include <asm_instr.h> +#include <malloc.h> +#include <mm.h> +#include <paging.h> +#include <sched.h> +#include <usermem.h> + +/** + * @brief new_pages() syscall handler + * @param f saved regs + */ +void sys_new_pages_real(stack_frame_t* f) { + reg_t esi = f->esi; + va_t base; + if (copy_from_user((va_t)esi, sizeof(va_t), &base) != 0) { + goto read_fail; + } + if ((base & PAGE_OFFSET_MASK) != 0) { + goto read_fail; + } + int len; + if (copy_from_user((va_t)esi + sizeof(va_t), sizeof(int), &len) != 0) { + goto read_fail; + } + if ((len & PAGE_OFFSET_MASK) != 0) { + goto read_fail; + } + + process_t* p = get_current()->process; + mutex_lock(&p->mm_lock); + int n_pages = len / PAGE_SIZE; + pa_t paddr = alloc_user_pages(n_pages); + if (paddr == 0) { + goto alloc_segment_fail; + } + if (add_region(p, base, n_pages, paddr, 1) != 0) { + goto add_region_fail; + } + + int i = 0; + int offset = 0; + /* file page table one entry per loop */ + pa_t pt_pa = find_or_create_pt(p, base); + if (pt_pa == BAD_PA) { + goto add_pt_fail; + } + do { + offset = i * PAGE_SIZE; + /* step to next page table */ + int pt_index = get_pt_index(base + offset); + if (pt_index == 0) { + pt_pa = find_or_create_pt(p, base + offset); + if (pt_pa == BAD_PA) { + goto add_pt_fail; + } + } + + int old_if = save_clear_if(); + page_table_t* pt = (page_table_t*)map_phys_page(pt_pa, NULL); + (*pt)[pt_index] = make_pte(paddr + offset, 0, PTE_USER, PTE_RW, 0); + restore_if(old_if); + invlpg(base + offset); + } while (++i < n_pages); + mutex_unlock(&p->mm_lock); + f->eax = 0; + return; + +add_pt_fail: + /* page tables are reachable from page directory so no need to free them */ + vector_pop(&p->regions); +add_region_fail: + free_user_pages(paddr, n_pages); +alloc_segment_fail: + mutex_unlock(&p->mm_lock); +read_fail: + f->eax = (reg_t)-1; +} + +/** + * @brief remove_pages() syscall handler + * @param f saved regs + */ +void sys_remove_pages_real(stack_frame_t* f) { + process_t* p = get_current()->process; + mutex_lock(&p->mm_lock); + va_t base = (va_t)f->esi; + int i, j, n = vector_size(&p->regions); + for (i = 0; i < n; i++) { + region_t* r = (region_t*)vector_at(&p->regions, i); + if (base == r->addr) { + int n_pages = r->size / PAGE_SIZE; + j = 0; + int offset = 0; + pa_t pt_pa = find_or_create_pt(p, base); + do { + offset = j * PAGE_SIZE; + int pt_index = get_pt_index(base + offset); + if (pt_index == 0) { + pt_pa = find_or_create_pt(p, base + offset); + } + + int old_if = save_clear_if(); + page_table_t* pt = (page_table_t*)map_phys_page(pt_pa, NULL); + (*pt)[pt_index] = BAD_PDE; + restore_if(old_if); + invlpg(base + offset); + } while (++j < n_pages); + + free_user_pages(r->paddr, r->size / PAGE_SIZE); + vector_remove(&p->regions, i); + mutex_unlock(&p->mm_lock); + f->eax = 0; + return; + } + } + /* no such region */ + mutex_unlock(&p->mm_lock); + f->eax = (reg_t)-1; +} |
