static mapid_t syscall_mmap (int fd, void *addr) { if(fd == 0 || fd == 1 || addr == NULL || (int) addr % PGSIZE != 0) { return MAP_FAILED; } struct mmap_entry *mme = mmap_entry_from_fd (fd); if (mme == NULL) return MAP_FAILED; mme->uaddr = addr; struct supp_page_table *spt = thread_current ()->spt; unsigned i = 0; for(; i < mme->num_pages; i++) { if (!is_user_vaddr ((char *)addr + i * PGSIZE)|| supp_page_lookup (spt, (char *)addr + i * PGSIZE) != NULL) { free (mme); return MAP_FAILED; } } lock_acquire (&filesys_lock); file_reopen (mme->fp); lock_release (&filesys_lock); mme->map_id = (mapid_t) get_new_fd (fm, mme->fp, FM_MODE_MMAP); i = 0; for(; i < mme->num_pages; i++) { supp_page_insert (spt, (char *)addr + i * PGSIZE, FRAME_MMAP, mme, false); } mmap_table_insert (thread_current ()->mmt, mme); return mme->map_id; }
/* Maps the file open as FD into the process' virtual address space - entire file mapped into consecutive virtual pages starting at ADDR. (Lazy load pages in mmap regions). (Evicting a page mapped by mmap writes it back to the actual file it was mapped from). (Set spare bytes on final page to zero when that page is faulted in the file system, and ignore these bytes when page is written back to disk). Returns mapid_t for the mapping, or -1 on failure. Failure occurs when file has length 0, if addr is not page aligned, if range of pages mapped overlaps any existing mapped pages (including the stack or pages mapped at executable load time), if addr is 0, or if fd is 0 or 1. */ static mapid_t sys_mmap(int fd, void *addr) { /* Check that fd is a valid file descriptor. */ check_fd(fd); /* Cannot map stdin or stdout. */ if (fd == STDIN_FILENO || fd == STDOUT_FILENO) { return ERROR; } /* Address to map to cannot be 0, because some Pintos code assumes virtual page 0 is not mapped. */ if (addr == 0) { return ERROR; } int size = sys_filesize(fd); /* Cannot map a file of size 0 bytes. */ if (size == 0) { return ERROR; } /* ADDR must be page-aligned. */ if (pg_ofs(addr) != 0) { return ERROR; } /* If ADDR is not in user/process address space, we cannot map the file there. */ if (!is_user_vaddr(addr)) { sys_exit(ERROR); } /* Pages is number of pages needed to map file. (size % PGSIZE) gives the number of spare bytes on the final page. This is necessary because the division is integer division, and so will round down, but we want it to round up. */ int pages = size / PGSIZE; if ((size % PGSIZE) != 0) { pages++; } struct thread *cur = thread_current(); struct hash *mmap_table = &cur->mmap_table; struct hash *spt = &cur->supp_pt; lock_acquire(&secure_file); struct file *old_file = get_file(fd); if (!old_file) { lock_release(&secure_file); sys_exit(ERROR); } /* Must use file_reopen() to get independent 'struct file *' for same file (with same inode) because the file could be being read at different points (file->pos could be different) and they could have different file->deny_write (file_deny_write() could be called on one struct file but not another of same file (inode) but different struct file). */ struct file *file = file_reopen(old_file); lock_release(&secure_file); int i; int bytes_to_write; void *cur_page; /* Check that the contiguous range of pages to be mapped doesn't overlap any existing set of mapped pages (Not including stack). Can then add these pages to the supplementary page table. */ for (i = 0; i < pages; i++) { cur_page = (void *) ((uint8_t *) addr) + (i * PGSIZE); /* Check to see if there is an existing mapped page at what would be the i'th page of this mapped file. */ if (get_spt_entry(spt, cur_page) != NULL) { return ERROR; } /* Only on the last page do we potentially not fill up whole page with part of file. */ bytes_to_write = (i == (pages - 1)) ? (size % PGSIZE) : PGSIZE; /* Add current page to the supplementary page table. */ spt_insert_file(cur_page, file, bytes_to_write, PGSIZE - bytes_to_write, i * PGSIZE, true, true, false); } mapid_t mapid = cur->next_mapid; /* Lock must be acquired to call hash_insert() in mmap_table_insert(), and since we have thread_current() here already it makes sense to lock here rather than in mmap_table_insert() in mmap.c. */ lock_acquire(&cur->mmap_table_lock); bool success = mmap_table_insert(mmap_table, addr, addr + size, pages, mapid, file); lock_release(&cur->mmap_table_lock); /* Return -1 if mmap_table_insert wasn't successful (meaning there isn't enough space to malloc for a struct mmap_mapping *). */ if (!success) { return ERROR; } /* Increment next_mapid for this thread, so that the next mmap will have a different mapid, ensuring unique mapids for all mappings for a process. Increment after checking for mmap_table_insert() success status, because in the case of failure, we can reuse the mapid that the failed mapping would have had. */ cur->next_mapid++; /* If successful, function returns the mapid that uniquely identifies the mapping within the process. */ return mapid; }