diff --git a/Makefile b/Makefile index 997769c..1ebe675 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CPPFLAGS = -MMD AS=nasm ASFLAGS += -f elf32 LDFLAGS += -m elf_i386 -CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector -fno-tree-vectorize -D__KERNEL__ +CFLAGS += -m32 -pipe -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector -fno-tree-vectorize -D__KERNEL__ #keep .i and .s #CFLAGS += -save-temps #CFLAGS += -fanalyzer -Wno-analyzer-malloc-leak -Wno-analyzer-out-of-bounds @@ -88,11 +88,16 @@ test: clean kernel disk.img run:kernel disk.img ## Run the OS on qemu qemu-system-x86_64 -kernel $< -serial stdio $(QEMU_OPT) -debug: CFLAGS += $(DEBUG_FLAGS) ## Run the OS on qemu and attach a debugger to it (may need a clean befor to have the debug symbols) +debug: CFLAGS += $(DEBUG_FLAGS) ## Run the OS on qemu and attach a debugger to it (may need a clean before to have the debug symbols) debug: CXXFLAGS += $(DEBUG_FLAGS) debug:kernel kernel.debug disk.img gdb -q -x debug.gdb +isodebug: CFLAGS += $(DEBUG_FLAGS) ## Same than previous but kernel is loaded by grub. So, for example, we can access the elf debug info +isodebug: CXXFLAGS += $(DEBUG_FLAGS) +isodebug:fd.iso disk.img + gdb -q -x debug.iso.gdb + debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST debug_test: debug diff --git a/arch/x86/exception.c b/arch/x86/exception.c index 512f8c5..61f5616 100644 --- a/arch/x86/exception.c +++ b/arch/x86/exception.c @@ -53,10 +53,12 @@ void print_handler(struct cpu_state *frame, ulong intr) void pagefault_handler(struct cpu_state *frame, ulong intr) { - + // PAGE_FAULT is a interrupt with an error code (see exception_wrapper.S) + uint32_t error_code = cpu_context_get_EX_err(frame); struct thread *current = getCurrentThread(); - assert(frame == current->cpuState); + if (cpu_context_is_in_user_mode(current->cpuState)) { + assert(frame == current->cpuState); // pagefault in kernel not supported ATM struct uAddrSpace *as = processGetAddrSpace(current->process); vaddr_t faultAddr = cpu_context_get_EX_faulting_vaddr(frame); @@ -70,8 +72,6 @@ void pagefault_handler(struct cpu_state *frame, ulong intr) if (!uAddrSpaceHeapCheckNAlloc(as, faultAddr)) goto release_context; - // PAGE_FAULT is a interrupt with an error code (see exception_wrapper.S) - uint32_t error_code = cpu_context_get_EX_err(frame); int ret = uAddrSpaceSolvePageFault(as, faultAddr, error_code & 0x2); if (!ret) diff --git a/core/main.c b/core/main.c index d04addd..c3a188f 100644 --- a/core/main.c +++ b/core/main.c @@ -46,7 +46,7 @@ void idleThread(void *arg) #define FILE_MAX_SIZE 64 // In nb of sectors void loadUserSpace() { - struct ata_partition *part = ATAGetPartition(1); + struct ata_partition *part = ATAGetPartitionByType(PART_TYPE_LINUX); if (part == NULL) { printf("No user partition found\n"); diff --git a/core/process.c b/core/process.c index 6d5b0a3..cff0b38 100644 --- a/core/process.c +++ b/core/process.c @@ -5,12 +5,14 @@ #include "list.h" #include "mmuContext.h" #include "types.h" +#include "thread.h" #include "uaddrspace.h" struct process { char name[PROCESS_NAME_MAX_LENGTH]; int ref; - int pid; + pid_t pid; + pid_t nextTid; struct uAddrSpace *addrSpace; struct thread *thList; @@ -47,7 +49,8 @@ struct process *processCreate(char *name) new->ref = 1; disable_IRQs(flags); - new->pid = nextPid++; + new->pid = nextPid++; + new->nextTid = new->pid; list_add_tail(processList, new); restore_IRQs(flags); @@ -66,7 +69,7 @@ void processListPrint() struct thread *th; int nbTh; - printf("%d %s %d %d\n", proc->pid, proc->name, processCountThread(proc), proc->ref); + printf("%lu %s %d %d\n", proc->pid, proc->name, processCountThread(proc), proc->ref); list_foreach_named(proc->thList, th, nbTh, prevInProcess, nextInProcess) { if (th == cur) { @@ -180,3 +183,47 @@ struct uAddrSpace *processGetAddrSpace(struct process *proc){ int processInitHeap(struct process *proc, uaddr_t lastUserAddr){ return uAddrSpaceSetHeap(proc->addrSpace, lastUserAddr, 0); } + +pid_t processGetId(struct process *proc){ + return proc->pid; +} + +pid_t processGetNextTid(struct process *proc){ + return proc->nextTid++; +} + +//Should be called with IRQ disabled +struct thread *processGetThread(struct process *proc, pid_t tid) +{ + int count; + struct thread *th; + struct thread *thFound = NULL; + + list_foreach_named(proc->thList, th, count, prevInProcess, nextInProcess) + { + if (th->tid == tid) { + thFound = th; + break; + } + } + return thFound; +} + +int processJoinThread(struct process *proc, pid_t tid) +{ + uint32_t flags; + struct thread *th; + int ret = -1; + + disable_IRQs(flags); + + th = processGetThread(proc, tid); + if (th && th->wqExit) { + wait(th->wqExit); + ret = 0; + } + + restore_IRQs(flags); + + return ret; +} diff --git a/core/process.h b/core/process.h index f5935f7..2aba9ef 100644 --- a/core/process.h +++ b/core/process.h @@ -1,9 +1,11 @@ #pragma once -#include "thread.h" +#include "types.h" #define PROCESS_NAME_MAX_LENGTH 32 +typedef unsigned long int pid_t; struct process; +struct thread; int processSetup(); struct process *processCreate(char *name); @@ -18,3 +20,6 @@ int processRemoveThread(struct thread *th); struct mmu_context *processGetMMUContext(struct process *th); struct uAddrSpace *processGetAddrSpace(struct process *proc); int processInitHeap(struct process *proc, uaddr_t lastUserAddr); +pid_t processGetId(struct process *proc); +pid_t processGetNextTid(struct process *proc); +int processJoinThread(struct process *proc, pid_t tid); diff --git a/core/syscall.c b/core/syscall.c index a760424..c1e2dc3 100644 --- a/core/syscall.c +++ b/core/syscall.c @@ -1,6 +1,8 @@ #include "syscall.h" +#include "kernel.h" #include "keyboard.h" #include "klibc.h" +#include "paging.h" #include "process.h" #include "stdarg.h" #include "thread.h" @@ -9,7 +11,6 @@ #include "uaddrspace.h" #include "zero.h" - int syscallExecute(int syscallId, const struct cpu_state *userCtx) { @@ -43,17 +44,17 @@ int syscallExecute(int syscallId, const struct cpu_state *userCtx) printf("Got 5args from userspace %d %d %d %d %d\n", arg1, arg2, arg3, arg4, arg5); break; } - case SYSCALL_ID_BRK:{ + case SYSCALL_ID_BRK: { struct uAddrSpace *as; uaddr_t newHeapTop; - as = processGetAddrSpace(getCurrentThread()->process); + as = processGetAddrSpace(getCurrentThread()->process); ret = syscallGet1arg(userCtx, (unsigned int *)&newHeapTop); if (ret != 0) break; threadChangeCurrentContext(uAddrSpaceGetMMUContext(as)); - //TODO : what if *newHeapTop raise page fault? + // TODO : what if *newHeapTop raise page fault? ret = sysBrk(as, newHeapTop); threadChangeCurrentContext(NULL); break; @@ -110,6 +111,83 @@ int syscallExecute(int syscallId, const struct cpu_state *userCtx) ret = uAddrSpaceUnmap(as, uaddr, size); break; } + case SYSCALL_ID_NEW_THREAD: { + struct uAddrSpace *as; + thread_id_t threadIdPtr; + thread_id_t threadId; + uaddr_t funcAddr; + uint32_t arg1, arg2; + size_t stackSize; + + ret = syscallGet5args(userCtx, (unsigned int *)&threadIdPtr, + (unsigned int *)&funcAddr, (unsigned int *)&arg1, + (unsigned int *)&arg2, (unsigned int *)&stackSize); + if (ret) + break; + if (stackSize <= 0) { + ret = -EINVAL; + break; + } + if (memcpyFromUser((vaddr_t)&threadId, threadIdPtr, sizeof(threadId)) != + sizeof(threadId)) { + ret = -EFAULT; + break; + } + + as = processGetAddrSpace(getCurrentThread()->process); + stackSize = ALIGN(stackSize, PAGE_SIZE); + + uaddr_t stackAddr = 0; + ret = zeroMmap(as, &stackAddr, stackSize, PAGING_MEM_READ | PAGING_MEM_WRITE, 0); + + if (ret) + break; + + struct thread *th = threadCreateUser(NULL, getCurrentThread()->process, funcAddr, + arg1, arg2, stackAddr + stackSize); + + if (th == NULL) { + ret = -ENOMEM; + uAddrSpaceUnmap(as, stackAddr, stackSize); + } + threadId = threadGetId(th); + + if (memcpyToUser(threadIdPtr, (vaddr_t)&threadId, sizeof(threadId)) != + sizeof(threadId)) { + ret = -EFAULT; + break; + } + + break; + } + case SYSCALL_ID_USLEEP: { + unsigned int sleep; + + ret = syscallGet1arg(userCtx, &sleep); + if (ret) + break; + + ret = threadUsleep(sleep); + + break; + } + case SYSCALL_ID_GETPID: { + ret = processGetId(getCurrentThread()->process); + break; + } + case SYSCALL_ID_GETTID: { + ret = threadGetId(getCurrentThread()); + break; + } + case SYSCALL_ID_THREAD_JOIN: { + thread_id_t tid; + + ret = syscallGet1arg(userCtx, (unsigned int *)&tid); + if (ret) + break; + ret = processJoinThread(getCurrentThread()->process, tid); + break; + } default: printf("Unknon syscall id %d\n", syscallId); ret = -ENOENT; diff --git a/core/syscall.h b/core/syscall.h index 444789b..40e82df 100644 --- a/core/syscall.h +++ b/core/syscall.h @@ -11,6 +11,11 @@ #define SYSCALL_ID_BRK 6 #define SYSCALL_ID_MMAP 7 #define SYSCALL_ID_MUNMAP 8 +#define SYSCALL_ID_NEW_THREAD 9 +#define SYSCALL_ID_USLEEP 10 +#define SYSCALL_ID_GETPID 11 +#define SYSCALL_ID_GETTID 12 +#define SYSCALL_ID_THREAD_JOIN 13 #ifdef __KERNEL__ int syscallExecute(int syscallId, const struct cpu_state *user_ctx); diff --git a/core/thread.c b/core/thread.c index ae0f293..6e78c12 100644 --- a/core/thread.c +++ b/core/thread.c @@ -12,6 +12,12 @@ static struct thread *currentThread; static struct thread *threadWithTimeout; +static thread_id_t nextTid; // This is the TID for kernel thread ONLY + +pid_t threadGetId(struct thread *th) +{ + return th->tid; +} static void threadPrepareContext(struct thread *th); @@ -83,7 +89,9 @@ struct thread *threadCreate(const char *name, cpu_kstate_function_arg1_t func, v (cpu_kstate_function_arg1_t *)threadExit, 0)) goto free_mem; - thread->state = READY; + thread->state = READY; + thread->tid = nextTid++; + thread->wqExit = NULL; uint32_t flags; disable_IRQs(flags); list_add_tail(currentThread, thread); @@ -110,6 +118,14 @@ struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t } thread->stackSize = THREAD_DEFAULT_STACK_SIZE; + thread->wqExit = (struct wait_queue * )malloc(sizeof(struct wait_queue)); + if (!thread->wqExit) { + free((void *)thread->stackAddr); + free(thread); + return NULL; + } + waitQueueInit(thread->wqExit); + if (name) strzcpy(thread->name, name, THREAD_NAME_MAX_LENGTH); else @@ -124,6 +140,7 @@ struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t goto free_mem; thread->state = READY; + thread->tid = processGetNextTid(proc); uint32_t flags; disable_IRQs(flags); list_add_tail(currentThread, thread); @@ -143,9 +160,14 @@ void threadDelete(struct thread *thread) restore_IRQs(flags); assert(thread->state == EXITING); - if (thread->squattedContext) { - threadChangeCurrentContext(NULL); + if (thread->wqExit) { + waitUp(thread->wqExit); } + + if (thread->squattedContext) { + mmuContextUnref(thread->squattedContext); + } + if (thread->process) processRemoveThread(thread); @@ -292,6 +314,11 @@ int threadYield() } int threadMsleep(unsigned long msec) +{ + return threadUsleep(msec*1000); +} + +int threadUsleep(unsigned long usec) { uint32_t flags; struct thread *next, *current; @@ -299,12 +326,14 @@ int threadMsleep(unsigned long msec) disable_IRQs(flags); current = currentThread; - assertmsg(current->state == RUNNING, "thread %s is in state %d for %lu\n", current->name, - current->state, msec); + assertmsg(current->state == RUNNING, "thread %s is in state %d for %lu us\n", current->name, + current->state, usec); current->state = SLEEPING; current->sleepHaveTimeouted = 0; - current->jiffiesSleeping = msecs_to_jiffies(msec); + current->jiffiesSleeping = usecs_to_jiffies(usec); + if (!current->jiffiesSleeping) // sleep at least 1 jiffies + current->jiffiesSleeping = 1; next = threadSelectNext(); assert(next != current); diff --git a/core/thread.h b/core/thread.h index 0997676..5941ed9 100644 --- a/core/thread.h +++ b/core/thread.h @@ -4,6 +4,7 @@ struct thread; #include "cpu_context.h" #include "mem.h" #include "process.h" +#include "wait.h" #define THREAD_NAME_MAX_LENGTH 32 #define THREAD_DEFAULT_STACK_SIZE PAGE_SIZE @@ -17,8 +18,11 @@ typedef enum { EXITING } thread_state; +typedef unsigned long int thread_id_t; + struct thread { char name[THREAD_NAME_MAX_LENGTH]; + thread_id_t tid; struct cpu_state *cpuState; thread_state state; vaddr_t stackAddr; @@ -32,6 +36,7 @@ struct thread { // For User thread only struct thread *nextInProcess, *prevInProcess; struct process *process; + struct wait_queue *wqExit; // This will be signaled at thread exit (user only) /** * Address space currently "squatted" by the thread, or used to be @@ -83,8 +88,10 @@ int threadYield(); int threadWait(struct thread *current, struct thread *next, unsigned long msec); int threadUnsched(struct thread *th); int threadMsleep(unsigned long msec); +int threadUsleep(unsigned long usec); int threadOnJieffiesTick(); struct thread *getCurrentThread(); int threadAddThread(struct thread *th); int threadChangeCurrentContext(struct mmu_context *ctx); int threadCount(); +thread_id_t threadGetId(struct thread *th); diff --git a/core/uaddrspace.h b/core/uaddrspace.h index 9cf8cac..1456c4a 100644 --- a/core/uaddrspace.h +++ b/core/uaddrspace.h @@ -1,8 +1,9 @@ #pragma once #include "mmuContext.h" #include "process.h" +#include "stddef.h" +#include "stdint.h" #include "types.h" -#include struct uAddrSpace; struct uAddrVirtualReg; diff --git a/debug.iso.gdb b/debug.iso.gdb new file mode 100644 index 0000000..1816cc1 --- /dev/null +++ b/debug.iso.gdb @@ -0,0 +1,10 @@ +add-symbol-file userspace/user +# Thx to add-gnu-debuglink gdb should know that symbols are in kernel.debug. +# And by default, it should be looking at executable.debug +# But we still have to give him the executable he is suppose to debug (See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Separate-Debug-Files.html) +#add-symbol-file kernel.debug +file kernel +source custom_gdb_extension.py +#For ASM sources +directory arch/x86/:core +target remote | qemu-system-i386 -S -gdb stdio -m 16M -serial file:serialOut -hda fd.iso -hdb disk.img diff --git a/drivers/ata.c b/drivers/ata.c index fbd6aea..82922fa 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -521,6 +521,19 @@ struct ata_partition *ATAGetPartition(int id) return NULL; } +struct ata_partition *ATAGetPartitionByType(uint type) +{ + struct ata_partition *part; + int count; + list_foreach(partitions, part, count) + { + if (part->type == type) + return part; + } + + return NULL; +} + struct ata_partition *ATAPartitionCreate(uint type, uint size, uint32_t lba, struct ata_device *dev) { diff --git a/drivers/ata.h b/drivers/ata.h index b6180e3..304cb5b 100644 --- a/drivers/ata.h +++ b/drivers/ata.h @@ -104,5 +104,6 @@ int ATAReadPartitionSector(struct ata_partition *part, int offset, uint nbSector int ATAReadSector(struct ata_device *dev, int lba, uint nbSector, void *buf); int ATAWriteSector(struct ata_device *dev, int lba, uint nbSector, void *buf); struct ata_device *ATAGetDevice(int ctlId, int devId); +struct ata_partition *ATAGetPartitionByType(uint type); int ATAReadPartition(struct ata_device *dev); struct ata_partition *ATAGetPartition(int id); diff --git a/drivers/zero.c b/drivers/zero.c index 4a830f3..b55bec3 100644 --- a/drivers/zero.c +++ b/drivers/zero.c @@ -3,6 +3,7 @@ #include "alloc.h" #include "kernel.h" #include "klibc.h" +#include "mem.h" struct zeroMappedEntry { int refCnt; diff --git a/userspace/kernel/syscall.h b/userspace/kernel/syscall.h index 444789b..40e82df 100644 --- a/userspace/kernel/syscall.h +++ b/userspace/kernel/syscall.h @@ -11,6 +11,11 @@ #define SYSCALL_ID_BRK 6 #define SYSCALL_ID_MMAP 7 #define SYSCALL_ID_MUNMAP 8 +#define SYSCALL_ID_NEW_THREAD 9 +#define SYSCALL_ID_USLEEP 10 +#define SYSCALL_ID_GETPID 11 +#define SYSCALL_ID_GETTID 12 +#define SYSCALL_ID_THREAD_JOIN 13 #ifdef __KERNEL__ int syscallExecute(int syscallId, const struct cpu_state *user_ctx); diff --git a/userspace/libc.c b/userspace/libc.c index eec4ccc..1050639 100644 --- a/userspace/libc.c +++ b/userspace/libc.c @@ -5,6 +5,8 @@ #include "swintr.h" #include "sys/mman.h" #include "syscall.h" +#include "thread.h" +#include "unistd.h" int errno = 0; int memcmp(const void *aptr, const void *bptr, size_t size) @@ -263,8 +265,57 @@ int puts(const char *str) return ret; \ } +#define PRINT_UINT(name, type) \ + int print##name(type integer, char *str, size_t size) \ + { \ + char num[sizeof(integer) * 3]; \ + int i = 0; \ + int c = 0; \ + int ret = 0; \ + \ + do { \ + int digit = integer % 10; \ + num[i++] = (digit > 0) ? digit : -digit; \ + integer = integer / 10; \ + } while (integer != 0); \ + \ + for (i = i - 1; i >= 0; i--) { \ + if (str) { \ + if (size) { \ + str[c++] = num[i] + '0'; \ + size--; \ + ret++; \ + } else { \ + return ret; \ + } \ + } else { \ + ret++; \ + } \ + } \ + return ret; \ + } + PRINT_INT(Int, int); -PRINT_INT(Int64, long long int); +PRINT_INT(Lint, long int); +PRINT_INT(Llint, long long int); +PRINT_UINT(Uint, unsigned int); +PRINT_UINT(Luint, long unsigned int); +PRINT_UINT(Lluint, long long unsigned int); + +#define PRINT_PART(func, type, str, size, c, ret) \ + { \ + int s; \ + type d = va_arg(ap, type); \ + if (str) \ + s = func(d, &str[c], size); \ + else \ + s = func(d, NULL, size); \ + \ + size -= s; \ + c += s; \ + ret += s; \ + break; \ + } int vsnprintf(char *str, size_t size, const char *format, va_list ap) { @@ -272,24 +323,13 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) int i = 0; int c = 0; - while (format[i] != '\0' && (size || !str)) { + while (format[i] != '\0' && (size|| !str)) { switch (format[i]) { case '%': switch (format[i + 1]) { case 'i': - case 'd': { - int s; - int d = va_arg(ap, int); - if (str) - s = printInt(d, &str[c], size); - else - s = printInt(d, NULL, size); - - size -= s; - c += s; - ret += s; - break; - } + case 'd': PRINT_PART(printInt, int, str, size, c, ret) + case 'u': PRINT_PART(printUint, uint, str, size, c, ret) case 'p': case 'x': { char val[sizeof(int) * 2]; @@ -350,24 +390,15 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) switch (format[i + 2]) { case 'l': switch (format[i + 3]) { - case 'd': { - int s; - long long int d = va_arg(ap, long long int); - if (str) - s = printInt64(d, &str[c], size); - else - s = printInt64(d, NULL, size); - - size -= s; - c += s; - ret += s; - break; - } + case 'i': + case 'd': PRINT_PART(printLlint, long long int, str, size, c, ret) + case 'u': PRINT_PART(printLluint, long long unsigned int, str, size, c, ret) case 'p': case 'x': { char val[sizeof(long long int) * 2]; unsigned int valIdx = 0; - long long int d = va_arg(ap, long long int); + unsigned long long int d = + va_arg(ap, unsigned long long int); itoa(d, val, 16); if (str) { while (val[valIdx]) { @@ -388,17 +419,28 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) i++; break; case 'i': - case 'd': { - int s; - long int d = va_arg(ap, long int); - if (str) - s = printInt64(d, &str[c], size); - else - s = printInt64(d, NULL, size); - - size -= s; - c += s; - ret += s; + case 'd': PRINT_PART(printLint, long int, str, size, c, ret) + case 'u': + PRINT_PART(printLuint, long unsigned int, str, size, c, ret) + case 'p': + case 'x': { + char val[sizeof(int) * 2]; + unsigned int valIdx = 0; + unsigned long int d = va_arg(ap, unsigned long int); + itoa(d, val, 16); + if (str) { + while (val[valIdx]) { + if (size) { + str[c++] = val[valIdx++]; + size--; + ret++; + } else { + return ret; + } + } + } else { + ret += strlen(val); + } break; } } @@ -745,3 +787,40 @@ int munmap(void *addr, size_t len) return syscall2(SYSCALL_ID_MUNMAP, (unsigned int)addr, len); } + +/* As when a new thread is run, the params are passed by register (See cpu_ustate_init), use + * this function to simplify new thread usage*/ +static void thread_runner() +{ + register unsigned long int reg_arg1 asm("%eax"); + register unsigned long int reg_arg2 asm("%ebx"); + + start_routine *func = (start_routine *)reg_arg1; + void *arg = (void *)reg_arg2; + + func(arg); + _exit(0); +} + +int thread_create(pthread_t *thread, start_routine *func, void *arg, size_t stackSize) { + return syscall5(SYSCALL_ID_NEW_THREAD, (unsigned int)thread, (unsigned int)thread_runner, (unsigned int)func, + (unsigned int)arg, stackSize); +} + +int thread_join(pthread_t thread, void **retval){ + (void)retval; + return syscall1(SYSCALL_ID_THREAD_JOIN, (unsigned int)thread); +} + + +int usleep(useconds_t usec) { + return syscall1(SYSCALL_ID_USLEEP, (unsigned int)usec); +} + +pid_t gettid(void) { + return syscall0(SYSCALL_ID_GETTID); +} + +pid_t getpid(void) { + return syscall0(SYSCALL_ID_GETPID); +} diff --git a/userspace/libc.h b/userspace/libc.h index 378d023..9a12ba2 100644 --- a/userspace/libc.h +++ b/userspace/libc.h @@ -4,6 +4,7 @@ #include "stdint.h" #include "stddef.h" #include "minmax.h" +#include "unistd.h" #define islower(c) (('a' <= (c)) && ((c) <= 'z')) #define isupper(c) (('A' <= (c)) && ((c) <= 'Z')) @@ -63,3 +64,6 @@ void *malloc(size_t size); void *calloc(size_t nmemb, size_t size); void *realloc(void *ptr, size_t size); void free(void *ptr); +pid_t gettid(void); +pid_t getpid(void); + diff --git a/userspace/main_user.c b/userspace/main_user.c index 65ce7c7..6711c9d 100644 --- a/userspace/main_user.c +++ b/userspace/main_user.c @@ -3,7 +3,9 @@ #include "stdarg.h" #include "stddef.h" #include "sys/mman.h" +#include "thread.h" #include "tiny.h" +#include "unistd.h" int func_help() { @@ -15,6 +17,7 @@ int func_help() printf(" tiny: a tiny C-like interpreter\n"); printf(" mmap: test mmap \n"); printf(" munmap: test munmap \n"); + printf(" thread: run a user thread\n"); return 0; } @@ -118,11 +121,28 @@ int func_munmap() return ret; } -int main(int argc, char *argv[]) -{ +static void *print_hello(void *arg) { + (void)arg; + printf("Hello World from thread %lu\n", gettid()); + usleep(100); + + return NULL; +} + +int func_thread() { + pthread_t id; + void *retval; + thread_create(&id, print_hello, NULL, 4096); + thread_join(id, &retval); + + return 0; +} + +int main(int argc, char *argv[]) { (void)argc; (void)argv; char buf[64]; + assert(getpid() == gettid()); printf("Shell starting... type \"help\" for help\n"); while (1) { printf(">"); @@ -145,7 +165,10 @@ int main(int argc, char *argv[]) continue; } if (strcmp(buf, "tiny") == 0) { - func_tiny(); + pthread_t id; + void *retval; + thread_create(&id, func_tiny, NULL, 4096); + thread_join(id, &retval); continue; } if (strcmp(buf, "mmap") == 0) { @@ -156,6 +179,10 @@ int main(int argc, char *argv[]) func_munmap(); continue; } + if (strcmp(buf, "thread") == 0) { + func_thread(); + continue; + } } return 0; } diff --git a/userspace/thread.h b/userspace/thread.h new file mode 100644 index 0000000..786452c --- /dev/null +++ b/userspace/thread.h @@ -0,0 +1,8 @@ +#pragma once +#include + +typedef unsigned long int pthread_t; +typedef void *(start_routine)(void *); +int thread_create(pthread_t *thread, start_routine *func, void *arg, size_t stackSize); +/* retval is ignored ATM */ +int thread_join(pthread_t thread, void **retval); diff --git a/userspace/tiny.c b/userspace/tiny.c index 196d242..0b478b2 100644 --- a/userspace/tiny.c +++ b/userspace/tiny.c @@ -282,8 +282,9 @@ void run() /* Main program. */ -int func_tiny() +void * func_tiny(void *args) { + (void)args; printf("Enter your program then ESC\n\n"); printf("TinyC grammar\n"); printf(" ::= \n"); diff --git a/userspace/tiny.h b/userspace/tiny.h index 435c1e9..78f9747 100644 --- a/userspace/tiny.h +++ b/userspace/tiny.h @@ -1,4 +1,4 @@ #pragma once -int func_tiny(); +void * func_tiny(void *args); diff --git a/userspace/unistd.h b/userspace/unistd.h new file mode 100644 index 0000000..db3d631 --- /dev/null +++ b/userspace/unistd.h @@ -0,0 +1,6 @@ +#pragma once + +typedef unsigned int useconds_t; +int usleep(useconds_t usec); + +typedef unsigned long int pid_t;