/* * This function implements the munmap(2) syscall. * * As with do_mmap() it should perform the required error checking, * before calling upon vmmap_remove() to do most of the work. * Remember to clear the TLB. */ int do_munmap(void *addr, size_t len) { /*NOT_YET_IMPLEMENTED("VM: do_munmap");*/ if ((size_t)addr < USER_MEM_LOW || (size_t)addr >= USER_MEM_HIGH) { return -EINVAL; } if(len == PAGE_SIZE * 15) { dbg(DBG_PRINT, "BREAK\n"); } if(!PAGE_ALIGNED(addr)){ dbg(DBG_PRINT,"Error: do_munmap failed due to addr or len is not page aligned!\n"); return -EINVAL; } if((len <= 0) || (len >= USER_MEM_HIGH - USER_MEM_LOW)){ dbg(DBG_PRINT,"Error: do_munmap failed due to len is <= 0!\n"); return -EINVAL; } vmmap_t *map = curproc->p_vmmap; uint32_t lopage; uint32_t npages; lopage = ADDR_TO_PN(addr); /* updated */ /* TODO: Check later: may change to: uint32_t hipage = ADDR_TO_PN((size_t)addr + len - 1) + 1; */ uint32_t hipage = ADDR_TO_PN((size_t)addr + len - 1) + 1; /*uint32_t hipage = ADDR_TO_PN((size_t)addr + len) + 1;*/ npages = hipage - lopage; int retval = vmmap_remove(map, lopage, npages); if(retval < 0){ dbg(DBG_PRINT,"Error: The unmapping of the vmarea was unsuccessful\n"); return retval; } /* clear TLB for this vaddr*/ /* Corrected */ tlb_flush_range((uintptr_t)addr, npages); return 0; }
/* * This function implements the mmap(2) syscall, but only * supports the MAP_SHARED, MAP_PRIVATE, MAP_FIXED, and * MAP_ANON flags. * * Add a mapping to the current process's address space. * You need to do some error checking; see the ERRORS section * of the manpage for the problems you should anticipate. * After error checking most of the work of this function is * done by vmmap_map(), but remember to clear the TLB. */ int do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off, void **ret) { /*NOT_YET_IMPLEMENTED("VM: do_mmap");*/ if(((flags & MAP_FIXED) == MAP_FIXED) && ((size_t)addr < USER_MEM_LOW || (size_t)addr >= USER_MEM_HIGH)){ return (int)MAP_FAILED; } if(!PAGE_ALIGNED(addr) || /*!PAGE_ALIGNED(len) ||*/ !PAGE_ALIGNED(off)){ dbg(DBG_PRINT,"Error: do_mmap failed due to addr or len or off is not page aligned!\n"); return (int)MAP_FAILED; } if((len <= 0) || (len >= USER_MEM_HIGH - USER_MEM_LOW)){ dbg(DBG_PRINT,"Error: do_mmap failed due to len is <= 0!\n"); return (int)MAP_FAILED; } if (!(((flags & MAP_PRIVATE) == MAP_PRIVATE) || ((flags & MAP_SHARED) == MAP_SHARED))) { return (int)MAP_FAILED; } if (((fd >= NFILES) || ( fd < 0)) && ((flags & MAP_ANON) != MAP_ANON)) { dbg(DBG_PRINT,"ERROR!!! fd = %d is out of range\n", fd); return (int)MAP_FAILED; } file_t *file = NULL; if ((flags & MAP_ANON) != MAP_ANON) { file = fget(fd); if (file == NULL) { return (int)MAP_FAILED; } if (((flags & MAP_PRIVATE) == MAP_PRIVATE) && ((file->f_mode & FMODE_READ) != FMODE_READ)) { fput(file); return (int)MAP_FAILED; } if (((flags & MAP_SHARED)==MAP_SHARED) && ((prot & PROT_WRITE) == PROT_WRITE) && /*(((file->f_mode & FMODE_READ )!=FMODE_READ)&&*/((file->f_mode &FMODE_WRITE)!=FMODE_WRITE)) { fput(file); return (int)MAP_FAILED; } if (((prot & PROT_WRITE)==PROT_WRITE)&&(file->f_mode==FMODE_APPEND)) { fput(file); return (int)MAP_FAILED; } if(file->f_vnode->vn_flags == VN_BUSY){ fput(file); return (int)MAP_FAILED; } } *ret = NULL; vmmap_t *map = curproc->p_vmmap; uint32_t lopage; uint32_t npages; vmarea_t *vma; lopage = ADDR_TO_PN(addr); uint32_t hipage = ADDR_TO_PN((size_t)addr + len - 1) + 1; /*uint32_t hipage = ADDR_TO_PN((size_t)addr + len) + 1;*/ npages = hipage - lopage; int dir = VMMAP_DIR_HILO; /* see elf32.c */ int retval; if ((flags & MAP_ANON) != MAP_ANON) { retval = vmmap_map(map, file->f_vnode, lopage, npages, prot, flags, off, dir, &vma); } else { retval = vmmap_map(map, NULL, lopage, npages, prot, flags, off, dir, &vma); } if(retval < 0){ if ((flags & MAP_ANON) != MAP_ANON) { fput(file); } dbg(DBG_PRINT,"Error: The mapping of the vmarea was unsuccessful\n"); return (int)MAP_FAILED; } *ret = PN_TO_ADDR (vma->vma_start); /* clear TLB for this vaddr*/ tlb_flush_range((uintptr_t)(*ret), npages); if ((flags & MAP_ANON) != MAP_ANON) { fput(file); } return 0; }
/* * This function implements the mmap(2) syscall, but only * supports the MAP_SHARED, MAP_PRIVATE, MAP_FIXED, and * MAP_ANON flags. * * Add a mapping to the current process's address space. * You need to do some error checking; see the ERRORS section * of the manpage for the problems you should anticipate. * After error checking most of the work of this function is * done by vmmap_map(), but remember to clear the TLB. */ int do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off, void **ret) { if (len == 0){ return -EINVAL; } if (!valid_map_type(flags)){ return -EINVAL; } if (!PAGE_ALIGNED(off)){ return -EINVAL; } if (!(flags & MAP_ANON) && (flags & MAP_FIXED) && !PAGE_ALIGNED(addr)){ return -EINVAL; } if (addr != NULL && (uint32_t) addr < USER_MEM_LOW){ return -EINVAL; } if (len > USER_MEM_HIGH){ return -EINVAL; } if (addr != NULL && len > USER_MEM_HIGH - (uint32_t) addr){ return -EINVAL; } if (addr == 0 && (flags & MAP_FIXED)){ return -EINVAL; } /* if ((!(flags & MAP_PRIVATE) && !(flags & MAP_SHARED))*/ /*|| ((flags & MAP_PRIVATE) && (flags & MAP_SHARED)))*/ /*{*/ /*return -EINVAL;*/ /*}*/ vnode_t *vnode; if (!(flags & MAP_ANON)){ if (!valid_fd(fd) || curproc->p_files[fd] == NULL){ return -EBADF; } file_t *f = curproc->p_files[fd]; vnode = f->f_vnode; if ((flags & MAP_PRIVATE) && !(f->f_mode & FMODE_READ)){ return -EACCES; } if ((flags & MAP_SHARED) && (prot & PROT_WRITE) && !((f->f_mode & FMODE_READ) && (f->f_mode & FMODE_WRITE))) { return -EACCES; } /*if ((prot & PROT_WRITE) && (f->f_mode & FMODE_APPEND)){*/ /*return -EACCES;*/ /*}*/ } else { vnode = NULL; } vmarea_t *vma; int retval = vmmap_map(curproc->p_vmmap, vnode, ADDR_TO_PN(addr), (uint32_t) PAGE_ALIGN_UP(len) / PAGE_SIZE, prot, flags, off, VMMAP_DIR_HILO, &vma); KASSERT(retval == 0 || retval == -ENOMEM); if (ret != NULL && retval >= 0){ *ret = PN_TO_ADDR(vma->vma_start); pt_unmap_range(curproc->p_pagedir, (uintptr_t) PN_TO_ADDR(vma->vma_start), (uintptr_t) PN_TO_ADDR(vma->vma_start) + (uintptr_t) PAGE_ALIGN_UP(len)); tlb_flush_range((uintptr_t) PN_TO_ADDR(vma->vma_start), (uint32_t) PAGE_ALIGN_UP(len) / PAGE_SIZE); } return retval; }
/* * This function implements the brk(2) system call. * * This routine manages the calling process's "break" -- the ending address * of the process's "dynamic" region (often also referred to as the "heap"). * The current value of a process's break is maintained in the 'p_brk' member * of the proc_t structure that represents the process in question. * * The 'p_brk' and 'p_start_brk' members of a proc_t struct are initialized * by the loader. 'p_start_brk' is subsequently never modified; it always * holds the initial value of the break. Note that the starting break is * not necessarily page aligned! * * 'p_start_brk' is the lower limit of 'p_brk' (that is, setting the break * to any value less than 'p_start_brk' should be disallowed). * * The upper limit of 'p_brk' is defined by the minimum of (1) the * starting address of the next occuring mapping or (2) USER_MEM_HIGH. * That is, growth of the process break is limited only in that it cannot * overlap with/expand into an existing mapping or beyond the region of * the address space allocated for use by userland. (note the presence of * the 'vmmap_is_range_empty' function). * * The dynamic region should always be represented by at most ONE vmarea. * Note that vmareas only have page granularity, you will need to take this * into account when deciding how to set the mappings if p_brk or p_start_brk * is not page aligned. * * You are guaranteed that the process data/bss region is non-empty. * That is, if the starting brk is not page-aligned, its page has * read/write permissions. * * If addr is NULL, you should NOT fail as the man page says. Instead, * "return" the current break. We use this to implement sbrk(0) without writing * a separate syscall. Look in user/libc/syscall.c if you're curious. * * Also, despite the statement on the manpage, you MUST support combined use * of brk and mmap in the same process. * * Note that this function "returns" the new break through the "ret" argument. * Return 0 on success, -errno on failure. */ int do_brk(void *addr, void **ret) { dbg(DBG_VM, "\n"); void *cur_sbrk = curproc->p_start_brk; vmarea_t *vma; vmarea_t *nvma = NULL; if (addr == NULL) { *ret = curproc->p_brk; return 0; } KASSERT((uintptr_t)curproc->p_brk >= (uintptr_t)curproc->p_start_brk); if ((uintptr_t)cur_sbrk > (uintptr_t)addr) { return -ENOMEM; } /* check for upper limits */ if ((uintptr_t)addr >= USER_MEM_HIGH) return -ENOMEM; uintptr_t pbrk_pg = ADDR_TO_PN(curproc->p_brk); uintptr_t addr_pg = ADDR_TO_PN(addr); (PAGE_ALIGNED(addr))? (addr_pg) : (addr_pg++); (PAGE_ALIGNED(curproc->p_brk))? (pbrk_pg) : (pbrk_pg++); /* if they resides in the same page, just update p_brk */ if (pbrk_pg == addr_pg) { curproc->p_brk = addr; *ret = addr; return 0; } /* Get dynamic region's vmarea */ vma = vmmap_lookup(curproc->p_vmmap, ADDR_TO_PN(cur_sbrk)); KASSERT(vma && "vmarea for the dynamic region is not found!"); /* check to see if vma has a next vma */ if (vma->vma_plink.l_next != &curproc->p_vmmap->vmm_list) { nvma = list_item(vma->vma_plink.l_next, vmarea_t, vma_plink); KASSERT(nvma && "next vmarea should exist but could not be found!"); } /* check for upper limits */ if (nvma && addr_pg > nvma->vma_start) return -ENOMEM; /* Now, update the vma, and curpor->p_brk */ if (pbrk_pg > addr_pg) { vmmap_remove(curproc->p_vmmap, addr_pg, (pbrk_pg - addr_pg)); tlb_flush_range((uintptr_t)PN_TO_ADDR(addr_pg), pbrk_pg - addr_pg); } else { vma->vma_end = addr_pg; } curproc->p_brk = addr; *ret = addr; return 0; }