int ipc_event_recv(int *pid_store, int *event_store, unsigned int timeout) { if (event_store == NULL) { return -E_INVAL; } struct mm_struct *mm = current->mm; if (pid_store != NULL) { if (!user_mem_check(mm, (uintptr_t)pid_store, sizeof(int), 1)) { return -E_INVAL; } } if (!user_mem_check(mm, (uintptr_t)event_store, sizeof(int), 1)) { return -E_INVAL; } unsigned long saved_ticks; timer_t __timer, *timer = ipc_timer_init(timeout, &saved_ticks, &__timer); int pid, event, ret; if ((ret = recv_event(&pid, &event, timer)) == 0) { lock_mm(mm); { ret = -E_INVAL; if (pid_store == NULL || copy_to_user(mm, pid_store, &pid, sizeof(int))) { if (copy_to_user(mm, event_store, &event, sizeof(int))) { ret = 0; } } } unlock_mm(mm); return ret; } return ipc_check_timeout(timeout, saved_ticks); }
int ipc_sem_get_value(sem_t sem_id, int *value_store) { assert(current->sem_queue != NULL); if (value_store == NULL) { return -E_INVAL; } struct mm_struct *mm = current->mm; if (!user_mem_check(mm, (uintptr_t) value_store, sizeof(int), 1)) { return -E_INVAL; } sem_undo_t *semu; sem_queue_t *sem_queue = current->sem_queue; down(&(sem_queue->sem)); semu = semu_list_search(&(sem_queue->semu_list), sem_id); up(&(sem_queue->sem)); int ret = -E_INVAL; if (semu != NULL) { int value = semu->sem->value; lock_mm(mm); { if (copy_to_user(mm, value_store, &value, sizeof(int))) { ret = 0; } } unlock_mm(mm); } return ret; }
int sysfile_write(int fd, void *base, size_t len) { int ret = 0; struct mm_struct *mm = pls_read(current)->mm; if (len == 0) { return 0; } if (!file_testfd(fd, 0, 1)) { return -E_INVAL; } /* for linux inode */ if (__is_linux_devfile(fd)) { /* use 8byte int, in case of 64bit off_t * config in linux kernel */ size_t alen = 0; ret = linux_devfile_write(fd, base, len, &alen); if (ret) return ret; return alen; } void *buffer; if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { return -E_NO_MEM; } size_t copied = 0, alen; while (len != 0) { if ((alen = IOBUF_SIZE) > len) { alen = len; } lock_mm(mm); { if (!copy_from_user(mm, buffer, base, alen, 0)) { ret = -E_INVAL; } } unlock_mm(mm); if (ret == 0) { ret = file_write(fd, buffer, alen, &alen); if (alen != 0) { assert(len >= alen); base += alen, len -= alen, copied += alen; } } if (ret != 0 || alen == 0) { goto out; } } out: kfree(buffer); if (copied != 0) { return copied; } return ret; }
unsigned long __ucore_copy_from_user(void *to, const void *from, unsigned long n) { int ret = 0; struct mm_struct *mm = pls_read(current)->mm; lock_mm(mm); ret = copy_from_user(mm, to, from, n, 0); unlock_mm(mm); if(ret) return 0; return n; }
// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags // - if clone_flags & CLONE_VM, then "share" ; else "duplicate" static int copy_mm(uint32_t clone_flags, struct proc_struct *proc) { struct mm_struct *mm, *oldmm = current->mm; /* current is a kernel thread */ if (oldmm == NULL) { return 0; } if (clone_flags & CLONE_VM) { mm = oldmm; goto good_mm; } int ret = -E_NO_MEM; if ((mm = mm_create()) == NULL) { goto bad_mm; } if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; } lock_mm(oldmm); { ret = dup_mmap(mm, oldmm); } unlock_mm(oldmm); if (ret != 0) { goto bad_dup_cleanup_mmap; } good_mm: if (mm != oldmm) { mm->brk_start = oldmm->brk_start; mm->brk = oldmm->brk; bool intr_flag; local_intr_save(intr_flag); { list_add(&(proc_mm_list), &(mm->proc_mm_link)); } local_intr_restore(intr_flag); } mm_count_inc(mm); proc->mm = mm; set_pgdir (proc, mm->pgdir); return 0; bad_dup_cleanup_mmap: exit_mmap(mm); put_pgdir(mm); bad_pgdir_cleanup_mm: mm_destroy(mm); bad_mm: return ret; }
int sysfile_read(int fd, void *base, size_t len) { int ret = 0; struct mm_struct *mm = pls_read(current)->mm; if (len == 0) { return 0; } if (!file_testfd(fd, 1, 0)) { return -E_INVAL; } /* for linux inode */ if (__is_linux_devfile(fd)) { size_t alen = 0; ret = linux_devfile_read(fd, base, len, &alen); if (ret) return ret; return alen; } void *buffer; if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { return -E_NO_MEM; } size_t copied = 0, alen; while (len != 0) { if ((alen = IOBUF_SIZE) > len) { alen = len; } ret = file_read(fd, buffer, alen, &alen); if (alen != 0) { lock_mm(mm); { if (copy_to_user(mm, base, buffer, alen)) { assert(len >= alen); base += alen, len -= alen, copied += alen; } else if (ret == 0) { ret = -E_INVAL; } } unlock_mm(mm); } if (ret != 0 || alen == 0) { goto out; } } out: kfree(buffer); if (copied != 0) { return copied; } return ret; }
int do_cleanup_module(const char __user * name_user) { struct module *mod; char name[MODULE_NAME_LEN]; int ret = 0, forced = 0; struct mm_struct *mm = current->mm; lock_mm(mm); int length = strlen(name_user); if (!copy_from_user(mm, name, name_user, length, 1)) { unlock_mm(mm); return -E_INVAL; } unlock_mm(mm); name[length] = '\0'; mod = find_module(name); if (!mod) { ret = -E_NOENT; goto out; //return -E_NOENT; } if (!list_empty(&mod->modules_which_use_me)) { ret = -E_INVAL; goto out; } if (mod->state != MODULE_STATE_LIVE) { kprintf("do_cleanup_module: %s already dying\n", mod->name); ret = -E_BUSY; } if (mod->init && !mod->exit) { kprintf("do_cleanup_module: %s can't be removed\n", mod->name); ret = -E_BUSY; } mod->waiter = current; mod->state = MODULE_STATE_GOING; if (mod->exit != NULL) mod->exit(); strncpy(last_unloaded_module, mod->name, strlen(mod->name)); free_module(mod); return ret; out: return ret; }
// do_shmem - create a share memory with addr, len, flags(VM_READ/M_WRITE/VM_STACK) int do_shmem(uintptr_t * addr_store, size_t len, uint32_t mmap_flags) { struct mm_struct *mm = current->mm; if (mm == NULL) { panic("kernel thread call mmap!!.\n"); } if (addr_store == NULL || len == 0) { return -E_INVAL; } int ret = -E_INVAL; uintptr_t addr; lock_mm(mm); if (!copy_from_user(mm, &addr, addr_store, sizeof(uintptr_t), 1)) { goto out_unlock; } uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE); addr = start, len = end - start; uint32_t vm_flags = VM_READ; if (mmap_flags & MMAP_WRITE) vm_flags |= VM_WRITE; if (mmap_flags & MMAP_STACK) vm_flags |= VM_STACK; ret = -E_NO_MEM; if (addr == 0) { if ((addr = get_unmapped_area(mm, len)) == 0) { goto out_unlock; } } struct shmem_struct *shmem; if ((shmem = shmem_create(len)) == NULL) { goto out_unlock; } if ((ret = mm_map_shmem(mm, addr, vm_flags, shmem, NULL)) != 0) { assert(shmem_ref(shmem) == 0); shmem_destroy(shmem); goto out_unlock; } copy_to_user(mm, addr_store, &addr, sizeof(uintptr_t)); out_unlock: unlock_mm(mm); return ret; }
/* sysfile_read - read file */ int sysfile_read(int fd, void *base, size_t len) { cprintf("sysfile_read fd = %d\n",fd); struct mm_struct *mm = current->mm; if (len == 0) { return 0; } if (!file_testfd(fd, 1, 0)) { return -E_INVAL; } void *buffer; if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { return -E_NO_MEM; } int ret = 0; size_t copied = 0, alen; while (len != 0) { if ((alen = IOBUF_SIZE) > len) { alen = len; } ret = file_read(fd, buffer, alen, &alen); if (alen != 0) { lock_mm(mm); { if (copy_to_user(mm, base, buffer, alen)) { assert(len >= alen); base += alen, len -= alen, copied += alen; } else if (ret == 0) { ret = -E_INVAL; } } unlock_mm(mm); } if (ret != 0 || alen == 0) { goto out; } } out: kfree(buffer); if (copied != 0) { return copied; } return ret; }
// do_munmap - delete vma with addr & len int do_munmap(uintptr_t addr, size_t len) { struct mm_struct *mm = current->mm; if (mm == NULL) { panic("kernel thread call munmap!!.\n"); } if (len == 0) { return -E_INVAL; } int ret; lock_mm(mm); { ret = mm_unmap(mm, addr, len); } unlock_mm(mm); return ret; }
int sysfile_write(int fd, void *base, size_t len) { struct mm_struct *mm = current->mm; if (len == 0) { return 0; } if (!file_testfd(fd, 0, 1)) { return -E_INVAL; } void *buffer; if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) { return -E_NO_MEM; } int ret = 0; size_t copied = 0, alen; while (len != 0) { if ((alen = IOBUF_SIZE) > len) { alen = len; } lock_mm(mm); { if (!copy_from_user(mm, buffer, base, alen, 0)) { ret = -E_INVAL; } } unlock_mm(mm); if (ret == 0) { ret = file_write(fd, buffer, alen, &alen); if (alen != 0) { assert(len >= alen); base += alen, len -= alen, copied += alen; } } if (ret != 0 || alen == 0) { goto out; } } out: kfree(buffer); if (copied != 0) { return copied; } return ret; }
// do_brk - adjust(increase/decrease) the size of process heap, align with page size // NOTE: will change the process vma int do_brk(uintptr_t *brk_store) { struct mm_struct *mm = current->mm; if (mm == NULL) { panic("kernel thread call sys_brk!!.\n"); } if (brk_store == NULL) { return -E_INVAL; } uintptr_t brk; lock_mm(mm); if (!copy_from_user(mm, &brk, brk_store, sizeof(uintptr_t), 1)) { unlock_mm(mm); return -E_INVAL; } if (brk < mm->brk_start) { goto out_unlock; } uintptr_t newbrk = ROUNDUP(brk, PGSIZE), oldbrk = mm->brk; assert(oldbrk % PGSIZE == 0); if (newbrk == oldbrk) { goto out_unlock; } if (newbrk < oldbrk) { if (mm_unmap(mm, newbrk, oldbrk - newbrk) != 0) { goto out_unlock; } } else { if (find_vma_intersection(mm, oldbrk, newbrk + PGSIZE) != NULL) { goto out_unlock; } if (mm_brk(mm, oldbrk, newbrk - oldbrk) != 0) { goto out_unlock; } } mm->brk = newbrk; out_unlock: copy_to_user (mm, brk_store, &mm->brk, sizeof (uintptr_t)); unlock_mm(mm); return 0; }
/* sysfile_get cwd - get current working directory */ int sysfile_getcwd(char *buf, size_t len) { struct mm_struct *mm = current->mm; if (len == 0) { return -E_INVAL; } int ret = -E_INVAL; lock_mm(mm); { if (user_mem_check(mm, (uintptr_t)buf, len, 1)) { struct iobuf __iob, *iob = iobuf_init(&__iob, buf, len, 0); ret = vfs_getcwd(iob); } } unlock_mm(mm); return ret; }
/* sysfile_fstat - stat file */ int sysfile_fstat(int fd, struct stat *__stat) { struct mm_struct *mm = current->mm; int ret; struct stat __local_stat, *stat = &__local_stat; if ((ret = file_fstat(fd, stat)) != 0) { return ret; } lock_mm(mm); { if (!copy_to_user(mm, __stat, stat, sizeof(struct stat))) { ret = -E_INVAL; } } unlock_mm(mm); return ret; }
// set user stack for signal handler, also set eip to handler int __sig_setup_frame(int sign, struct sigaction *act, sigset_t oldset, struct trapframe *tf) { struct mm_struct *mm = current->mm; uintptr_t stack = current->signal_info.sas_ss_sp; if (stack == 0) { stack = tf->tf_esp; } struct sigframe *kframe = kmalloc(sizeof(struct sigframe)); if (!kframe) return -E_NO_MEM; memset(kframe, 0, sizeof(struct sigframe)); kframe->sign = sign; kframe->tf = *tf; kframe->old_blocked = oldset; /* mov r7, #119 */ kframe->retcode[0] = 0xe3a07077; /* swi #0 */ kframe->retcode[1] = 0xef000000; /* 4byte align */ struct sigframe *frame = (struct sigframe *)((stack - sizeof(struct sigframe)) & 0xfffffff8); lock_mm(mm); { if (!copy_to_user(mm, frame, kframe, sizeof(struct sigframe))) { unlock_mm(mm); kfree(kframe); return -E_INVAL; } } unlock_mm(mm); kfree(kframe); frame->pretcode = (uintptr_t) frame->retcode; tf->tf_regs.reg_r[0] = sign; tf->tf_epc = (uintptr_t) act->sa_handler; tf->tf_esp = (uintptr_t) frame; tf->__tf_user_lr = (uint32_t) frame->pretcode; return 0; }
/* poring from linux */ int do_linux_brk(uintptr_t brk) { uint32_t newbrk, oldbrk, retval; struct mm_struct *mm = current->mm; uint32_t min_brk; if (!mm) { panic("kernel thread call sys_brk!!.\n"); } lock_mm(mm); min_brk = mm->brk_start; if (brk < min_brk) goto out_unlock; newbrk = ROUNDUP(brk, PGSIZE); oldbrk = ROUNDUP(mm->brk, PGSIZE); if (oldbrk == newbrk) goto set_brk; if (brk <= mm->brk) { if (!mm_unmap(mm, newbrk, oldbrk - newbrk)) goto set_brk; goto out_unlock; } if (find_vma_intersection(mm, oldbrk, newbrk + PGSIZE)) goto out_unlock; /* set the brk */ if (mm_brk(mm, oldbrk, newbrk - oldbrk)) goto out_unlock; set_brk: mm->brk = brk; out_unlock: retval = mm->brk; unlock_mm(mm); return retval; }
/* copy_path - copy path name */ static int copy_path(char **to, const char *from) { struct mm_struct *mm = current->mm; char *buffer; if ((buffer = kmalloc(FS_MAX_FPATH_LEN + 1)) == NULL) { return -E_NO_MEM; } lock_mm(mm); if (!copy_string(mm, buffer, from, FS_MAX_FPATH_LEN + 1)) { unlock_mm(mm); goto failed_cleanup; } unlock_mm(mm); *to = buffer; return 0; failed_cleanup: kfree(buffer); return -E_INVAL; }
// do syscall sigreturn, reset the user stack and eip int do_sigreturn() { struct mm_struct *mm = current->mm; if (!current) return -E_INVAL; struct sighand_struct *sighand = current->signal_info.sighand; if (!sighand) return -E_INVAL; struct sigframe *kframe = kmalloc(sizeof(struct sigframe)); if (!kframe) return -E_NO_MEM; struct sigframe *frame = (struct sigframe *)(current->tf->tf_esp); lock_mm(mm); { if (!copy_from_user (mm, kframe, frame, sizeof(struct sigframe), 0)) { unlock_mm(mm); kfree(kframe); return -E_INVAL; } } unlock_mm(mm); /* check the trapframe */ if (trap_in_kernel(&kframe->tf)) { do_exit(-E_KILLED); return -E_INVAL; } lock_sig(sighand); current->signal_info.blocked = kframe->old_blocked; sig_recalc_pending(current); unlock_sig(sighand); *(current->tf) = kframe->tf; kfree(kframe); return 0; }
int sysfile_pipe(int *fd_store) { struct mm_struct *mm = current->mm; int ret, fd[2]; if (!user_mem_check(mm, (uintptr_t)fd_store, sizeof(fd), 1)) { return -E_INVAL; } if ((ret = file_pipe(fd)) == 0) { lock_mm(mm); { if (!copy_to_user(mm, fd_store, fd, sizeof(fd))) { ret = -E_INVAL; } } unlock_mm(mm); if (ret != 0) { file_close(fd[0]), file_close(fd[1]); } } return ret; }
/* from x86 bionic porting */ int do_linux_brk(uintptr_t brk) { struct mm_struct *mm = current->mm; if (mm == NULL) { panic("kernel thread call sys_brk!!.\n"); } if (brk == 0) { return mm->brk_start; } lock_mm(mm); if (brk < mm->brk_start) { goto out_unlock; } uintptr_t newbrk = ROUNDUP(brk, PGSIZE), oldbrk = mm->brk; assert(oldbrk % PGSIZE == 0); if (newbrk == oldbrk) { goto out_unlock; } if (newbrk < oldbrk) { if (mm_unmap(mm, newbrk, oldbrk - newbrk) != 0) { goto out_unlock; } } else { if (find_vma_intersection(mm, oldbrk, newbrk + PGSIZE) != NULL) { goto out_unlock; } if (mm_brk(mm, oldbrk, newbrk - oldbrk) != 0) { goto out_unlock; } } mm->brk = newbrk; out_unlock: unlock_mm(mm); return newbrk; }
// do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack // - proc struct of this child. // NOTE: only after do_wait function, all resources of the child proces are free. int do_wait(int pid, int *code_store) { struct mm_struct *mm = current->mm; if (code_store != NULL) { if (!user_mem_check(mm, (uintptr_t) code_store, sizeof(int), 1)) { return -E_INVAL; } } struct proc_struct *proc, *cproc; bool intr_flag, haskid; repeat: cproc = current; haskid = 0; if (pid != 0) { proc = find_proc(pid); if (proc != NULL) { do { if (proc->parent == cproc) { haskid = 1; if (proc->state == PROC_ZOMBIE) { goto found; } break; } cproc = next_thread(cproc); } while (cproc != current); } } else { do { proc = cproc->cptr; for (; proc != NULL; proc = proc->optr) { haskid = 1; if (proc->state == PROC_ZOMBIE) { goto found; } } cproc = next_thread(cproc); } while (cproc != current); } if (haskid) { current->state = PROC_SLEEPING; current->wait_state = WT_CHILD; schedule(); may_killed(); goto repeat; } return -E_BAD_PROC; found: if (proc == idleproc || proc == initproc) { panic("wait idleproc or initproc.\n"); } int exit_code = proc->exit_code; spin_lock_irqsave(&proc_lock, intr_flag); { unhash_proc(proc); remove_links(proc); } spin_unlock_irqrestore(&proc_lock, intr_flag); put_kstack(proc); kfree(proc); int ret = 0; if (code_store != NULL) { lock_mm(mm); { if (!copy_to_user (mm, code_store, &exit_code, sizeof(int))) { ret = -E_INVAL; } } unlock_mm(mm); } return ret; }
static noinline struct module *load_module(void __user * umod, unsigned long len, const char __user * uargs) { struct elfhdr *hdr; struct secthdr *sechdrs; char *secstrings, *args, *modmagic, *strtab = NULL; //char *staging; unsigned int i; unsigned int symindex = 0; unsigned int strindex = 0; unsigned int modindex, versindex, infoindex, pcpuindex; struct module *mod; long err = 0; void *ptr = NULL; kprintf("load_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); if (len < sizeof(*hdr)) return NULL; if (len > 64 * 1024 * 1024 || (hdr = kmalloc(len)) == NULL) return NULL; kprintf("load_module: copy_from_user\n"); struct mm_struct *mm = current->mm; lock_mm(mm); if (!copy_from_user(mm, hdr, umod, len, 1)) { unlock_mm(mm); goto free_hdr; } unlock_mm(mm); kprintf("load_module: hdr:%p\n", hdr); // sanity check if (memcmp(&(hdr->e_magic), ELFMAG, SELFMAG) != 0 || hdr->e_type != ET_REL || !elf_check_arch(hdr) || hdr->e_shentsize != sizeof(*sechdrs)) { kprintf("load_module: sanity check failed.\n"); goto free_hdr; } if (len < hdr->e_shoff + hdr->e_shnum * sizeof(*sechdrs)) goto truncated; sechdrs = (void *)hdr + hdr->e_shoff; secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_NOBITS && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) goto truncated; // mark sh_addr sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; if (sechdrs[i].sh_type == SHT_SYMTAB) { symindex = i; strindex = sechdrs[i].sh_link; strtab = (char *)hdr + sechdrs[strindex].sh_offset; } } modindex = find_sec(hdr, sechdrs, secstrings, ".gnu.linkonce.this_module"); if (!modindex) { kprintf("load_module: No module found in object.\n"); goto free_hdr; } // temp: point mod into copy of data mod = (void *)sechdrs[modindex].sh_addr; if (symindex == 0) { kprintf("load_module: %s module has no symbols (stripped?).\n", mod->name); goto free_hdr; } versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); pcpuindex = 0;//find_pcpusec(hdr, sechdrs, secstrings); // don't keep modinfo and version sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC; // keep symbol and string tables sechdrs[symindex].sh_flags |= SHF_ALLOC; sechdrs[strindex].sh_flags |= SHF_ALLOC; /*if (!check_modstruct_version(sechdrs, versindex, mod)) { goto free_hdr; }*/ /* modmagic = get_modinfo(sechdrs, infoindex, "vermagic"); if (!modmagic) { kprintf("load_module: bad vermagic\n"); goto free_hdr; } else if (!same_magic(modmagic, vermagic, versindex)) { ; // TODO: module magic is left for future use. } */ //staging = get_modinfo(sechdrs, infoindex, "staging"); // TODO: staging is left for future use. if (find_module(mod->name)) { kprintf("load_module: module %s exists\n", mod->name); goto free_mod; } mod->state = MODULE_STATE_COMING; // err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod); // TODO: we do not need it for x86 or arm // TODO: percpu is no longer needed. layout_sections(mod, hdr, sechdrs, secstrings); ptr = module_alloc_update_bounds(mod->core_size); if (!ptr) { goto free_percpu; } memset(ptr, 0, mod->core_size); mod->module_core = ptr; ptr = module_alloc_update_bounds(mod->init_size); if (!ptr && mod->init_size) { goto free_core; } memset(ptr, 0, mod->init_size); mod->module_init = ptr; kprintf("load_module: final section addresses:\n"); for (i = 0; i < hdr->e_shnum; i++) { void *dest; if (!(sechdrs[i].sh_flags & SHF_ALLOC)) { kprintf("\tSkipped %s\n", secstrings + sechdrs[i].sh_name); continue; } if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK) dest = mod->module_init + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK); else dest = mod->module_core + sechdrs[i].sh_entsize; if (sechdrs[i].sh_type != SHT_NOBITS) memcpy(dest, (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size); sechdrs[i].sh_addr = (unsigned long)dest; kprintf("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name); } /* Module has been moved. */ mod = (void *)sechdrs[modindex].sh_addr; /* Now we've moved module, initialize linked lists, etc. */ module_unload_init(mod); /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, mod); if (err < 0) goto cleanup; mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab", sizeof(*mod->syms), &mod->num_syms); mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab"); // relocations for (i = 1; i < hdr->e_shnum; i++) { const char *strtab = (char *)sechdrs[strindex].sh_addr; unsigned int info = sechdrs[i].sh_info; /* Not a valid relocation section */ if (info >= hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) continue; if (sechdrs[i].sh_type == SHT_REL) err = apply_relocate(sechdrs, strtab, symindex, i, mod); else if (sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(sechdrs, strtab, symindex, i, mod); if (err < 0) goto cleanup; } err = verify_export_symbols(mod); if (err < 0) goto cleanup; // TODO: kallsyms is left for future use. //add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; list_add(&modules, &mod->list); kfree(hdr); return mod; cleanup: module_unload_free(mod); free_init: module_free(mod, mod->module_init); free_core: module_free(mod, mod->module_core); free_percpu: free_mod: free_hdr: kfree(hdr); return NULL; truncated: kprintf("load_module: module len %lu truncated.\n"); goto free_hdr; }
int do_pgfault(struct mm_struct *mm, machine_word_t error_code, uintptr_t addr) { if (mm == NULL) { assert(current != NULL); /* Chen Yuheng * give handler a chance to deal with it */ kprintf ("page fault in kernel thread: pid = %d, name = %s, %d %08x.\n", current->pid, current->name, error_code, addr); return -E_KILLED; } bool need_unlock = 1; if (!try_lock_mm(mm)) { if (current != NULL && mm->locked_by == current->pid) { need_unlock = 0; } else { lock_mm(mm); } } int ret = -E_INVAL; struct vma_struct *vma = find_vma(mm, addr); if (vma == NULL || vma->vm_start > addr) { goto failed; } if (vma->vm_flags & VM_STACK) { if (addr < vma->vm_start + PGSIZE) { goto failed; } } //kprintf("@ %x %08x\n", vma->vm_flags, vma->vm_start); //assert((vma->vm_flags & VM_IO)==0); if (vma->vm_flags & VM_IO) { ret = -E_INVAL; goto failed; } switch (error_code & 3) { default: /* default is 3: write, present */ case 2: /* write, not present */ if (!(vma->vm_flags & VM_WRITE)) { goto failed; } break; case 1: /* read, present */ goto failed; case 0: /* read, not present */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { goto failed; } } pte_perm_t perm, nperm; #ifdef ARCH_ARM /* ARM9 software emulated PTE_xxx */ perm = PTE_P | PTE_U; if (vma->vm_flags & VM_WRITE) { perm |= PTE_W; } #else ptep_unmap(&perm); ptep_set_u_read(&perm); if (vma->vm_flags & VM_WRITE) { ptep_set_u_write(&perm); } #endif addr = ROUNDDOWN(addr, PGSIZE); ret = -E_NO_MEM; pte_t *ptep; if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { goto failed; } if (ptep_invalid(ptep)) { #ifdef UCONFIG_BIONIC_LIBC if (vma->mfile.file != NULL) { struct file *file = vma->mfile.file; off_t old_pos = file->pos, new_pos = vma->mfile.offset + addr - vma->vm_start; #ifdef SHARE_MAPPED_FILE struct mapped_addr *maddr = find_maddr(file, new_pos, NULL); if (maddr == NULL) { #endif // SHARE_MAPPED_FILE struct Page *page; if ((page = alloc_page()) == NULL) { assert(false); goto failed; } nperm = perm; #ifdef ARCH_ARM /* ARM9 software emulated PTE_xxx */ nperm &= ~PTE_W; #else ptep_unset_s_write(&nperm); #endif page_insert_pte(mm->pgdir, page, ptep, addr, nperm); if ((ret = filestruct_setpos(file, new_pos)) != 0) { assert(false); goto failed; } filestruct_read(file, page2kva(page), PGSIZE); if ((ret = filestruct_setpos(file, old_pos)) != 0) { assert(false); goto failed; } #ifdef SHARE_MAPPED_FILE if ((maddr = (struct mapped_addr *) kmalloc(sizeof(struct mapped_addr))) != NULL) { maddr->page = page; maddr->offset = new_pos; page->maddr = maddr; list_add(& (file->node->mapped_addr_list), &(maddr->list)); } else { assert(false); } } else { nperm = perm; #ifdef ARCH_ARM /* ARM9 software emulated PTE_xxx */ nperm &= ~PTE_W; #else ptep_unset_s_write(&nperm); #endif page_insert_pte(mm->pgdir, maddr->page, ptep, addr, nperm); } #endif //SHARE_MAPPED_FILE } else #endif //UCONFIG_BIONIC_LIBC if (!(vma->vm_flags & VM_SHARE)) { if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { goto failed; } #ifdef UCONFIG_BIONIC_LIBC if (vma->vm_flags & VM_ANONYMOUS) { memset((void *)addr, 0, PGSIZE); } #endif //UCONFIG_BIONIC_LIBC } else { //shared mem lock_shmem(vma->shmem); uintptr_t shmem_addr = addr - vma->vm_start + vma->shmem_off; pte_t *sh_ptep = shmem_get_entry(vma->shmem, shmem_addr, 1); if (sh_ptep == NULL || ptep_invalid(sh_ptep)) { unlock_shmem(vma->shmem); goto failed; } unlock_shmem(vma->shmem); if (ptep_present(sh_ptep)) { page_insert(mm->pgdir, pa2page(*sh_ptep), addr, perm); } else { #ifdef UCONFIG_SWAP swap_duplicate(*ptep); ptep_copy(ptep, sh_ptep); #else panic("NO SWAP\n"); #endif } } } else { //a present page, handle copy-on-write (cow) struct Page *page, *newpage = NULL; bool cow = ((vma->vm_flags & (VM_SHARE | VM_WRITE)) == VM_WRITE), may_copy = 1; #if 1 if (!(!ptep_present(ptep) || ((error_code & 2) && !ptep_u_write(ptep) && cow))) { //assert(PADDR(mm->pgdir) == rcr3()); kprintf("%p %p %d %d %x\n", *ptep, addr, error_code, cow, vma->vm_flags); assert(0); } #endif if (cow) { newpage = alloc_page(); } if (ptep_present(ptep)) { page = pte2page(*ptep); } else { #ifdef UCONFIG_SWAP if ((ret = swap_in_page(*ptep, &page)) != 0) { if (newpage != NULL) { free_page(newpage); } goto failed; } #else assert(0); #endif if (!(error_code & 2) && cow) { #ifdef ARCH_ARM //#warning ARM9 software emulated PTE_xxx perm &= ~PTE_W; #else ptep_unset_s_write(&perm); #endif may_copy = 0; } } if (cow && may_copy) { #ifdef UCONFIG_SWAP if (page_ref(page) + swap_page_count(page) > 1) { #else if (page_ref(page) > 1) { #endif if (newpage == NULL) { goto failed; } memcpy(page2kva(newpage), page2kva(page), PGSIZE); //kprintf("COW!\n"); page = newpage, newpage = NULL; } } #ifdef UCONFIG_BIONIC_LIBC else if (vma->mfile.file != NULL) { #ifdef UCONFIG_SWAP assert(page_reg(page) + swap_page_count(page) == 1); #else assert(page_ref(page) == 1); #endif #ifdef SHARE_MAPPED_FILE off_t offset = vma->mfile.offset + addr - vma->vm_start; struct mapped_addr *maddr = find_maddr(vma->mfile.file, offset, page); if (maddr != NULL) { list_del(&(maddr->list)); kfree(maddr); page->maddr = NULL; assert(find_maddr(vma->mfile.file, offset, page) == NULL); } else { } #endif //SHARE_MAPPED_FILE } #endif //UCONFIG_BIONIC_LIBC else { } page_insert(mm->pgdir, page, addr, perm); if (newpage != NULL) { free_page(newpage); } } ret = 0; failed: if (need_unlock) { unlock_mm(mm); } return ret; }
int do_mprotect(void *addr, size_t len, int prot) { /* return 0; */ struct mm_struct *mm = current->mm; assert(mm != NULL); if (len == 0) { return -E_INVAL; } uintptr_t start = ROUNDDOWN(addr, PGSIZE); uintptr_t end = ROUNDUP(addr + len, PGSIZE); int ret = -E_INVAL; lock_mm(mm); while (1) { struct vma_struct *vma = find_vma(mm, start); uintptr_t last_end; if (vma != NULL) { last_end = vma->vm_end; } if (vma == NULL) { goto out; } else if (vma->vm_start == start && vma->vm_end == end) { if (prot & PROT_WRITE) { vma->vm_flags |= VM_WRITE; } else { vma->vm_flags &= ~VM_WRITE; } } else { uintptr_t this_end = (end <= vma->vm_end) ? end : vma->vm_end; uintptr_t this_start = (start >= vma->vm_start) ? start : vma->vm_start; struct mapped_file_struct mfile = vma->mfile; mfile.offset += this_start - vma->vm_start; uint32_t flags = vma->vm_flags; if ((ret = mm_unmap_keep_pages(mm, this_start, this_end - this_start)) != 0) { goto out; } if (prot & PROT_WRITE) { flags |= VM_WRITE; } else { flags &= ~VM_WRITE; } if ((ret = mm_map(mm, this_start, this_end - this_start, flags, &vma)) != 0) { goto out; } vma->mfile = mfile; if (vma->mfile.file != NULL) { filemap_acquire(mfile.file); } } ret = 0; if (end <= last_end) break; start = last_end; } out: unlock_mm(mm); return ret; }
// do_pgfault - interrupt handler to process the page fault execption int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) { if (mm == NULL) { assert(current != NULL); panic("page fault in kernel thread: pid = %d, %d %08x.\n", current->pid, error_code, addr); } lock_mm(mm); int ret = -E_INVAL; struct vma_struct *vma = find_vma(mm, addr); if (vma == NULL || vma->vm_start > addr) { goto failed; } if (vma->vm_flags & VM_STACK) { if (addr < vma->vm_start + PGSIZE) { goto failed; } } switch (error_code & 3) { default: /* default is 3: write, present */ case 2: /* write, not present */ if (!(vma->vm_flags & VM_WRITE)) { goto failed; } break; case 1: /* read, present */ goto failed; case 0: /* read, not present */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { goto failed; } } uint32_t perm = PTE_U; if (vma->vm_flags & VM_WRITE) { perm |= PTE_W; } addr = ROUNDDOWN(addr, PGSIZE); ret = -E_NO_MEM; pte_t *ptep; if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { goto failed; } if (*ptep == 0) { if (!(vma->vm_flags & VM_SHARE)) { if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { goto failed; } } else { lock_shmem(vma->shmem); uintptr_t shmem_addr = addr - vma->vm_start + vma->shmem_off; pte_t *sh_ptep = shmem_get_entry(vma->shmem, shmem_addr, 1); if (sh_ptep == NULL || *sh_ptep == 0) { unlock_shmem(vma->shmem); goto failed; } unlock_shmem(vma->shmem); if (*sh_ptep & PTE_P) { page_insert(mm->pgdir, pa2page(*sh_ptep), addr, perm); } else { swap_duplicate(*ptep); *ptep = *sh_ptep; } } } else { struct Page *page, *newpage = NULL; bool cow = ((vma->vm_flags & (VM_SHARE | VM_WRITE)) == VM_WRITE), may_copy = 1; assert(!(*ptep & PTE_P) || ((error_code & 2) && !(*ptep & PTE_W) && cow)); if (cow) { newpage = alloc_page(); } if (*ptep & PTE_P) { page = pte2page(*ptep); } else { if ((ret = swap_in_page(*ptep, &page)) != 0) { if (newpage != NULL) { free_page(newpage); } goto failed; } if (!(error_code & 2) && cow) { perm &= ~PTE_W; may_copy = 0; } } if (cow && may_copy) { if (page_ref(page) + swap_page_count(page) > 1) { if (newpage == NULL) { goto failed; } memcpy(page2kva(newpage), page2kva(page), PGSIZE); page = newpage, newpage = NULL; } } page_insert(mm->pgdir, page, addr, perm); if (newpage != NULL) { free_page(newpage); } } ret = 0; failed: unlock_mm(mm); return ret; }
int do_execve(const char *filename, const char **argv, const char **envp) { static_assert(EXEC_MAX_ARG_LEN >= FS_MAX_FPATH_LEN); struct mm_struct *mm = current->mm; char local_name[PROC_NAME_LEN + 1]; memset(local_name, 0, sizeof(local_name)); char *kargv[EXEC_MAX_ARG_NUM], *kenvp[EXEC_MAX_ENV_NUM]; const char *path; int ret = -E_INVAL; lock_mm(mm); #if 0 if (name == NULL) { snprintf(local_name, sizeof(local_name), "<null> %d", current->pid); } else { if (!copy_string(mm, local_name, name, sizeof(local_name))) { unlock_mm(mm); return ret; } } #endif snprintf(local_name, sizeof(local_name), "<null> %d", current->pid); int argc = 0, envc = 0; if ((ret = copy_kargv(mm, kargv, argv, EXEC_MAX_ARG_NUM, &argc)) != 0) { unlock_mm(mm); return ret; } if ((ret = copy_kargv(mm, kenvp, envp, EXEC_MAX_ENV_NUM, &envc)) != 0) { unlock_mm(mm); put_kargv(argc, kargv); return ret; } #if 0 int i; kprintf("## fn %s\n", filename); kprintf("## argc %d\n", argc); for(i=0;i<argc;i++) kprintf("## %08x %s\n", kargv[i], kargv[i]); kprintf("## envc %d\n", envc); for(i=0;i<envc;i++) kprintf("## %08x %s\n", kenvp[i], kenvp[i]); #endif //path = argv[0]; //copy_from_user (mm, &path, argv, sizeof (char*), 0); path = filename; unlock_mm(mm); /* linux never do this */ //fs_closeall(current->fs_struct); /* sysfile_open will check the first argument path, thus we have to use a user-space pointer, and argv[0] may be incorrect */ int fd; if ((ret = fd = sysfile_open(path, O_RDONLY)) < 0) { goto execve_exit; } if (mm != NULL) { mm->lapic = -1; mp_set_mm_pagetable(NULL); if (mm_count_dec(mm) == 0) { exit_mmap(mm); put_pgdir(mm); bool intr_flag; local_intr_save(intr_flag); { list_del(&(mm->proc_mm_link)); } local_intr_restore(intr_flag); mm_destroy(mm); } current->mm = NULL; } put_sem_queue(current); ret = -E_NO_MEM; /* init signal */ put_sighand(current); if ((current->signal_info.sighand = sighand_create()) == NULL) { goto execve_exit; } sighand_count_inc(current->signal_info.sighand); put_signal(current); if ((current->signal_info.signal = signal_create()) == NULL) { goto execve_exit; } signal_count_inc(current->signal_info.signal); if ((current->sem_queue = sem_queue_create()) == NULL) { goto execve_exit; } sem_queue_count_inc(current->sem_queue); if ((ret = load_icode(fd, argc, kargv, envc, kenvp)) != 0) { goto execve_exit; } set_proc_name(current, local_name); if (do_execve_arch_hook (argc, kargv) < 0) goto execve_exit; put_kargv(argc, kargv); return 0; execve_exit: put_kargv(argc, kargv); put_kargv(envc, kenvp); /* exec should return -1 if failed */ //return ret; do_exit(ret); panic("already exit: %e.\n", ret); }
void *linux_regfile_mmap2(void *addr, size_t len, int prot, int flags, int fd, size_t off) { int subret = -E_INVAL; struct mm_struct *mm = current->mm; assert(mm != NULL); if (len == 0) { return -1; } lock_mm(mm); uintptr_t start = ROUNDDOWN(addr, PGSIZE); len = ROUNDUP(len, PGSIZE); uint32_t vm_flags = VM_READ; if (prot & PROT_WRITE) { vm_flags |= VM_WRITE; } if (prot & PROT_EXEC) { vm_flags |= VM_EXEC; } if (flags & MAP_STACK) { vm_flags |= VM_STACK; } if (flags & MAP_ANONYMOUS) { vm_flags |= VM_ANONYMOUS; } subret = -E_NO_MEM; if (start == 0 && (start = get_unmapped_area(mm, len)) == 0) { goto out_unlock; } uintptr_t end = start + len; struct vma_struct *vma = find_vma(mm, start); if (vma == NULL || vma->vm_start >= end) { vma = NULL; } else if (!(flags & MAP_FIXED)) { start = get_unmapped_area(mm, len); vma = NULL; } else if (!(vma->vm_flags & VM_ANONYMOUS)) { goto out_unlock; } else if (vma->vm_start == start && end == vma->vm_end) { vma->vm_flags = vm_flags; } else { assert(vma->vm_start <= start && end <= vma->vm_end); if ((subret = mm_unmap_keep_pages(mm, start, len)) != 0) { goto out_unlock; } vma = NULL; } if (vma == NULL && (subret = mm_map(mm, start, len, vm_flags, &vma)) != 0) { goto out_unlock; } if (!(flags & MAP_ANONYMOUS)) { vma_mapfile(vma, fd, off << 12, NULL); } subret = 0; out_unlock: unlock_mm(mm); return subret == 0 ? start : -1; }
int do_linux_waitpid(int pid, int *code_store) { struct mm_struct *mm = current->mm; if (code_store != NULL) { if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) { return -E_INVAL; } } struct proc_struct *proc, *cproc; bool intr_flag, haskid; repeat: cproc = current; haskid = 0; if (pid > 0) { proc = find_proc(pid); if (proc != NULL) { do { if (proc->parent == cproc) { haskid = 1; if (proc->state == PROC_ZOMBIE) { goto found; } break; } cproc = next_thread(cproc); } while (cproc != current); } } /* we do NOT have group id, so..*/ else if(pid==0 || pid==-1){ /* pid == 0 */ do { proc = cproc->cptr; for (; proc != NULL; proc = proc->optr) { haskid = 1; if (proc->state == PROC_ZOMBIE) { goto found; } } cproc = next_thread(cproc); } while (cproc != current); }else{ //pid<-1 //TODO return -E_INVAL; } if (haskid) { current->state = PROC_SLEEPING; current->wait_state = WT_CHILD; schedule(); may_killed(); goto repeat; } return -E_BAD_PROC; found: if (proc == idleproc || proc == initproc) { panic("wait idleproc or initproc.\n"); } int exit_code = proc->exit_code; int return_pid = proc->pid; local_intr_save(intr_flag); { unhash_proc(proc); remove_links(proc); } local_intr_restore(intr_flag); put_kstack(proc); kfree(proc); int ret = 0; if (code_store != NULL) { lock_mm(mm); { int status = exit_code << 8; if (!copy_to_user(mm, code_store, &status, sizeof(int))) { ret = -E_INVAL; } } unlock_mm(mm); } return (ret == 0) ? return_pid : ret; }