int kmodule_parse(char *data, t_elfparse *ep, t_elfparse_symb **ksym) { Elf32_Ehdr *h; h = (Elf32_Ehdr*)data; if (elf_parse_header(h, ep) < 0) return (-1); if (elf_parse_sections(h, ep) < 0) return (-1); if (elf_parse_symb(h, ep) < 0) return (-1); if (kmodule_bind_got(h, ep, *ksym) < 0) return (-1); return (0); }
/** * Starts one userland process. The thread calling this function will * be used to run the process and will therefore never return from * this function. This function asserts that no errors occur in * process startup (the executable file exists and is a valid ecoff * file, enough memory is available, file operations succeed...). * Therefore this function is not suitable to allow startup of * arbitrary processes. * * @executable The name of the executable to be run in the userland * process */ void process_start(uint32_t pid) { thread_table_t *my_entry; pagetable_t *pagetable; uint32_t phys_page; context_t user_context; uint32_t stack_bottom; elf_info_t elf; openfile_t file; const char* executable; int i; interrupt_status_t intr_status; my_entry = thread_get_current_thread_entry(); my_entry->process_id = pid; executable = process_table[pid].executable; /* If the pagetable of this thread is not NULL, we are trying to run a userland process for a second time in the same thread. This is not possible. */ KERNEL_ASSERT(my_entry->pagetable == NULL); pagetable = vm_create_pagetable(thread_get_current_thread()); KERNEL_ASSERT(pagetable != NULL); intr_status = _interrupt_disable(); my_entry->pagetable = pagetable; _interrupt_set_state(intr_status); file = vfs_open((char *)executable); /* Make sure the file existed and was a valid ELF file */ KERNEL_ASSERT(file >= 0); KERNEL_ASSERT(elf_parse_header(&elf, file)); /* Trivial and naive sanity check for entry point: */ KERNEL_ASSERT(elf.entry_point >= PAGE_SIZE); /* Calculate the number of pages needed by the whole process (including userland stack). Since we don't have proper tlb handling code, all these pages must fit into TLB. */ KERNEL_ASSERT(elf.ro_pages + elf.rw_pages + CONFIG_USERLAND_STACK_SIZE <= _tlb_get_maxindex() + 1); /* Allocate and map stack */ for(i = 0; i < CONFIG_USERLAND_STACK_SIZE; i++) { phys_page = pagepool_get_phys_page(); KERNEL_ASSERT(phys_page != 0); vm_map(my_entry->pagetable, phys_page, (USERLAND_STACK_TOP & PAGE_SIZE_MASK) - i*PAGE_SIZE, 1); } /* Allocate and map pages for the segments. We assume that segments begin at page boundary. (The linker script in tests directory creates this kind of segments) */ for(i = 0; i < (int)elf.ro_pages; i++) { phys_page = pagepool_get_phys_page(); KERNEL_ASSERT(phys_page != 0); vm_map(my_entry->pagetable, phys_page, elf.ro_vaddr + i*PAGE_SIZE, 1); } for(i = 0; i < (int)elf.rw_pages; i++) { phys_page = pagepool_get_phys_page(); KERNEL_ASSERT(phys_page != 0); vm_map(my_entry->pagetable, phys_page, elf.rw_vaddr + i*PAGE_SIZE, 1); } /* Put the mapped pages into TLB. Here we again assume that the pages fit into the TLB. After writing proper TLB exception handling this call should be skipped. */ //intr_status = _interrupt_disable(); //tlb_fill(my_entry->pagetable); //_interrupt_set_state(intr_status); /* Now we may use the virtual addresses of the segments. */ /* Zero the pages. */ memoryset((void *)elf.ro_vaddr, 0, elf.ro_pages*PAGE_SIZE); memoryset((void *)elf.rw_vaddr, 0, elf.rw_pages*PAGE_SIZE); stack_bottom = (USERLAND_STACK_TOP & PAGE_SIZE_MASK) - (CONFIG_USERLAND_STACK_SIZE-1)*PAGE_SIZE; memoryset((void *)stack_bottom, 0, CONFIG_USERLAND_STACK_SIZE*PAGE_SIZE); /* Copy segments */ if (elf.ro_size > 0) { /* Make sure that the segment is in proper place. */ KERNEL_ASSERT(elf.ro_vaddr >= PAGE_SIZE); KERNEL_ASSERT(vfs_seek(file, elf.ro_location) == VFS_OK); KERNEL_ASSERT(vfs_read(file, (void *)elf.ro_vaddr, elf.ro_size) == (int)elf.ro_size); } if (elf.rw_size > 0) { /* Make sure that the segment is in proper place. */ KERNEL_ASSERT(elf.rw_vaddr >= PAGE_SIZE); KERNEL_ASSERT(vfs_seek(file, elf.rw_location) == VFS_OK); KERNEL_ASSERT(vfs_read(file, (void *)elf.rw_vaddr, elf.rw_size) == (int)elf.rw_size); } /* Set the dirty bit to zero (read-only) on read-only pages. */ for(i = 0; i < (int)elf.ro_pages; i++) { vm_set_dirty(my_entry->pagetable, elf.ro_vaddr + i*PAGE_SIZE, 0); } /* Insert page mappings again to TLB to take read-only bits into use */ //intr_status = _interrupt_disable(); //tlb_fill(my_entry->pagetable); //_interrupt_set_state(intr_status); /* Initialize the user context. (Status register is handled by thread_goto_userland) */ memoryset(&user_context, 0, sizeof(user_context)); user_context.cpu_regs[MIPS_REGISTER_SP] = USERLAND_STACK_TOP; user_context.pc = elf.entry_point; vfs_close(file); thread_goto_userland(&user_context); KERNEL_PANIC("thread_goto_userland failed."); }
/* Return non-zero on error. */ int setup_new_process(TID_t thread, const char *executable, const char **argv_src, virtaddr_t *entry_point, virtaddr_t *stack_top) { pagetable_t *pagetable; elf_info_t elf; openfile_t file; uintptr_t phys_page; int i, res; thread_table_t *thread_entry = thread_get_thread_entry(thread); int argc = 1; virtaddr_t argv_begin; virtaddr_t argv_dst; int argv_elem_size; virtaddr_t argv_elem_dst; file = vfs_open((char *)executable); /* Make sure the file existed and was a valid ELF file */ if (file < 0) { return -1; } res = elf_parse_header(&elf, file); if (res < 0) { return -1; } /* Trivial and naive sanity check for entry point: */ if (elf.entry_point < PAGE_SIZE) { return -1; } *entry_point = elf.entry_point; pagetable = vm_create_pagetable(thread); thread_entry->pagetable = pagetable; /* Allocate and map stack */ for(i = 0; i < CONFIG_USERLAND_STACK_SIZE; i++) { phys_page = physmem_allocblock(); KERNEL_ASSERT(phys_page != 0); /* Zero the page */ memoryset((void*)ADDR_PHYS_TO_KERNEL(phys_page), 0, PAGE_SIZE); vm_map(pagetable, phys_page, (USERLAND_STACK_TOP & PAGE_SIZE_MASK) - i*PAGE_SIZE, 1); } /* Allocate and map pages for the segments. We assume that segments begin at page boundary. (The linker script in tests directory creates this kind of segments) */ for(i = 0; i < (int)elf.ro_pages; i++) { int left_to_read = elf.ro_size - i*PAGE_SIZE; phys_page = physmem_allocblock(); KERNEL_ASSERT(phys_page != 0); /* Zero the page */ memoryset((void*)ADDR_PHYS_TO_KERNEL(phys_page), 0, PAGE_SIZE); /* Fill the page from ro segment */ if (left_to_read > 0) { KERNEL_ASSERT(vfs_seek(file, elf.ro_location + i*PAGE_SIZE) == VFS_OK); KERNEL_ASSERT(vfs_read(file, (void*)ADDR_PHYS_TO_KERNEL(phys_page), MIN(PAGE_SIZE, left_to_read)) == (int) MIN(PAGE_SIZE, left_to_read)); } vm_map(pagetable, phys_page, elf.ro_vaddr + i*PAGE_SIZE, 0); } for(i = 0; i < (int)elf.rw_pages; i++) { int left_to_read = elf.rw_size - i*PAGE_SIZE; phys_page = physmem_allocblock(); KERNEL_ASSERT(phys_page != 0); /* Zero the page */ memoryset((void*)ADDR_PHYS_TO_KERNEL(phys_page), 0, PAGE_SIZE); /* Fill the page from rw segment */ if (left_to_read > 0) { KERNEL_ASSERT(vfs_seek(file, elf.rw_location + i*PAGE_SIZE) == VFS_OK); KERNEL_ASSERT(vfs_read(file, (void*)ADDR_PHYS_TO_KERNEL(phys_page), MIN(PAGE_SIZE, left_to_read)) == (int) MIN(PAGE_SIZE, left_to_read)); } vm_map(pagetable, phys_page, elf.rw_vaddr + i*PAGE_SIZE, 1); } /* Set up argc and argv on the stack. */ /* Start by preparing ancillary information for the new process argv. */ if (argv_src != NULL) for (i = 0; argv_src[i] != NULL; i++) { argc++; } argv_begin = USERLAND_STACK_TOP - (argc * sizeof(virtaddr_t)); argv_dst = argv_begin; /* Prepare for copying executable. */ argv_elem_size = strlen(executable) + 1; argv_elem_dst = argv_dst - wordpad(argv_elem_size); /* Copy executable to argv[0] location. */ vm_memwrite(pagetable, argv_elem_size, argv_elem_dst, executable); /* Set argv[i] */ vm_memwrite(pagetable, sizeof(virtaddr_t), argv_dst, &argv_elem_dst); /* Move argv_dst to &argv[1]. */ argv_dst += sizeof(virtaddr_t); if (argv_src != NULL) { for (i = 0; argv_src[i] != NULL; i++) { /* Compute the size of argv[i+1] */ argv_elem_size = strlen(argv_src[i]) + 1; argv_elem_dst -= wordpad(argv_elem_size); /* Write the 'i+1'th element of argv */ vm_memwrite(pagetable, argv_elem_size, argv_elem_dst, argv_src[i]); /* Write argv[i+1] */ vm_memwrite(pagetable, sizeof(virtaddr_t), argv_dst, &argv_elem_dst); /* Move argv_dst to next element of argv. */ argv_dst += sizeof(virtaddr_t); } } /* Write argc to the stack. */ vm_memwrite(pagetable, sizeof(int), argv_elem_dst - sizeof(int), &argc); /* Write argv to the stack. */ vm_memwrite(pagetable, sizeof(virtaddr_t), argv_elem_dst - sizeof(int) - sizeof(virtaddr_t), &argv_begin); /* Stack pointer points at argv. */ *stack_top = argv_elem_dst - sizeof(int) - sizeof(virtaddr_t); return 0; }