int verify_variable_length(void* start) { if(start == NULL || start == 0 || start >= PHYS_BASE) { return false; } char* current_character; void* last_addr = pg_round_down(start); // Is first page OK? if(pagedir_get_page(thread_current()->pagedir, last_addr) == NULL){ return false; } for(current_character = start; ; current_character++){ // We have changed page, lets validate it. :-) if(last_addr != pg_round_down(current_character)){ last_addr = pg_round_down(current_character); if(last_addr == NULL || last_addr == 0 || last_addr >= PHYS_BASE){ return false; } if(pagedir_get_page(thread_current()->pagedir, last_addr) == NULL){ return false; } } // If we have come this far, we know the current page is OK. if(*current_character == '\0'){ return true; } } return false; }
/* * Maps the given user virtual address to the hash table's supplementary_page_table_entry. * i.e. Allocate the new supplementary_page_table_entry object and map it with * rounded down value of given virtual address */ struct supplementary_page_table_entry* allocate_supplementary_PTE ( void * vir_addr, bool write_only) { struct thread *current = thread_current (); struct supplementary_page_table_entry *page = malloc (sizeof ( *page )); if (page == NULL) { return page; } /* Initialize supplementary_page_table_entry with default values and given virtual address*/ page->sup_thread = current; page->supplementary_frame_in_memory = NULL; page->file_offset = 0; page->vaddr = pg_round_down (vir_addr); page->is_readonly = !write_only; page->swap_write = write_only; page->area_swap = (block_sector_t) -1; page->rw_bytes = 0; page->sup_file = NULL; // Insert this supplementary page table entry into supplementary page table if (hash_insert (current->supplementary_page_table, &page->elem) != NULL) { // If failed - release the object and return NULL free (page); page = NULL; } return page; }
bool grow_stack (void *uaddr) { void *upage = pg_round_down (uaddr); if((size_t)(PHYS_BASE - upage) > (1 << 23)) return false; struct spage_table_entry *spte = malloc (sizeof (struct spage_table_entry)); if (!spte) return false; spte->upage = upage; spte->in_memory = true; spte->writable = true; spte->spage_type = SWAP; spte->inevictable = true; uint8_t *f = frame_alloc (PAL_USER, spte); if (!f) { free (spte); return false; } if (!install_page (spte->upage, f, spte->writable)) { free (spte); frame_free (f); return false; } if (intr_context ()) spte->inevictable = false; return hash_insert (&thread_current ()->spage_table, &spte->elem) == NULL; }
/* Validates a user-provided pointer. Checks that it's in the required * range, and that it correpsonds to a page directory entry */ bool valid_user_pointer(const void *ptr) { uint32_t *pd, *pde; struct list_elem *e; struct vm_area_struct *iter; struct thread *t = thread_current(); void *esp = t->esp; /* Check if page in supplemental page table */ if (spt_present(t, pg_round_down(ptr))) { return true; } /* Special case for esp, must be in pagedir */ pd = active_pd(); pde = pd + pd_no(esp); /* If we have messed up esp */ if (*pde == 0) { return false; } /* If the pointer is above esp (but in user_vaddr), it may correspond to * a part of the stack that would be later loaded lazily on a pagefault. * So this should be considered a good pointer. */ if (is_user_vaddr(ptr) && ptr >= esp) { return true; } /* If we're here, this isn't a valid address */ return false; }
void stack_growth(void *vaddr){ uint32_t *paddr,*lru_page; if ((paddr = palloc_get_page(PAL_ZERO)) != NULL) { fte_create(paddr, false); install_page_ext (pg_round_down(vaddr), paddr, true); set_page_valid(pg_round_down(vaddr), paddr); } else { lru_page = lru_get_page(); swap_out(lru_page); set_page_valid(pg_round_down(vaddr), lru_page); } }
struct spage_table_entry * spage_table_find (void *uaddr) { struct spage_table_entry spte; spte.upage = pg_round_down (uaddr); struct hash_elem *e = hash_find (&thread_current()->spage_table, &spte.elem); if (!e) return NULL; return hash_entry (e, struct spage_table_entry, elem); }
/* * Converts a pointer to any address into a number from * 0 to USER_PAGES-1 (the amount of pages in the userpool) * This can be used to as an index to access the frames * within the frame table. */ static uint32_t page_to_pagenum(void *page) { // pages are consecutive and nothing can be before the base ASSERT(page >= frametable.base_addr); // remove offset within frame page = pg_round_down(page); // address shift relative to base addr page -= (uintptr_t) frametable.base_addr; return pg_no(page); }
/* Reads 'size' bytes from file open as fd into buffer. Returns number of bytes actually read - 0 at end of file, or -1 if file could not be read. fd = 0 reads from the keyboard. */ static int sys_read(int fd, void *buffer, unsigned size, struct intr_frame *f) { /* Cannot read from stdout. */ if (fd == STDOUT_FILENO) sys_exit(ERROR); int bytes; void *buf_iter = pg_round_down(buffer); check_fd(fd); check_buffer(buffer, size); lock_acquire(&secure_file); for (; buf_iter <= buffer + size; buf_iter += PGSIZE) { struct spt_entry *entry = get_spt_entry(&thread_current()->supp_pt, buf_iter); if (entry == NULL && should_stack_grow(buf_iter, f->esp)) { grow_stack(buf_iter); } } /* fd = 0 corresponds to reading from stdin. */ if (fd == STDIN_FILENO) { unsigned i; uint8_t keys[size]; /* Make an array of keys pressed. */ for (i = 0; i < size; i++) { keys[i] = input_getc(); } /* Put these keys pressed into the buffer. */ memcpy(buffer, (const void *) keys, size); /* Must have successfully read all bytes we were told to. */ bytes = size; } else { struct file *f = get_file(fd); if (!f) { lock_release(&secure_file); return ERROR; } bytes = file_read(f, buffer, size); } lock_release(&secure_file); return bytes; }
// Find the vm_entry object that got vaddr. struct vm_entry *find_vme (void *vaddr) { struct vm_entry vm; struct hash_elem *h; struct thread *cur = thread_current(); vm.vaddr = pg_round_down(vaddr); h = hash_find(&cur->vm, &vm.elem); if (h == NULL) { return h; } return hash_entry(h, struct vm_entry, elem); }
/*! Read from file */ int read(uint32_t fd, void *buffer, unsigned size) { uint8_t* addr_e; struct supp_table* st; /* Check the validity of given pointer */ if ((!checkva(buffer)) || (!checkva(buffer + size))){ exit(-1); } for (addr_e = (uint8_t*) pg_round_down(buffer); addr_e < (uint8_t*) buffer + size; addr_e += PGSIZE){ st = find_supp_table(addr_e); if (st && !st->writable) exit(-1); } int read_size = 0; if (fd == STDIN_FILENO) { /* If std-in, then read using input_getc() */ unsigned i; for (i = 0; i < size; i++){ *((uint8_t*) buffer) = input_getc(); ++ read_size; ++ buffer; } } else { /* Otherwise, first find the file of this fd. */ struct f_info* f = findfile(fd); /* We should read a dir like a file. */ if (f->isdir) exit(-1); struct file* fin = f->f; off_t pos = f->pos; /* Read from the file at f->pos */ //lock_acquire(&filesys_lock); read_size = (int) file_read_at(fin, buffer, (off_t) size, pos); f->pos += (off_t) read_size; //lock_release(&filesys_lock); } return read_size; }
/** * Get supplementary_page_table_entry for given virtual address -> * First finds it into the supplementary_page_table, if found returns * supplementary_page_table_entry object. * Else if its a new page for stack auto growth then try to allocate the new page, * Else if its not a stack-page just return NULL. */ static struct supplementary_page_table_entry *get_page_table_entry (const void *vaddr) { if (vaddr >= PHYS_BASE) { return NULL; } struct thread *current = thread_current (); struct supplementary_page_table_entry page1; struct hash_elem *elem; page1.vaddr = (void *) pg_round_down (vaddr); elem = hash_find (current->supplementary_page_table, &page1.elem); /* If page is present return the hash entry. */ if (elem != NULL) { return hash_entry(elem, struct supplementary_page_table_entry, elem); } else { // Check the virtual address of stack page within 1MB - maximum stack limit if ( ( vaddr >= ( PHYS_BASE - STACK_SIZE_LIMIT ) )
/* Given a virtual address (page) find a frame to put the page in and return the physical address of the frame */ void* frame_obtain (enum palloc_flags flags, void* uaddr) { struct frame_table_entry* fte; /* Try and obtain frame in user memory */ void *kaddr = palloc_get_page (flags); /* Successfully obtained frame */ if (kaddr != NULL) { /* Create new frame table entry mapping the given page to the allocated frame */ fte = (struct frame_table_entry *) malloc (sizeof (struct frame_table_entry)); fte->owner = thread_current (); fte->kaddr = kaddr; fte->uaddr = pg_round_down (uaddr); lock_acquire (&frame_table_lock); list_push_front (&frame_table, &fte->elem); lock_release (&frame_table_lock); return fte->kaddr; } /* Failed to obtain frame */ else { /* Perform eviction to release a frame and try allocation again */ if (PAGE_EVICTION) { return frame_evict (uaddr); } else { PANIC ("Failed to allocate frame - eviction disabled."); } } }
/* Kontrollera alla adresser från och med start till och inte med * (start+length). */ int verify_fix_length(void* start, int length) { if(start == NULL || start == 0 || start >= PHYS_BASE){ return false; } void* page_ptr = pg_round_down(start); while(page_ptr < start + length){ if(page_ptr == NULL || page_ptr == 0 || page_ptr >= PHYS_BASE ){ return false; } void* success = pagedir_get_page(thread_current()->pagedir, page_ptr); if(success == NULL){ return false; } page_ptr += PGSIZE; } return true; }
/* Creates a copy of user string US in kernel memory and returns it as a page that must be freed with palloc_free_page(). Truncates the string at PGSIZE bytes in size. Call thread_exit() if any of the user accesses are invalid. */ static char * copy_in_string (const char *us) { char *ks; char *upage; size_t length; ks = palloc_get_page (0); if (ks == NULL) thread_exit (); length = 0; for (;;) { upage = pg_round_down (us); if (!page_lock (upage, false)) goto lock_error; for (; us < upage + PGSIZE; us++) { ks[length++] = *us; if (*us == '\0') { page_unlock (upage); return ks; } else if (length >= PGSIZE) goto too_long_error; } page_unlock (upage); } too_long_error: page_unlock (upage); lock_error: palloc_free_page (ks); thread_exit (); }
void write (struct intr_frame *f) { int fd = *value_stack_int(f->esp,4); const void * buffer = *(void **)value_stack(f->esp,8); unsigned size = *value_stack_int(f->esp,12); if (fd == 1) { //break up the string in multilpe outputs if it is bigger than 256 int split = 256; int i = size / split; for (;i>0;i--) { putbuf(buffer,split); buffer += split; } putbuf(buffer,size % split); } else { unsigned fl_length = 0; if (fd == 0 || fd < 0 ) { f->eax = 0 ; return ; } struct filed * filed = find_file(fd); if (filed == NULL) { f->eax = 0 ; return ; } struct file * writeto = find_file(fd)->file; //check if it is a directory if ( file_direct(writeto) ) { f->eax = -1; return; } fl_length = file_length (writeto); //check if the actual file length is smaller than size //if(fl_length < size) size = fl_length; void * npage = pg_round_down (buffer); unsigned readsize = (unsigned) (npage + PGSIZE - buffer); bool read = true; unsigned bytes_read_total=0,bytes_read=0; //the new pagewise writing while(read){ struct page * p = page_lookup(buffer,thread_current()); //if the page is not her, we have to swap it in if (pagedir_get_page(thread_current()->pagedir,p->vaddr) == NULL) { page_swap_out(p->vaddr); } frame_lockdown(p->paddr); //everything fits in with one read if(size <= readsize){ bytes_read = file_write (writeto, buffer, size); read = false; } else { //now we have tos plit it up bytes_read= file_write (writeto, buffer, readsize); //couldn't read all the bytes if(bytes_read != readsize) read = false; size -= bytes_read; if(size == 0) read = false; else { buffer += bytes_read; if (size >= PGSIZE) readsize = PGSIZE; else readsize = size; } } //unlock the frame again frame_unlock(p->paddr); bytes_read_total+=bytes_read; } size = bytes_read_total; } //set eax to return value f->eax = size; }
void* frame_evict (void* uaddr) { /* 1. Choose a frame to evict, using your page replacement algorithm. The "accessed" and "dirty" bits in the page table, described below, will come in handy. */ struct frame_table_entry *fte = NULL; switch (PAGE_EVICTION_ALGORITHM) { /* First in first out */ case PAGE_EVICTION_FIFO: fte = frame_evict_choose_fifo (); break; /* Second chance */ case PAGE_EVICTION_SECONDCHANCE: fte = frame_evict_choose_secondchance (); break; default: PANIC ("Invalid eviction algorithm choice."); } ASSERT (fte != NULL); /* 2. Remove references to the frame from any page table that refers to it. Unless you have implemented sharing, only a single page should refer to a frame at any given time. */ pagedir_clear_page (fte->owner->pagedir, pg_round_down (fte->uaddr)); /* 3. If necessary, write the page to the file system or to swap. The evicted frame may then be used to store a different page. */ struct page *p_evict = page_lookup (fte->owner->pages, pg_round_down (fte->uaddr)); if (p_evict == NULL) PANIC ("Failed to get supp page for existing page."); /* Page to be evicted is in swap */ if (p_evict->page_location_option == FILESYS) { if (p_evict->writable) { file_write_at (p_evict->file, fte->kaddr, p_evict->page_read_bytes, p_evict->ofs); } } else if (p_evict->page_location_option == ALLZERO) { // All zero, so can just be overwritten } else { // From stack int index = swap_to_disk (pg_round_down (fte->uaddr)); /* Creates a supp page and insert it into pages. */ struct page *p = page_create (); if (p == NULL) PANIC ("Failed to get supp page for swap slot."); p->addr = fte->uaddr; p->page_location_option = SWAPSLOT; p->swap_index = index; page_insert (fte->owner->pages, &p->hash_elem); } /* Replace virtual address with new virtual address */ fte->owner = thread_current (); fte->uaddr = uaddr; /* Reinsert the frame table entry into the frame table */ lock_acquire (&frame_table_lock); list_remove (&fte->elem); list_push_front (&frame_table, &fte->elem); lock_release (&frame_table_lock); return fte->kaddr; }
void read (struct intr_frame *f) { int fd = *value_stack_int(f->esp,4); void * buffer = *(void **)value_stack(f->esp,8); unsigned size = *value_stack_int(f->esp,12); int ret_val = 1; if (fd == 0) { //read from stdin ret_val = input_getc(); } else { if (fd == 1 || fd < 0) { f->eax = 0; return ; } struct filed * filed = find_file(fd); if (filed == NULL) ret_val = 0; else { struct file * readfrom = find_file(fd)->file; void * npage = pg_round_down (buffer); unsigned readsize = (unsigned) (npage + PGSIZE - buffer); bool read = true; unsigned bytes_read_total=0,bytes_read=0; //the new pagewise reading while(read){ struct page * p = page_lookup(buffer,thread_current()); //if the page is not her, we have to swap it in if (pagedir_get_page(thread_current()->pagedir,p->vaddr) == NULL) { page_swap_out(p->vaddr); } if(!p->writable) exit_mythread(-1); frame_lockdown(p->paddr); //everything fits in with one read if(size <= readsize){ bytes_read = file_read(readfrom, buffer, size); read = false; } else { //now we have tos plit it up bytes_read= file_read(readfrom, buffer, readsize); //couldn't read all the bytes if(bytes_read != readsize) read = false; size -= bytes_read; if(size == 0) read = false; else { buffer += bytes_read; if (size >= PGSIZE) readsize = PGSIZE; else readsize = size; } } //unlock the frame again frame_unlock(p->paddr); bytes_read_total+=bytes_read; } ret_val = bytes_read_total; } } //set eax to return value f->eax = ret_val; }