diff --git a/src/arch/x86_64/isr.asm b/src/arch/x86_64/isr.asm index 08dfd33d..7aed7597 100644 --- a/src/arch/x86_64/isr.asm +++ b/src/arch/x86_64/isr.asm @@ -5,6 +5,12 @@ section .text push rbp + mov rbp, [rsp+24] ; save CS for usermode checking + ; check and patch ss if it is invalid because of the syscall instruction + cmp qword [rsp+48], 0x33 + jne .keepgoing + mov qword [rsp+48], 0x43 + .keepgoing: push rsi push rdi push r15 @@ -22,12 +28,13 @@ section .text - mov rax, ds - cmp rax,0x40 + + cmp rbp,0x3b jne .noswapgs swapgs .noswapgs: + mov rax, ds push rax mov rax, es push rax @@ -38,10 +45,10 @@ section .text ; now use kernel segments for data - mov rax, 0x30 - mov ds, rax - mov es, rax - mov ss, rax + ;mov rax, 0x38 + ;mov ds, rax + ;mov es, rax + ;mov ss, rax mov rdi,cr2 push rdi @@ -52,18 +59,15 @@ section .text add rsp, 24 ; cr2 gs fs + cmp rbp,0x3b + jne .noswapgspop + swapgs + .noswapgspop: + pop rax - cmp rax,0x43 - je .dontpopsegs mov es,rax pop rax mov ds,rax - swapgs - jmp .poprest - .dontpopsegs: - add rsp,8 ; ds - - .poprest: pop rax pop rbx pop rcx diff --git a/src/arch/x86_64/syscall.asm b/src/arch/x86_64/syscall.asm index 21c85a64..6a9ad127 100644 --- a/src/arch/x86_64/syscall.asm +++ b/src/arch/x86_64/syscall.asm @@ -2,12 +2,13 @@ section .rodata extern syscall_libc_log -func_count equ 1 +extern syscall_mmap +func_count equ 2 func_table: dq syscall_libc_log - + dq syscall_mmap section .text global asm_syscall_entry @@ -45,11 +46,14 @@ asm_syscall_entry: push r8 push r9 push r10 - + + + ; system V C abi expects the third param to be in rcx ; but the param is in rdx because of the syscall instruction - mov rcx, rdx + mov rcx, r10 + call [func_table + rax * 8] diff --git a/src/include/kernel/elf.h b/src/include/kernel/elf.h index 20495af5..62495c2a 100644 --- a/src/include/kernel/elf.h +++ b/src/include/kernel/elf.h @@ -60,6 +60,8 @@ typedef struct{ #define PH_TYPE_DYNAMIC 2 #define PH_TYPE_INTERPRETER 3 #define PH_TYPE_NOTE 4 +#define PH_TYPE_SHLIB 5 +#define PH_TYPE_PHDR 6 typedef struct{ uint32_t type; diff --git a/src/include/kernel/syscalls.h b/src/include/kernel/syscalls.h index c4726063..e1b93fdd 100644 --- a/src/include/kernel/syscalls.h +++ b/src/include/kernel/syscalls.h @@ -7,5 +7,6 @@ typedef struct { } syscallret; syscallret syscall_libc_log(char* str); +syscallret syscall_mmap(void* hint, size_t len, int prot, int flags, int fd, off_t offset); #endif diff --git a/src/include/kernel/vmm.h b/src/include/kernel/vmm.h index 82c52385..5426caa4 100644 --- a/src/include/kernel/vmm.h +++ b/src/include/kernel/vmm.h @@ -7,8 +7,10 @@ #define KERNEL_SPACE_START 0xFFFF800000000000 #define KERNEL_SPACE_END 0xFFFFFFFFFFFFFFFF -#define USER_SPACE_START 0x1000 -#define USER_SPACE_END 0x00007FFFFFFFFFFF +#define USER_SPACE_START 0x0 +#define USER_SPACE_END (void*)0x00007FFFFFFFFFFF + +#define USER_ALLOC_START (void*)0x1000 #define VMM_TYPE_FREE 0 #define VMM_TYPE_ANON 1 @@ -60,5 +62,6 @@ bool vmm_map(void* paddr, void* vaddr, size_t pagec, size_t mmuflags); void* vmm_alloc(size_t pagec, size_t mmuflags); bool vmm_setfree(void* addr, size_t pagec); bool vmm_allocnowat(void* addr, size_t mmuflags, size_t size); +void* vmm_allocfrom(void* addr, size_t mmuflags, size_t size); vmm_context* vmm_newcontext(); #endif diff --git a/src/sys/mm/vmm.c b/src/sys/mm/vmm.c index 00219c8c..707e61ff 100644 --- a/src/sys/mm/vmm.c +++ b/src/sys/mm/vmm.c @@ -106,6 +106,17 @@ static char* debugnames[] = { "FILE" }; +static void debug_dumpusermappings(){ + vmm_mapping* map = arch_getcls()->context->userstart; + + printf("User mappings:\n"); + + while(map != NULL){ + printf("START: %016p END: %016p MMUFLAGS: %016p DATA: %016p OFFSET: %lu TYPE: %s\n", map->start, map->end, map->mmuflags, map->data, map->offset, debugnames[map->type]); + map = map->next; + } + +} static void debug_dumpkernelmappings(){ vmm_mapping* map = kmapstart; @@ -375,6 +386,31 @@ void* vmm_alloc(size_t pagec, size_t mmuflags){ return addr; } +void* vmm_allocfrom(void* addr, size_t mmuflags, size_t pagec){ + + int* lock; + vmm_mapping** start; + + getcontextinfo(addr, &lock, &start); + + spinlock_acquire(lock); + + addr = findfirstfreearea(findmappingfromaddr(*start, addr), pagec); + + if(!addr) + goto _done; + + if(!setmap(start, addr, pagec, mmuflags, VMM_TYPE_ANON, 0, 0)) + addr = NULL; + + _done: + + spinlock_release(lock); + + return addr; + +} + bool vmm_unmap(void* addr, size_t pagec){ int* lock; @@ -448,6 +484,14 @@ bool vmm_allocnowat(void* addr, size_t mmuflags, size_t size){ spinlock_acquire(lock); + vmm_mapping* m = findmappingfromaddr(*start, addr); + + if(m->type != VMM_TYPE_FREE || (addr + size*PAGE_SIZE-1) > m->end){ + spinlock_release(lock); + return false; + } + + // XXX allocate 1 page at a time to reduce fragmentation void* paddr = pmm_alloc(size); @@ -480,7 +524,6 @@ bool vmm_dealwithrequest(void* addr){ if(!start) return false; spinlock_acquire(lock); - bool status; vmm_mapping* map = findmappingfromaddr(*start, addr); @@ -498,7 +541,7 @@ bool vmm_dealwithrequest(void* addr){ addr = (size_t)addr & ~(0xFFF); - printf("Demand paging %p\n", addr); + //printf("Demand paging %p\n", addr); diff --git a/src/sys/sched/elf.c b/src/sys/sched/elf.c index 739efcb0..6b064506 100644 --- a/src/sys/sched/elf.c +++ b/src/sys/sched/elf.c @@ -57,7 +57,7 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head size_t initialsize = 0; size_t argdatasize = 0; size_t envdatasize = 0; - size_t auxvsize = 5*8; // FIXME better way of keeping track of this + size_t auxvsize = 5*16; // FIXME better way of keeping track of this while(argv[argc]){ argdatasize += strlen(argv[argc]) + 1; @@ -90,8 +90,7 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head // get the addresses to copy the data to - elf_ph64* phdrdatastart = STACK_TOP - header.ph_count * header.ph_size; - char* argdatastart = (void*) phdrdatastart - argdatasize; + char* argdatastart = STACK_TOP - argdatasize; char* envdatastart = argdatastart - envdatasize; auxv64_t* auxvstart = (void*)(((uintptr_t)envdatastart & (~0xF)) - auxvsize); @@ -108,10 +107,8 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head // AT_PHDR auxvstart->a_type = AT_PHDR; - auxvstart->a_val = (uint64_t)phdrdatastart; + auxvstart->a_val = (uint64_t)phs; auxvstart++; - - memcpy(phdrdatastart, phs, header.ph_count * header.ph_size); // AT_PHENT @@ -123,6 +120,7 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head auxvstart->a_type = AT_PHNUM; auxvstart->a_val = header.ph_count; + auxvstart++; // AT_ENTRY @@ -133,6 +131,7 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head // AT_NULL auxvstart->a_type = AT_NULL; + auxvstart->a_val = 0; // copy the data @@ -174,6 +173,7 @@ int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ elf_header64 header; elf_header64 progheader; + vnode_t* prognode = node; elf_ph64 ph; int err = 0; @@ -193,7 +193,7 @@ int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ // and also save the executable's program headers // for auxv - elf_ph64 progphs[header.ph_count]; + elf_ph64* progphs; for(size_t currentph = 0; currentph < header.ph_count; ++currentph){ readc = vfs_read(&err, node, &ph, header.ph_size, header.ph_pos + currentph*header.ph_size); @@ -202,15 +202,16 @@ int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ if(readc != header.ph_size) return EINVAL; - memcpy(progphs+currentph, &ph, sizeof(elf_ph64)); - + if(ph.type == PH_TYPE_PHDR) + progphs = (elf_ph64*)ph.memaddr; + if(ph.type == PH_TYPE_INTERPRETER){ // load the name and elf header of the interpreter progheader = header; - char* interp = alloc(ph.fsize + 1); + interp = alloc(ph.fsize + 1); if(!interp) return ENOMEM; @@ -300,12 +301,66 @@ int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ } + // also load the executable memory image if we're using an interpreter + if(interp){ + for(size_t currentph = 0; currentph < header.ph_count; ++currentph){ + // read ph + + readc = vfs_read(&err, prognode, &ph, progheader.ph_size, progheader.ph_pos + currentph*progheader.ph_size); + + if(err) goto _fail; + + if(readc != progheader.ph_size){ + err = EINVAL; + goto _fail; + } + + if(ph.type != PH_TYPE_LOAD) + continue; + + if(!vmm_allocnowat((void*)ph.memaddr, phflagtommuflag(ph.flags), ph.msize)){ + err = ENOMEM; + goto _fail; + } + + // set up temporary mmu flags to copy data + + arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); + + // read program data + + readc = vfs_read(&err, prognode, (void*)ph.memaddr, ph.fsize, ph.offset); + + if(err) + goto _fail; + + if(readc != ph.fsize){ + err = EINVAL; + goto _fail; + } + + // zero if needed + + size_t zerocount = ph.msize - ph.fsize; + + if(zerocount){ + void* zeroaddr = ph.memaddr + ph.fsize; + memset(zeroaddr, 0, zerocount); + } + + // reset proper mmu flags + + arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, phflagtommuflag(ph.flags), ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); + + + } + } void* stack = stacksetup(interp, argv, env, progheader, progphs); - + arch_regs_setupuser(thread->regs, (void*)header.entry, stack, true); return 0; diff --git a/src/sys/syscalls/libclog.c b/src/sys/syscalls/libclog.c index 0251012f..26248017 100644 --- a/src/sys/syscalls/libclog.c +++ b/src/sys/syscalls/libclog.c @@ -17,7 +17,7 @@ syscallret syscall_libc_log(char* str){ strcpy(buf, str); - printf("%s", str); + printf("%s\n", str); ret.errno = 0; ret.ret = 0; diff --git a/src/sys/syscalls/mmap.c b/src/sys/syscalls/mmap.c new file mode 100644 index 00000000..34e1206b --- /dev/null +++ b/src/sys/syscalls/mmap.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +// linux abi + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_FILE 0x00 +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANON 0x20 +#define MAP_ANONYMOUS 0x20 +#define MAP_NORESERVE 0x4000 + +#define MS_ASYNC 0x01 +#define MS_INVALIDATE 0x02 +#define MS_SYNC 0x04 + +#define MCL_CURRENT 0x01 +#define MCL_FUTURE 0x02 + +#define POSIX_MADV_NORMAL 0 +#define POSIX_MADV_RANDOM 1 +#define POSIX_MADV_SEQUENTIAL 2 +#define POSIX_MADV_WILLNEED 3 +#define POSIX_MADV_DONTNEED 4 + +#define MADV_NORMAL 0 +#define MADV_RANDOM 1 +#define MADV_SEQUENTIAL 2 +#define MADV_WILLNEED 3 +#define MADV_DONTNEED 4 +#define MADV_FREE 8 + +#define MREMAP_MAYMOVE 1 +#define MREMAP_FIXED 2 + +#define MFD_CLOEXEC 1U +#define MFD_ALLOW_SEALING 2U + +syscallret syscall_mmap(void* hint, size_t len, int prot, int flags, int fd, off_t offset){ + syscallret retv; + + retv.ret = -1; + retv.errno = 0; + + if(len == 0 || hint > USER_SPACE_END){ + retv.errno = EINVAL; + return retv; + } + + if(flags != (MAP_ANON | MAP_PRIVATE)){ + retv.errno = ENOSYS; + return retv; + + } + + size_t mmuflags = 0; + + if(prot & PROT_READ) + mmuflags |= ARCH_MMU_MAP_READ; + + if(prot & PROT_WRITE) + mmuflags |= ARCH_MMU_MAP_WRITE; + + if(!prot & PROT_EXEC) + mmuflags |= ARCH_MMU_MAP_NOEXEC; + + mmuflags |= ARCH_MMU_MAP_USER; + + size_t plen = len / PAGE_SIZE + (len % PAGE_SIZE ? 1 : 0); + + void* ret = NULL; + + if(hint) + ret = vmm_allocnowat(hint, mmuflags, plen) ? hint : NULL; // TODO demand page + + if(!ret && !(flags & MAP_FIXED)) + ret = vmm_allocfrom(USER_ALLOC_START, mmuflags, len); + + if(!ret){ + retv.errno = ENOMEM; + if(flags & MAP_ANON) + memset(ret, 0, plen); + } + else + retv.ret = ret; + + return retv; + +}