/** * The hook called before 'do_execve' successfully return. * What we need to do here are: * 1. create a new host container if the process doesn't have one yet; * 2. create the stack for syscall stub; * 3. unmap umUcore kernel area in the child and erasing the info in the page table; * 4. copy arguments to the user stack of the child, and free the kernel's copy; * 5. call 'userspace'. * If everything is done, the current thread becomes a monitor thread forever. * @param argc the number of arguments * @param kargv the copy of the arguments in the kernel */ int do_execve_arch_hook(int argc, char **kargv) { if (current->arch.host == NULL) { current->arch.host = kmalloc(sizeof(struct host_container)); if (current->arch.host == NULL) return -1; } void *stub_stack = boot_alloc_page(); if (stub_stack == NULL) goto free_host; int ret = start_userspace(stub_stack); if (ret <= 0) goto free_stub_stack; current->arch.host->stub_stack = stub_stack; current->arch.host->host_pid = ret; current->arch.host->nr_threads = 1; /* unmap kernel area */ if (host_munmap(current, (void *)KERNBASE, KERNTOP - KERNBASE) < 0) panic("unmap kernel area failed\n"); /* erase kernel maps in the page table */ int valid_size = KERNBASE / PTSIZE * sizeof(pte_t); memset((void *)((int)(current->mm->pgdir) + valid_size), 0, PGSIZE - valid_size); /* Copy arguments */ current->arch.regs.is_user = 1; uintptr_t stacktop = USTACKTOP - argc * PGSIZE; char **uargv = (char **)(stacktop - argc * sizeof(char *)); int i, addr; for (i = 0; i < argc; i++) { addr = stacktop + i * PGSIZE; assert(copy_to_user (current->mm, uargv + i, &addr, sizeof(int))); assert(copy_to_user (current->mm, (void *)addr, kargv[i], strlen(kargv[i]) + 1)); } stacktop = (uintptr_t) uargv - sizeof(int); copy_to_user(current->mm, (void *)stacktop, &argc, sizeof(int)); /* The copy of the args in the kernel will never be used again and we will not return, * so free them here. */ while (argc > 0) { kfree(kargv[--argc]); } userspace(&(current->arch.regs)); /* should never comes here */ free_stub_stack: free_page(kva2page(stub_stack)); free_host: kfree(current->arch.host); current->arch.host = NULL; return -1; }
//pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism // - check the correctness of pmm & paging mechanism, print PDT&PT void pmm_init(void) { init_pmm_manager (); page_init (); #ifndef NOCHECK //check_alloc_page(); #endif boot_pgdir = boot_alloc_page (); memset (boot_pgdir, 0, PGSIZE); boot_pgdir_pa = PADDR (boot_pgdir); current_pgdir_pa = boot_pgdir_pa; #ifndef NOCHECK //check_pgdir (); #endif static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0); boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_SPR_R | PTE_SPR_W | PTE_A | PTE_D; boot_map_segment(boot_pgdir, KERNBASE, RAM_SIZE, 0, PTE_SPR_R | PTE_SPR_W | PTE_A | PTE_D); enable_paging (); #ifndef NOCHECK //check_boot_pgdir (); #endif print_pgdir (kprintf); slab_init (); }
//pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism // - check the correctness of pmm & paging mechanism, print PDT&PT void pmm_init(void) { //We need to alloc/free the physical memory (granularity is 4KB or other size). //So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h //First we should init a physical memory manager(pmm) based on the framework. //Then pmm can alloc/free the physical memory. //Now the first_fit/best_fit/worst_fit/buddy_system pmm are available. init_pmm_manager(); // detect physical memory space, reserve already used memory, // then use pmm->init_memmap to create free page list page_init(); //use pmm->check to verify the correctness of the alloc/free function in a pmm check_alloc_page(); // create boot_pgdir, an initial page directory(Page Directory Table, PDT) boot_pgdir = boot_alloc_page(); memset(boot_pgdir, 0, PGSIZE); boot_cr3 = PADDR(boot_pgdir); check_pgdir(); static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0); // recursively insert boot_pgdir in itself // to form a virtual page table at virtual address VPT // cprintf("haah1\n"); // map all physical memory to linear memory with base linear addr KERNBASE //linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE //But shouldn't use this map until enable_paging() & gdt_init() finished. boot_map_segment(boot_pgdir, 0, KMEMSIZE, 0, PTE_TYPE_URWX_SRWX | PTE_R | PTE_V); boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_TYPE_TABLE | PTE_R | PTE_V; // pgdir_alloc_page(boot_pgdir, USTACKTOP-PGSIZE , PTE_TYPE_URW_SRW); //cprintf("haha2\n"); //temporary map: //virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M //boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)]; //cprintf("OK!\n"); enable_paging(); // cprintf("haah\n"); //reload gdt(third time,the last time) to map all physical memory //virtual_addr 0~4G=liear_addr 0~4G //then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS //gdt_init(); //disable the map of virtual_addr 0~4M //boot_pgdir[0] = 0; //now the basic virtual memory map(see memalyout.h) is established. //check the correctness of the basic virtual memory map. check_boot_pgdir(); print_pgdir(); kmalloc_init(); }
/** * Initialize page management mechanism. * Parts of no use are deleted, while no extra parts except a check is added. * arch/x86/mm/pmm.c should be a good reference. */ void pmm_init (void) { check_vpm (); init_pmm_manager (); page_init (); check_alloc_page (); boot_pgdir = boot_alloc_page(); memset(boot_pgdir, 0, PGSIZE); check_pgdir(); /* register kernel code and data pages in the table so that it won't raise bad segv. */ boot_map_segment (boot_pgdir, KERNBASE, mem_size, 0, PTE_W); check_boot_pgdir (); print_pgdir (kprintf); slab_init (); }
/** * Make a copy of the current thread/process, giving the parent the child's pid and the child 0. * This is called in do_fork after all structures in the child's PCB are ready. * @param clone_flags we need this to determine whether we're creating a 'thread' or a 'process' * @param proc the PCB of the child * @param user_stack the stack used by the child * @param tf the struct containing 'fn' and 'arg' * @return 0 if succeeds, or -1 otherwise */ int copy_thread(uint32_t clone_flags, struct proc_struct *proc, uintptr_t user_stack, struct trapframe *tf) { int pid; void *stub_stack; /* If 'do_fork' is called by the kernel, 'current' should be idle, * and we're actually creating a kernel thread . * If 'do_fork' is called by the user (i.e. syscall), 'current' is a user PCB, * and we need to create another user process. */ if (RUN_AS_USER) { if (clone_flags & CLONE_VM) { /* Use current host process as its container */ proc->arch.host = current->arch.host; proc->arch.host->nr_threads++; } else { /* Create a new child process */ proc->arch.host = kmalloc(sizeof(struct host_container)); if (proc->arch.host == NULL) goto exit; stub_stack = boot_alloc_page(); if (stub_stack == NULL) goto exit_free_host; pid = start_userspace(stub_stack); if (pid < 0) goto exit_free_stub; /* Initialize host process descriptor */ proc->arch.forking = 0; proc->arch.host->host_pid = pid; proc->arch.host->nr_threads = 1; proc->arch.host->stub_stack = (struct stub_stack *)stub_stack; /* unmap kernel area. */ if (host_munmap (proc, (void *)KERNBASE, KERNTOP - KERNBASE) < 0) panic("unmap kernel area failed \n"); } /* The child should have the same regs as the parent's. */ memcpy(&(proc->arch.regs.regs), &(current->arch.regs.regs), sizeof(struct user_regs_struct)); /* make the child return 0 for the syscall */ proc->arch.regs.regs.eax = 0; proc->arch.regs.regs.esp = user_stack; /* The current thread will run in 'userspace' */ proc->context.switch_buf->__ebx = (uint32_t) userspace; proc->context.switch_buf->__ebp = (uint32_t) & (proc->arch.regs); } else { /* For kernel thread */ proc->context.switch_buf->__ebx = (uint32_t) (tf->fn); proc->context.switch_buf->__ebp = (uint32_t) (tf->arg); } /* All new threads/processes start running from 'kernel_thread_entry' * for the 'processes' actually means 'monitor threads' to the kernel. */ proc->context.switch_buf->__eip = (uint32_t) kernel_thread_entry; proc->context.switch_buf->__esp = proc->kstack + KSTACKSIZE; return 0; exit_free_stub: free_page(kva2page(stub_stack)); exit_free_host: kfree(proc->arch.host); exit: return -1; }