void setup_thread(thread_params_t *params) { context_t user_context; uint32_t phys_page; int i; interrupt_status_t intr_status; thread_table_t *thread= thread_get_current_thread_entry(); /* Copy thread parameters. */ int arg = params->arg; void (*func)(int) = params->func; process_id_t pid = thread->process_id = params->pid; thread->pagetable = params->pagetable; params->done = 1; /* OK, we don't need params any more. */ intr_status = _interrupt_disable(); spinlock_acquire(&process_table_slock); /* Set up userspace environment. */ memoryset(&user_context, 0, sizeof(user_context)); user_context.cpu_regs[MIPS_REGISTER_A0] = arg; user_context.pc = (uint32_t)func; /* Allocate thread stack */ if (process_table[pid].bot_free_stack != 0) { /* Reuse old thread stack. */ user_context.cpu_regs[MIPS_REGISTER_SP] = process_table[pid].bot_free_stack + CONFIG_USERLAND_STACK_SIZE*PAGE_SIZE - 4; /* Space for the thread argument */ process_table[pid].bot_free_stack = *(uint32_t*)process_table[pid].bot_free_stack; } else { /* Allocate physical pages (frames) for the stack. */ for (i = 0; i < CONFIG_USERLAND_STACK_SIZE; i++) { phys_page = pagepool_get_phys_page(); KERNEL_ASSERT(phys_page != 0); vm_map(thread->pagetable, phys_page, process_table[pid].stack_end - (i+1)*PAGE_SIZE, 1); } user_context.cpu_regs[MIPS_REGISTER_SP] = process_table[pid].stack_end-4; /* Space for the thread argument */ process_table[pid].stack_end -= PAGE_SIZE*CONFIG_USERLAND_STACK_SIZE; } tlb_fill(thread->pagetable); spinlock_release(&process_table_slock); _interrupt_set_state(intr_status); thread_goto_userland(&user_context); }
/** * 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."); }
/** * Initialize trivial filesystem. Allocates 1 page of memory dynamically for * filesystem data structure, tfs data structure and buffers needed. * Sets fs_t and tfs_t fields. If initialization is succesful, returns * pointer to fs_t data structure. Else NULL pointer is returned. * * @param Pointer to gbd-device performing tfs. * * @return Pointer to the filesystem data structure fs_t, if fails * return NULL. */ fs_t * tfs_init(gbd_t *disk) { uint32_t addr; gbd_request_t req; char name[TFS_VOLUMENAME_MAX]; fs_t *fs; tfs_t *tfs; int r; semaphore_t *sem; if(disk->block_size(disk) != TFS_BLOCK_SIZE) return NULL; /* check semaphore availability before memory allocation */ sem = semaphore_create(1); if (sem == NULL) { kprintf("tfs_init: could not create a new semaphore.\n"); return NULL; } addr = pagepool_get_phys_page(); if(addr == 0) { semaphore_destroy(sem); kprintf("tfs_init: could not allocate memory.\n"); return NULL; } addr = ADDR_PHYS_TO_KERNEL(addr); /* transform to vm address */ /* Assert that one page is enough */ KERNEL_ASSERT(PAGE_SIZE >= (3*TFS_BLOCK_SIZE+sizeof(tfs_t)+sizeof(fs_t))); /* Read header block, and make sure this is tfs drive */ req.block = 0; req.sem = NULL; req.buf = ADDR_KERNEL_TO_PHYS(addr); /* disk needs physical addr */ r = disk->read_block(disk, &req); if(r == 0) { semaphore_destroy(sem); pagepool_free_phys_page(ADDR_KERNEL_TO_PHYS(addr)); kprintf("tfs_init: Error during disk read. Initialization failed.\n"); return NULL; } if(((uint32_t *)addr)[0] != TFS_MAGIC) { semaphore_destroy(sem); pagepool_free_phys_page(ADDR_KERNEL_TO_PHYS(addr)); return NULL; } /* Copy volume name from header block. */ stringcopy(name, (char *)(addr+4), TFS_VOLUMENAME_MAX); /* fs_t, tfs_t and all buffers in tfs_t fit in one page, so obtain addresses for each structure and buffer inside the allocated memory page. */ fs = (fs_t *)addr; tfs = (tfs_t *)(addr + sizeof(fs_t)); tfs->buffer_inode = (tfs_inode_t *)((uint32_t)tfs + sizeof(tfs_t)); tfs->buffer_bat = (bitmap_t *)((uint32_t)tfs->buffer_inode + TFS_BLOCK_SIZE); tfs->buffer_md = (tfs_direntry_t *)((uint32_t)tfs->buffer_bat + TFS_BLOCK_SIZE); tfs->totalblocks = MIN(disk->total_blocks(disk), 8*TFS_BLOCK_SIZE); tfs->disk = disk; /* save the semaphore to the tfs_t */ tfs->lock = sem; fs->internal = (void *)tfs; stringcopy(fs->volume_name, name, VFS_NAME_LENGTH); fs->unmount = tfs_unmount; fs->open = tfs_open; fs->close = tfs_close; fs->create = tfs_create; fs->remove = tfs_remove; fs->read = tfs_read; fs->write = tfs_write; fs->getfree = tfs_getfree; return fs; }