void init_paging() { size_t sz; uint32_t i; uint32_t mem_end_page; DPRINTK("paging...\t\t"); mem_end_page = 0x1000000; nframes = mem_end_page / PAGE_SIZ; sz = INDEX_FROM_BIT(nframes); frames = (uint32_t *)kmalloc(sz); memset(frames, 0, sz); kernel_directory = (struct page_directory *) kmalloc_a(sizeof(struct page_directory)); memset(kernel_directory, 0, sizeof(struct page_directory)); // don't do this... current_directory = kernel_directory; // do this instead... kernel_directory->physical_addr = (uint32_t)kernel_directory->tables_physical; for (i = KHEAP_START; i < KHEAP_START + KHEAP_INITIAL_SIZE; i += PAGE_SIZ) get_page(i, 1, kernel_directory); i = 0; while (i < placement_addr + PAGE_SIZ) { alloc_frame(get_page(i, 1, kernel_directory), 0, 0); i += PAGE_SIZ; } for (i = KHEAP_START; i < KHEAP_START + KHEAP_INITIAL_SIZE; i += PAGE_SIZ) alloc_frame(get_page(i, 1, kernel_directory), 0, 0); // register_interrupt_handler(14, page_fault); switch_page_directory(kernel_directory); enable_paging(); kheap = create_heap(KHEAP_START, KHEAP_START + KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0); current_directory = clone_directory(kernel_directory); switch_page_directory(current_directory); DPRINTK("done!\n"); }
void initialize_paging(uint32_t memsize) { nframes = memsize / 4; frames = (uint32_t *)kmalloc(INDEX_FROM_BIT(nframes)); uintptr_t pg; assert(frames != NULL); memset(frames, 0, INDEX_FROM_BIT(nframes)); uintptr_t physical; kernel_directory = (page_directory_t *)kmalloc_ap(sizeof(page_directory_t), &physical); memset(kernel_directory, 0, sizeof(page_directory_t)); current_directory = kernel_directory; #if 1 get_page(0,1,kernel_directory)->present = 0; set_frame(0); for(uintptr_t i = 0x1000; i < placement_address+0x3000; i += 0x1000) #else for(uintptr_t i = 0x0; i < placement_address+0x3000; i += 0x1000) #endif { direct_frame( get_page(i, 1, kernel_directory), 1, 0, i); } kernel_directory->physical_addr = (uintptr_t)kernel_directory->tables_physical; uintptr_t heap_start = KERNEL_HEAP_START; if(heap_start <= placement_address + 0x3000) { heap_start = placement_address + 0x100000; } for (uintptr_t i = placement_address + 0x3000; i < heap_start; i += 0x1000) { alloc_frame(get_page(i, 1, kernel_directory), 1, 0); } for(uintptr_t i = heap_start; i < heap_start + KERNEL_HEAP_INIT; i += 0x1000) { get_page(i, 1, kernel_directory); } for(uintptr_t i = heap_start; i < heap_start + KERNEL_HEAP_INIT; i += 0x1000) { alloc_frame(get_page(i, 1, kernel_directory), 0, 0); } register_isr_handler(13, general_protection_fault); register_isr_handler(14, page_fault); switch_page_directory(kernel_directory); kernel_heap = create_heap(heap_start, heap_start + KERNEL_HEAP_INIT, KERNEL_HEAP_END, 0, 0); //kernel_heap = create_heap(heap_start, KERNEL_HEAP_END, KERNEL_HEAP_END, 0, 0); }
uint32_t exec_elf(const char* name) { fs_node_t* file = finddir(fs_root, name); open(file, 1, 0); // disable interrupts asm volatile("cli"); // save directory and create one for new task page_directory_t* old_dir = current_directory; page_directory_t* new_dir = clone_directory(old_dir); // switch to new directory (this is where we set up process) switch_page_directory(new_dir); // load elf and get entry point uint32_t entry = load_elf_binary(file); // setup stack uint32_t stack = (uint32_t) allocate_stack(THREAD_STACK_SIZE); // create scheduling structures thread_t* new_thread = create_thread((int(*)(void*)) entry, 0, (uint32_t*) stack, THREAD_STACK_SIZE); task_t* new_task = create_task(new_thread, new_dir); schedule_add_task(new_task); // switch back to parents directory switch_page_directory(old_dir); // close executable image close(file); // reenable interrupts asm volatile("sti"); // like in fork return child pid return new_task->pid; }
int execve(const char *path, char *argv[], char *envp[]) { INTERRUPT_LOCK; int r = elf_load_int(path, (task_t *)current_task, argv, envp); kfree((void *)path); // argv and envp are freed in elf_load_int argv = envp = NULL; if (r == 0) { assert(interrupts_enabled() == false); current_task->state = TASK_RUNNING; destroy_user_page_dir(current_task->old_mm->page_directory); vmm_destroy_task_mm(current_task->old_mm); current_task->old_mm = NULL; current_task->did_execve = true; current_task->esp = (uint32)current_task->stack - 84 + 12; // Overwrite the task's stack with new, zeroed values for registers etc. set_task_stack((task_t *)current_task, NULL, 0, 0); assert(current_task->new_entry > 0x100000); set_entry_point((task_t *)current_task, current_task->new_entry); assert(current_directory == current_task->mm->page_directory); //current_task->esp = ((uint32)((uint32)USER_STACK_START - sizeof(registers_t))); INTERRUPT_UNLOCK; YIELD; panic("returned past execve()!"); } else { assert(r < 0); // execve failed! Let's undo most of the work, and return. struct task_mm *new_mm = current_task->mm; current_task->mm = current_task->old_mm; switch_page_directory(current_task->mm->page_directory); destroy_user_page_dir(new_mm->page_directory); vmm_destroy_task_mm(new_mm); INTERRUPT_UNLOCK; return r; } return 0; // To silence warnings }
// this is main() function for other processors static int __main(cpu_state_t * cpu) { // init AP init_application_processor(); // init segmentation init_global_descriptor_table(cpu); init_interrupt_descriptor_table(); // enable local interrupt local_irq_enable(); // init paging switch_page_directory(k_pdir); // init sched init_sched(); return 0xFADEFADE; }
int system( char * path, /* Path to the executable to run */ int argc, /* Argument count (ie, /bin/echo hello world = 3) */ char ** argv /* Argument strings (including executable path) */ ) { char ** argv_ = malloc(sizeof(char *) * (argc + 1)); for (int j = 0; j < argc; ++j) { argv_[j] = malloc((strlen(argv[j]) + 1) * sizeof(char)); memcpy(argv_[j], argv[j], strlen(argv[j]) + 1); } argv_[argc] = 0; char * env[] = {NULL}; set_process_environment((process_t*)current_process, clone_directory(current_directory)); current_directory = current_process->thread.page_directory; switch_page_directory(current_directory); exec(path,argc,argv_,env); debug_print(ERROR, "Failed to execute process!"); kexit(-1); return -1; }
void vma_nopage(vma_t *vma, u32int vaddr) { if (!vma) return; /*printk("cr2 %p\n", current_task->dir->addr);*/ /*MAGIC_BREAK();*/ alloc_frame(get_page(vaddr, 1, current_task->dir), 1, vma->flags & VMA_WRITE); switch_page_directory(current_task->dir); if (vma->flags & VMA_ANON) { if (vma->flags & VMA_STACK) { vma->vm->end_stack -= 0x1000; vma->start -= 0x1000; } return; } if (vma->flags & VMA_FILE) { file_mapping_t *f_map = (file_mapping_t*)vma->priv; u32int size = 0x1000; u32int off = vaddr - vma->start; if (off >= f_map->size) { memset((void*)vaddr, 0, size); } else { file_lseek(f_map->file, f_map->offset + off, SEEK_SET); if (off + size > f_map->size) { /*printk("file_read: %p, size: %d\n", vaddr, f_map->size - off);*/ /*printk("memset: %p, size: %d\n", vma->start+f_map->size, size + off - f_map->size);*/ file_read(f_map->file, (u8int*)vaddr, f_map->size - off); memset((void*)(vma->start + f_map->size), 0, size + off - f_map->size); } else { /*printk("file_read: %p, size: %d\n", vaddr, size);*/ file_read(f_map->file, (u8int*)vaddr, size); } } } }
static int elf_load_int(const char *path, task_t *task, char *argv[], char *envp[]) { // Loads to a fixed address of 0x10000000 for now; not a HUGE deal // since each (user mode) task has its own address space assert(interrupts_enabled() == false); // TODO: get rid of the race condition from create_task, so that this isn't needed assert(task != NULL); struct task_mm *mm = task->mm; assert(mm != NULL); struct stat st; int r; if ((r = stat(path, &st)) != 0) { assert(r < 0); return r; } uint32 file_size = st.st_size; unsigned char *data = kmalloc(file_size); int retval = 0; int fd = open(path, O_RDONLY); if (fd < 0) { printk("elf_load(): unable to open %s\n", path); retval = fd; goto err; } if ((r = read(fd, data, file_size)) != (int)file_size) { printk("elf_load(): unable to read from %s; got %d bytes, requested %d\n", path, r, (int)file_size); if (r < 0) { retval = r; goto err; } else { panic("read() returned less than the expected file size, but not a negative value... why?"); retval = -EIO; goto err; } } close(fd); elf_header_t *header = (elf_header_t *)data; const unsigned char ELF_IDENT[] = {0x7f, 'E', 'L', 'F'}; if (memcmp(header->e_ident.ei_mag, &ELF_IDENT, 4) != 0) { printk("Warning: file %s is not an ELF file; aborting execution\n", path); retval = -ENOEXEC; goto err; } // TODO SECURITY: don't trust anything from the file - users can EASILY execute // "forged" ELF files! if (header->e_ident.ei_class != ELFCLASS32 || header->e_ident.ei_data != ELFDATA2LSB || \ header->e_ident.ei_version != 1 || header->e_machine != EM_386 || header->e_type != ET_EXEC) { printk("Warning: file %s is not a valid ELF file (invalid ELFCLASS, ELFDATA, version, machine or not ET_EXEC\n"); retval = -ENOEXEC; goto err; } assert(header->e_entry >= 0x10000000); assert(header->e_entry < 0x11000000); if (task == current_task) { // execve assert(current_task->mm != NULL); assert(current_task->mm->areas != NULL); assert(current_task->mm->page_directory != NULL); } for (int i=0; i < header->e_phnum; i++) { Elf32_Phdr *phdr = (Elf32_Phdr *)(data + header->e_phoff + header->e_phentsize * i); if (phdr->p_type == PT_LOAD) { // This is a segment to load! // Should this be writable to the task? bool writable = ((phdr->p_flags & PF_W) ? true : false); #if ELF_DEBUG printk("Segment #%u: copy %u bytes from 0x%08x (data + offset) to 0x%08x (virt in task page dir); read%s\n", i, phdr->p_filesz, data + phdr->p_offset, phdr->p_vaddr, writable ? "-write" : "only"); #endif if (i == 0) assert(phdr->p_vaddr == 0x10000000); else assert(phdr->p_vaddr > 0x10000000); uint32 start_addr = phdr->p_vaddr; uint32 start_addr_aligned = (phdr->p_vaddr & 0xfffff000); uint32 end_addr = start_addr + phdr->p_memsz; if (!IS_PAGE_ALIGNED(end_addr)) { end_addr &= ~(PAGE_SIZE - 1); end_addr += PAGE_SIZE; } if (end_addr > task->mm->brk_start) { uint32 new_brk = end_addr; if (!IS_PAGE_ALIGNED(new_brk)) { new_brk &= ~(PAGE_SIZE - 1); new_brk += PAGE_SIZE; } task->mm->brk_start = new_brk; task->mm->brk = new_brk; } // Allocate memory for this address in the task's address space, set for user mode vmm_alloc_user(start_addr_aligned, end_addr, mm, writable); // Switch to the new page directory, so that we can copy the data there page_directory_t *old_dir = current_directory; switch_page_directory(mm->page_directory); // Okay, we should have the memory. Let's clear it (since PARTS may be left empty by the memcpy, // e.g. the .bss section, and we do want zeroes to be there) memset((void *)start_addr_aligned, 0, end_addr - start_addr_aligned); // Copy the segment (e.g. .text + .rodata + .eh_frame, or .data + .bss) to the location // DO NOT use start_addr_aligned here - we want the program to dictate the exact location memcpy((void *)start_addr, data + phdr->p_offset, phdr->p_filesz); switch_page_directory(old_dir); } else if (phdr->p_type == PT_GNU_STACK || phdr->p_type == PT_GNU_RELRO || phdr->p_type == PT_GNU_EH_FRAME) { // Quietly ignore } else printk("Warning: skipping unsupported ELF program header (#%u, p_type = 0x%x)\n", i, phdr->p_type); } // Set up the reentrancy structure for Newlib // (It is initialized below, after switching to the new page directory.) uint32 reent_size = sizeof(struct _reent); if (reent_size & 0xfff) { reent_size &= 0xfffff000; reent_size += PAGE_SIZE; } vmm_alloc_user(task->mm->brk, task->mm->brk + reent_size, mm, PAGE_RW); //assert(current_directory == kernel_directory); page_directory_t *old_dir = current_directory; switch_page_directory(task->mm->page_directory); task->reent = (struct _reent *)task->mm->brk; _REENT_INIT_PTR(task->reent); task->mm->brk += reent_size; task->mm->brk_start += reent_size; assert(IS_PAGE_ALIGNED(task->mm->brk)); assert(task->mm->brk == task->mm->brk_start); // The value brk has when the process starts; // userspace may not decrease the brk point below this address task->mm->initial_brk = task->mm->brk_start; // Copy the argv data from the kernel heap to the task's address space // This function updates argv to point to the new location. uint32 argc = 0; for (; argv[argc] != NULL; argc++) { } copy_argv_env_to_task(&argv, argc, task); uint32 envc = 0; assert(envp != NULL); for (; envp[envc] != NULL; envc++) { } copy_argv_env_to_task(&envp, envc, task); *((uint32 *)(USER_STACK_START - 0)) = (uint32)envp; *((uint32 *)(USER_STACK_START - 4)) = (uint32)argv; *((uint32 *)(USER_STACK_START - 8)) = (uint32)argc; // Update the task's name strlcpy((char *)task->name, argv[0], TASK_NAME_LEN); if (old_dir != kernel_directory) { // execve, stay with the new dir } else switch_page_directory(old_dir); #if ELF_DEBUG printk("File has %u program headers (each %u bytes), %u section headers (each %u bytes)\n", header->e_phnum, header->e_phentsize, header->e_shnum, header->e_shentsize); printk("Program Header:\n"); for (int i=0; i < header->e_phnum; i++) { Elf32_Phdr *phdr = (Elf32_Phdr *)(data + header->e_phoff + header->e_phentsize * i); if (phdr->p_type == PT_LOAD) { printk("LOAD offset 0x%08x vaddr 0x%08x alignment %u bytes\n", phdr->p_offset, phdr->p_vaddr, phdr->p_align); unsigned int f = phdr->p_flags; printk(" filesz 0x%08x memsz 0x%08x flags %c%c%c\n", phdr->p_filesz, phdr->p_memsz, (f & PF_R ? 'r' : '-'), (f & PF_W ? 'w' : '-'), (f & PF_X ? 'x' : '-')); } else { printk("unsupported program header (#%u), skipping\n", i); } } // Find the string table assert(header->e_shoff != 0); // we need a section header Elf32_Shdr *string_table_hdr = (Elf32_Shdr *)(data + header->e_shoff + header->e_shentsize * header->e_shstrndx); char *string_table = (char *)(data + string_table_hdr->sh_offset); printk("Sections:\n"); printk("Idx Name Size VMA LMA File off Align\n"); for (int i=1; i < header->e_shnum; i++) { // skip #0, which is always empty Elf32_Shdr *shdr = (Elf32_Shdr *)(data + header->e_shoff + header->e_shentsize * i); char *name = (char *)&string_table[shdr->sh_name]; printk("%03d %12s %08x %08x %08x %08x %u\n", i, name, shdr->sh_size, shdr->sh_addr, shdr->sh_addr /* TODO: LMA */, shdr->sh_offset, shdr->sh_addralign); unsigned int f = shdr->sh_flags; printk(" "); if (shdr->sh_type != SHT_NOBITS) printk("CONTENTS, "); if ((f & SHF_ALLOC)) printk("ALLOC, "); if ((f & SHF_WRITE) == 0) printk("READONLY, "); if ((f & SHF_EXECINSTR)) printk("CODE\n"); else printk("DATA\n"); } #endif // ELF_DEBUG // Try to find symbols, so we can get nice backtrace displays Elf32_Sym *symhdr = NULL; uint32 num_syms = 0; const char *sym_string_table = NULL; uint32 string_table_size = 0; for (uint32 i=1; i < header->e_shnum; i++) { // skip #0, which is always empty Elf32_Shdr *shdr = (Elf32_Shdr *)((uint32)data + header->e_shoff + (header->e_shentsize * i)); if (shdr->sh_type == SHT_SYMTAB) { symhdr = (Elf32_Sym *)(data + shdr->sh_offset); num_syms = shdr->sh_size / shdr->sh_entsize; Elf32_Shdr *string_table_hdr = (Elf32_Shdr *)((uint32)data + header->e_shoff + shdr->sh_link * header->e_shentsize); string_table_size = string_table_hdr->sh_size; sym_string_table = (char *)(data + string_table_hdr->sh_offset); break; } } // Load symbols for this file, so that we can display them in backtraces if (!symhdr || !sym_string_table || num_syms < 1) { printk("Warning: failed to load symbols for %s\n", path); } else { // Clone the string table. Because load_symbols doesn't strdup() names // for performance reasons, we need the string table to keep existing // for as long as the task lives. char *old_table = task->symbol_string_table; task->symbol_string_table = kmalloc(string_table_size); task->symbol_string_table_size = string_table_size; memcpy(task->symbol_string_table, sym_string_table, string_table_size); if (load_symbols(symhdr, task->symbol_string_table, &task->symbols, num_syms) != 0) { printk("Warning: failed to load symbols for %s\n", path); } else if (old_table) { // execve, so free the old one, or it'll leak kfree(old_table); } } // If we're still here: set the program entry point // (This updates the value on the stack in task.c) task->new_entry = (uint32)header->e_entry; set_entry_point((task_t *)task, task->new_entry); retval = 0; /* fall through on success */ err: kfree(data); assert(retval <= 0); return retval; }
int free_frame(frame_t * frame) { intmask mask; mask = disable(); //LOG("Freeing"); //print_frame(frame); if(frame->id <5) { LOG(" WHAT THE F**K %d %d", frame->id, frame->type); restore(mask); return OK; } //kprintf("id %d type %d ", frame->id, frame->type); //print_fifo_list(); //kprintf("\n"); if(frame == NULL) { restore(mask); return SYSERR; } else if(!FRAMEID_IS_VALID(frame->id)) { restore(mask); return SYSERR; } else if(frame->type == FREE) { restore(mask); return OK; } else if(frame->type == PAGE){ //print_fifo_list(); //LOG("Got here 0.5"); //3. Using the inverted page table, get vp, the virtual page number of the page to be replaced. uint32 vp = frame->vp_no; //4. Let a be vp*4096 (the first virtual address on page vp). hook_pswap_out(vp, frame->id + FRAME0); uint32 a = vp*PAGE_SIZE; virtual_addr * virt = (virtual_addr *) &a; //5. Let p be the high 10 bits of a. Let q be bits [21:12] of a. uint32 p = virt->page_directory_offset; uint32 q = virt->page_table_offset; //6. Let pid be the process id of the process owning vp. pid32 pid = frame->pid; //7. Let pd point to the page directory of process pid. struct procent *prptr; /* Ptr to process table entry */ prptr = &proctab[pid]; pd_t * pd = prptr->pagedir; if( pd == NULL) { LOG(" pd doesn't exist "); restore(mask); return SYSERR; } bool8 pt_pres = FALSE; pt_pres = (bool8) pd[p].pd_pres; bool8 pg_pres = FALSE; bool8 dirty = FALSE; if(pt_pres) { //8. Let pt point to the pid's p_th page table. pt_t * pt = (pt_t *) ((pd[p].pd_base) * PAGE_SIZE); pg_pres = (bool8) pt[q].pt_pres; uint32 pg_base = (uint32) pt[q].pt_base; if(pg_pres){ if((uint32)FRAMEID_TO_VPAGE(frame->id) == pg_base) { pg_pres = TRUE; dirty = pt[q].pt_dirty; } else { pg_pres = FALSE; } } } if(pg_pres) { frame_t * pt_frame = &frames[(pd[p].pd_base) - FRAME0]; pt_t * pt = (pt_t *) ((pd[p].pd_base) * PAGE_SIZE); //9. Mark the appropriate entry of pt as not present. pt[q].pt_pres = 0; if(pt_frame->type == VPTBL){ decr_frame_refcount(pt_frame); if(pt_frame->refcount == 0){ pd[p].pd_pres = 0; free_frame(pt_frame); } bzero((char *)&pt[q], sizeof(pt_t)); } else if(pt_frame->type == GPTBL) { // kprintf(" Uh OH"); } // If the reference count has reached zero, you should mark the appropriate entry in pd as "not present." // This conserves frames by keeping only page tables which are necessary. //LOG("Got here 1.5"); //If the dirty bit for page vp was set in its page table, you must do the following: //a) Using the backing store map, find the store and page offset within the store, given pid and a. // If the lookup fails, something is wrong. Print an error message and kill the process with id pid. //b) Write the page back to the backing store. //LOG("Got here 2"); if(dirty){ bsd_t bs_store_id; int bs_store_page_offset; if(SYSERR == bs_map_check(pid, vp, &bs_store_id, &bs_store_page_offset)) { kprintf("FATAL :Can't find the bs_map"); restore(mask); kill(currpid); return SYSERR; } //print_frame(frame); if(BACKSTORE_ID_IS_VALID(frame->backstore) && BACKSTORE_OFFSET_IS_VALID(frame->backstore_offset) && frame->backstore == bs_store_id && frame->backstore_offset == bs_store_page_offset) { //LOG("Frame %d was dirty", frame->id); open_bs(frame->backstore); write_bs(FRAMEID_TO_PHYSICALADDR(frame->id), frame->backstore, frame->backstore_offset); close_bs(frame->backstore); } //else //{ // print_frame(frame); // kprintf("Fatal error: Cannot locate backstore for vpage %d to swap out page for pid %d ", vp, pid); // kill(pid); // initialize_frame(frame); // restore(mask); // return SYSERR; //} } else{ //print_frame(frame); } } //LOG("Got here 1"); //10. If the page being removed belongs to the current process, // invalidate the TLB entry for the page vp, using the invlpg instruction (see Intel Manual, volumes II and III for more details on this instruction). // 11. In the inverted page table, decrement the reference count of the frame occupied by pt. //LOG(" Free frame"); //print_frame(frame); enable_paging(); initialize_frame(frame); // Update page table entries associated with this frame // set the frame to be free } else if(frame->type == VPTBL) { evict_from_fifo_list(frame); hook_ptable_delete(frame->id + FRAME0); enable_paging(); initialize_frame(frame); } else if(frame->type == DIR) { struct procent * prptrNULL = &proctab[NULLPROC]; pd_t * null_pg_dir = prptrNULL->pagedir; struct procent *prptr; /* Ptr to process table entry */ prptr = &proctab[currpid]; if(prptr->pagedir!= null_pg_dir) { evict_from_fifo_list(frame); prptr->pagedir = prptrNULL->pagedir; switch_page_directory(prptr->pagedir); enable_paging(); initialize_frame(frame); } } restore(mask); return OK; }