void sleep(int ticks) { if (getCurrentThread() == NULL) { // not ready to wait! uint64_t then = getUptime() + (uint64_t)ticks; while (getUptime() < then); } else { uint64_t then = getUptime() + (uint64_t)ticks; uint64_t nanoThen = then * (uint64_t)1000000; cli(); lockSched(); TimedEvent ev; timedPost(&ev, nanoThen); while (getNanotime() <= nanoThen) { waitThread(getCurrentThread()); unlockSched(); kyield(); cli(); lockSched(); }; timedCancel(&ev); unlockSched(); sti(); }; };
void threadExit(Thread *thread, int status) { if (thread->pid == 0) { panic("a kernel thread tried to threadExit()"); }; if (thread->pid == 1) { panic("init terminated with status %d!", status); }; // don't break the CPU runqueue ASM("cli"); lockSched(); // init will adopt all orphans Thread *scan = thread; do { if (scan->pidParent == thread->pid) { scan->pidParent = 1; }; scan = scan->next; } while (scan != thread); // terminate us thread->status = status; thread->flags |= THREAD_TERMINATED; // find the parent Thread *parent = thread; do { parent = parent->next; } while (parent->pid != thread->pidParent); // tell the parent that its child has died siginfo_t siginfo; siginfo.si_signo = SIGCHLD; if (status >= 0) { siginfo.si_code = CLD_EXITED; } else { siginfo.si_code = CLD_KILLED; }; siginfo.si_pid = thread->pid; siginfo.si_status = status; siginfo.si_uid = thread->ruid; sendSignal(parent, &siginfo); unlockSched(); ASM("sti"); };
int signalPid(int pid, int signo) { if (pid == currentThread->pid) { currentThread->therrno = EINVAL; return -1; }; lockSched(); Thread *thread = currentThread->next; while (thread != currentThread) { if (thread->pid == pid) { break; }; thread = thread->next; }; if (thread->flags & THREAD_TERMINATED) { currentThread->therrno = ESRCH; unlockSched(); return -1; }; if (thread == currentThread) { currentThread->therrno = ESRCH; unlockSched(); return -1; }; if (!canSendSignal(currentThread, thread, signo)) { currentThread->therrno = EPERM; unlockSched(); return -1; }; siginfo_t si; si.si_signo = signo; if (signo != 0) sendSignal(thread, &si); unlockSched(); return 0; };
static ssize_t termWrite(Inode *inode, File *file, const void *buffer, size_t size, off_t pos) { if (getCurrentThread()->creds->pgid != termGroup) { cli(); lockSched(); siginfo_t si; si.si_signo = SIGTTOU; sendSignal(getCurrentThread(), &si); unlockSched(); sti(); ERRNO = ENOTTY; return -1; }; kputbuf((const char*) buffer, size); return size; };
void onTick() { lockSched(); int doResched = 0; while (1) { if (timedEvents == NULL) break; if (getNanotime() <= timedEvents->nanotime) break; TimedEvent *ev = timedEvents; Thread *thread = ev->thread; timedEvents = ev->next; if (timedEvents != NULL) timedEvents->prev = NULL; ev->prev = ev->next = NULL; doResched = doResched || signalThread(thread); }; unlockSched(); if (doResched) kyield(); };
static ssize_t termRead(Inode *inode, File *fp, void *buffer, size_t size, off_t pos) { if (getCurrentThread()->creds->pgid != termGroup) { cli(); lockSched(); siginfo_t si; si.si_signo = SIGTTIN; sendSignal(getCurrentThread(), &si); unlockSched(); sti(); ERRNO = ENOTTY; return -1; }; int count = semWaitGen(&semCount, (int) size, SEM_W_INTR, 0); if (count < 0) { ERRNO = -count; return -1; }; semWait(&semInput); if (size > (size_t) count) size = (size_t) count; ssize_t out = 0; while (size > 0) { if (inputRead == INPUT_BUFFER_SIZE) inputRead = 0; size_t max = INPUT_BUFFER_SIZE - inputRead; if (max > size) max = size; memcpy(buffer, &inputBuffer[inputRead], max); size -= max; out += max; inputRead += max; buffer = (void*)((uint64_t)buffer + max); }; semSignal(&semInput); return out; };
int elfExec(Regs *regs, const char *path, const char *pars, size_t parsz) { //getCurrentThread()->therrno = ENOEXEC; vfsLockCreation(); struct stat st; int error = vfsStat(path, &st); if (error != 0) { vfsUnlockCreation(); return sysOpenErrno(error); }; if (!vfsCanCurrentThread(&st, 1)) { vfsUnlockCreation(); getCurrentThread()->therrno = EPERM; return -1; }; File *fp = vfsOpen(path, VFS_CHECK_ACCESS, &error); if (fp == NULL) { vfsUnlockCreation(); return sysOpenErrno(error); }; vfsUnlockCreation(); if (fp->seek == NULL) { vfsClose(fp); getCurrentThread()->therrno = EIO; return -1; }; if (fp->dup == NULL) { vfsClose(fp); getCurrentThread()->therrno = EIO; return -1; }; Elf64_Ehdr elfHeader; if (vfsRead(fp, &elfHeader, sizeof(Elf64_Ehdr)) < sizeof(Elf64_Ehdr)) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (memcmp(elfHeader.e_ident, "\x7f" "ELF", 4) != 0) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (elfHeader.e_ident[EI_CLASS] != ELFCLASS64) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (elfHeader.e_ident[EI_DATA] != ELFDATA2LSB) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (elfHeader.e_ident[EI_VERSION] != 1) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (elfHeader.e_type != ET_EXEC) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (elfHeader.e_phentsize < sizeof(Elf64_Phdr)) { vfsClose(fp); getCurrentThread()->therrno = ENOEXEC; return -1; }; ProgramSegment *segments = (ProgramSegment*) kmalloc(sizeof(ProgramSegment)*(elfHeader.e_phnum)); memset(segments, 0, sizeof(ProgramSegment) * elfHeader.e_phnum); int interpNeeded = 0; Elf64_Dyn *dynamic; unsigned int i; for (i=0; i<elfHeader.e_phnum; i++) { fp->seek(fp, elfHeader.e_phoff + i * elfHeader.e_phentsize, SEEK_SET); Elf64_Phdr proghead; if (vfsRead(fp, &proghead, sizeof(Elf64_Phdr)) < sizeof(Elf64_Phdr)) { kfree(segments); getCurrentThread()->therrno = ENOEXEC; return -1; }; if (proghead.p_type == PT_PHDR) { continue; } else if (proghead.p_type == PT_NULL) { continue; } else if (proghead.p_type == PT_LOAD) { if (proghead.p_vaddr < 0x1000) { vfsClose(fp); kfree(segments); getCurrentThread()->therrno = ENOEXEC; return -1; }; if ((proghead.p_vaddr+proghead.p_memsz) > 0x8000000000) { vfsClose(fp); kfree(segments); return -1; }; uint64_t start = proghead.p_vaddr; segments[i].index = (start)/0x1000; uint64_t end = proghead.p_vaddr + proghead.p_memsz; uint64_t size = end - start; uint64_t numPages = ((start + size) / 0x1000) - segments[i].index + 1; //if (size % 0x1000) numPages++; segments[i].count = (int) numPages; segments[i].fileOffset = proghead.p_offset; segments[i].memorySize = proghead.p_memsz; segments[i].fileSize = proghead.p_filesz; segments[i].loadAddr = proghead.p_vaddr; segments[i].flags = 0; if (proghead.p_flags & PF_R) { segments[i].flags |= PROT_READ; }; if (proghead.p_flags & PF_W) { segments[i].flags |= PROT_WRITE; }; if (proghead.p_flags & PF_X) { segments[i].flags |= PROT_EXEC; }; } else if (proghead.p_type == PT_INTERP) { interpNeeded = 1; } else if (proghead.p_type == PT_DYNAMIC) { dynamic = (Elf64_Dyn*) proghead.p_vaddr; } else { kfree(segments); getCurrentThread()->therrno = ENOEXEC; return -1; }; }; // set the signal handler to default. getCurrentThread()->rootSigHandler = 0; // thread name strcpy(getCurrentThread()->name, path); // set the execPars Thread *thread = getCurrentThread(); if (thread->execPars != NULL) kfree(thread->execPars); thread->execPars = (char*) kmalloc(parsz); thread->szExecPars = parsz; memcpy(thread->execPars, pars, parsz); // create a new address space ProcMem *pm = CreateProcessMemory(); // switch the address space, so that AddSegment() can optimize mapping lockSched(); ProcMem *oldPM = thread->pm; thread->pm = pm; unlockSched(); SetProcessMemory(pm); DownrefProcessMemory(oldPM); // pass 1: allocate the frames and map them for (i=0; i<(elfHeader.e_phnum); i++) { if (segments[i].count > 0) { FrameList *fl = palloc_later(segments[i].count, segments[i].fileOffset, segments[i].fileSize); if (AddSegment(pm, segments[i].index, fl, segments[i].flags) != 0) { getCurrentThread()->therrno = ENOEXEC; pdownref(fl); DownrefProcessMemory(pm); break; }; pdownref(fl); }; }; // change the fpexec if (thread->fpexec != NULL) { if (thread->fpexec->close != NULL) thread->fpexec->close(thread->fpexec); kfree(thread->fpexec); }; thread->fpexec = fp; // make sure we jump to the entry upon return regs->rip = elfHeader.e_entry; // the errnoptr is now invalid thread->errnoptr = NULL; // close all files marked with O_CLOEXEC (on glidx a.k.a. FD_CLOEXEC) spinlockAcquire(&getCurrentThread()->ftab->spinlock); for (i=0; i<MAX_OPEN_FILES; i++) { File *fp = getCurrentThread()->ftab->entries[i]; if (fp != NULL) { if (fp->oflag & O_CLOEXEC) { getCurrentThread()->ftab->entries[i] = NULL; vfsClose(fp); }; }; }; spinlockRelease(&getCurrentThread()->ftab->spinlock); // suid/sgid stuff if (st.st_mode & VFS_MODE_SETUID) { thread->euid = st.st_uid; //thread->ruid = st.st_uid; //thread->suid = st.st_uid; thread->flags |= THREAD_REBEL; }; if (st.st_mode & VFS_MODE_SETGID) { thread->egid = st.st_gid; //thread->rgid = st.st_gid; //thread->sgid = st.st_gid; thread->flags |= THREAD_REBEL; }; if (interpNeeded) { linkInterp(regs, dynamic, pm); }; return 0; };
int pollThread(Regs *regs, int pid, int *stat_loc, int flags) { if (kernelStatus != KERNEL_RUNNING) { currentThread->therrno = EPERM; return -1; }; int sigcnt = getCurrentThread()->sigcnt; lockSched(); ASM("cli"); Thread *threadToKill = NULL; Thread *thread = currentThread->next; while (thread != currentThread) { if (thread->pidParent == currentThread->pid) { if ((thread->pid == pid) || (pid == -1)) { if (thread->flags & THREAD_TERMINATED) { threadToKill = thread; *stat_loc = thread->status; // unlink from the runqueue thread->prev->next = thread->next; thread->next->prev = thread->prev; break; }; }; }; thread = thread->next; }; // when WNOHANG is clear while ((threadToKill == NULL) && ((flags & WNOHANG) == 0)) { //currentThread->flags |= THREAD_WAITING; //currentThread->therrno = ECHILD; //*((int*)®s->rax) = -1; //switchTask(regs); getCurrentThread()->flags |= THREAD_WAITING; unlockSched(); kyield(); if (getCurrentThread()->sigcnt > sigcnt) { ERRNO = EINTR; return -1; }; lockSched(); }; unlockSched(); ASM("sti"); // when WNOHANG is set if (threadToKill == NULL) { currentThread->therrno = ECHILD; return -1; }; // there is a process ready to be deleted, it's already removed from the runqueue. kfree(thread->stack); DownrefProcessMemory(thread->pm); ftabDownref(thread->ftab); if (thread->fpexec != NULL) { if (thread->fpexec->close != NULL) thread->fpexec->close(thread->fpexec); kfree(thread->fpexec); }; if (thread->execPars != NULL) kfree(thread->execPars); int ret = thread->pid; kfree(thread); return ret; };
int termIoctl(Inode *inode, File *fp, uint64_t cmd, void *argp) { Thread *target = NULL; Thread *scan; int pgid; struct termios *tc = (struct termios*) argp; TermWinSize *winsz = (TermWinSize*) argp; switch (cmd) { case IOCTL_TTY_GETATTR: memcpy(tc, &termState, sizeof(struct termios)); return 0; case IOCTL_TTY_SETATTR: termState.c_iflag = tc->c_iflag; termState.c_oflag = tc->c_oflag; termState.c_cflag = tc->c_cflag; termState.c_lflag = tc->c_lflag; return 0; case IOCTL_TTY_GETPGID: *((int*)argp) = termGroup; return 0; case IOCTL_TTY_SETPGID: if (getCurrentThread()->creds->sid != 1) { ERRNO = ENOTTY; return -1; }; pgid = *((int*)argp); cli(); lockSched(); scan = getCurrentThread(); do { if (scan->creds != NULL) { if (scan->creds->pgid == pgid) { target = scan; break; }; }; scan = scan->next; } while (scan != getCurrentThread()); if (target == NULL) { unlockSched(); sti(); ERRNO = EPERM; return -1; }; if (target->creds->sid != 1) { unlockSched(); sti(); ERRNO = EPERM; return -1; }; unlockSched(); sti(); termGroup = pgid; return 0; case IOCTL_TTY_ISATTY: return 0; case IOCTL_TTY_GETSIZE: getConsoleSize(&winsz->ws_col, &winsz->ws_row); winsz->ws_xpixel = 0; winsz->ws_ypixel = 0; return 0; default: ERRNO = ENODEV; return -1; }; };