diff --git a/.gitignore b/.gitignore index bd25471..3068063 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ serialOut disk.img kernel -kernel.sym +kernel.debug userspace/user docs/*.html diff --git a/arch/x86/exception.c b/arch/x86/exception.c index 68e5cee..512f8c5 100644 --- a/arch/x86/exception.c +++ b/arch/x86/exception.c @@ -45,9 +45,9 @@ int exceptionSetRoutine(int exception, exception_handler handler) void print_handler(struct cpu_state *frame, ulong intr) { - int intNbInt = intr; - VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "EXCEPTION %d", intNbInt); + VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "EXCEPTION %lu", intr); printf("Exception %lu at 0x%p\n", intr, (void *)cpu_context_get_PC(frame)); + asm("hlt"); } @@ -55,20 +55,43 @@ void pagefault_handler(struct cpu_state *frame, ulong intr) { struct thread *current = getCurrentThread(); - struct uAddrSpace *as = processGetAddrSpace(current->process); - vaddr_t faultAddr = cpu_context_get_EX_faulting_vaddr(frame); + assert(frame == current->cpuState); + if (cpu_context_is_in_user_mode(current->cpuState)) { - if(!uAddrSpaceCheckNAlloc(as, faultAddr)) - return; + struct uAddrSpace *as = processGetAddrSpace(current->process); + vaddr_t faultAddr = cpu_context_get_EX_faulting_vaddr(frame); + int needMMUSetup = + (uAddrSpaceGetMMUContext(as) != getCurrentThread()->squattedContext); - printf("page fault while in thread [%s] at 0x%p when trying to access 0x%p err_code 0x%x\n", current->name, - (void *)cpu_context_get_PC(frame), (void *)faultAddr, cpu_context_get_EX_err(frame)); - if (cpu_context_is_in_user_mode(frame)) { + if (needMMUSetup) { + threadChangeCurrentContext(uAddrSpaceGetMMUContext(as)); + } + + 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) + goto release_context; + + printf( + "page fault while in thread [%s] at 0x%p when trying to access 0x%p err_code 0x%x ressource ret %d\n", + current->name, (void *)cpu_context_get_PC(frame), (void *)faultAddr, error_code, + ret); printf("Killing User Thread\n"); threadExit(); + + release_context: + if (needMMUSetup) + threadChangeCurrentContext(NULL); + + return; } + VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr); - (void)intr; for (;;) continue; } @@ -79,5 +102,6 @@ int exceptionSetup() exceptionSetRoutine(i, print_handler); } exceptionSetRoutine(EXCEPTION_PAGE_FAULT, pagefault_handler); + return 0; } diff --git a/arch/x86/exception_wrappers.S b/arch/x86/exception_wrappers.S index 52f6a09..c490e13 100644 --- a/arch/x86/exception_wrappers.S +++ b/arch/x86/exception_wrappers.S @@ -165,7 +165,7 @@ iret .endm -/*List of exception w or w/o err codehttps://wiki.osdev.org/Exceptions*/ +/* List of exception w or w/o err code https://wiki.osdev.org/Exceptions */ .irp exception_id, 8, 10, 11, 12, 13, 14, 17, 30 exception_mac_with_errcode exception_id .endr diff --git a/arch/x86/paging.h b/arch/x86/paging.h index 360d052..e18d1f0 100644 --- a/arch/x86/paging.h +++ b/arch/x86/paging.h @@ -17,6 +17,7 @@ #define PAGING_MEM_USER (1U << 0) #define PAGING_MEM_READ (1U << 1) #define PAGING_MEM_WRITE (1U << 2) +#define PAGING_MEM_EXEC (1U << 3) int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr); diff --git a/core/alloc.h b/core/alloc.h index 174a325..bba2533 100644 --- a/core/alloc.h +++ b/core/alloc.h @@ -3,11 +3,17 @@ #include "stddef.h" #include "types.h" +/** Arbitrary size allocator **/ + /* * Initialize malloc system */ int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr); +/* + * Add Slice for some simple/commun size + */ + int allocPopulate(); /* * Allow malloc to allocate elements of this precise size. diff --git a/core/allocArea.c b/core/allocArea.c index 9fa4eee..d11a6cf 100644 --- a/core/allocArea.c +++ b/core/allocArea.c @@ -7,6 +7,14 @@ #include "mem.h" #include "stdarg.h" +struct memArea { + vaddr_t startAddr; + uint nbPages; + + struct memArea *next; + struct memArea *prev; +}; + static struct memArea *freeArea; static struct memArea *usedArea; diff --git a/core/allocArea.h b/core/allocArea.h index 4ceda6e..e65ab48 100644 --- a/core/allocArea.h +++ b/core/allocArea.h @@ -9,19 +9,29 @@ #define AREA_PHY_MAP (1<<0) #define AREA_MEM_TOP PAGING_MIRROR_VADDR -struct memArea { - vaddr_t startAddr; - uint nbPages; - - struct memArea *next; - struct memArea *prev; -}; - +/** + * Initialize the Area subsystem + **/ void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stack_bottom, vaddr_t stack_top); + +/** + * Request a virtual memory area of @param nbPages + **/ vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags); -// Remove an area from the free ones but do not add it into used ones. -// This area should be latter added woth areaAdd. -// Used by malloc to avoid recursivity issue -vaddr_t areaBook(unsigned int nbPages, uint32_t flags); + +/** + * Free a virtual area + **/ int areaFree(vaddr_t addr); + +/** + * Remove an area from the "free" ones but do not add it into used ones. + * This area should be latter added with areaAdd. + * Used by malloc to avoid recursivity issue + **/ +vaddr_t areaBook(unsigned int nbPages, uint32_t flags); + +/** + * Declare a virtual region to be managed by the subsytem + */ int areaAdd(vaddr_t begin, vaddr_t end, int isFree); diff --git a/core/elf.c b/core/elf.c index f87572d..8f4ccc1 100644 --- a/core/elf.c +++ b/core/elf.c @@ -5,6 +5,7 @@ #include "paging.h" #include "thread.h" #include "types.h" +#include "zero.h" /** * Make sure the program is in a valid ELF format, map it into memory, @@ -13,12 +14,11 @@ * @return 0 when the program is not a valid ELF */ -uaddr_t loadElfProg(const char *prog, struct process * proc) +uaddr_t loadElfProg(const char *prog, struct process *proc) { int i; uaddr_t lastUserAddr = 0; - - + struct uAddrSpace *as = processGetAddrSpace(proc); /* e_ident value */ #define ELFMAG0 0x7f @@ -138,6 +138,11 @@ uaddr_t loadElfProg(const char *prog, struct process * proc) unrefPhyPage(ppage); } + // Hack: Even if already allocated mark the adresse space as managed by a ressource + // So this address space is not used by another ressource. + uaddr = elf_phdrs[i].p_vaddr; + zeroMmap(as, &uaddr, elf_phdrs[i].p_memsz, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ, 0); + /* Copy segment into memory */ memcpy((void *)elf_phdrs[i].p_vaddr, (void *)(prog + elf_phdrs[i].p_offset), elf_phdrs[i].p_filesz); diff --git a/core/main.c b/core/main.c index e52114b..2db8bec 100644 --- a/core/main.c +++ b/core/main.c @@ -28,6 +28,7 @@ #include "time.h" #include "types.h" #include "vga.h" +#include "zero.h" #define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit))) @@ -83,27 +84,28 @@ void loadUserSpace() return; } - struct process *proc = processCreate("UserSpace"); + { + struct process *proc = processCreate("init"); + struct uAddrSpace *as = processGetAddrSpace(proc); - threadChangeCurrentContext(processGetMMUContext(proc)); - uaddr_t prog = loadElfProg(buf + FILE_HEADER_SIZE, proc); - if (prog == (uaddr_t)NULL) { - free(buf); - return; + threadChangeCurrentContext(processGetMMUContext(proc)); + uaddr_t prog = loadElfProg(buf + FILE_HEADER_SIZE, proc); + if (prog == (uaddr_t)NULL) { + free(buf); + return; + } + + // Alloc user stack + size_t stackSize = PAGE_SIZE * 2; + uaddr_t stack = PAGING_TOP_USER_ADDRESS - stackSize + 1; + zeroMmap(as, &stack, stackSize, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ, + 0); + + threadCreateUser("init", proc, prog, 0, 0, stack + stackSize - 4); + processUnref(proc); + threadChangeCurrentContext(NULL); } - // Alloc user stack - uaddr_t stackTop = 0xfffffffc; - uaddr_t stackBottom = ALIGN_DOWN(stackTop, PAGE_SIZE); - paddr_t stackPhy = allocPhyPage(1); - assert(pageMap(stackBottom, stackPhy, - PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); - unrefPhyPage(stackPhy); - - threadCreateUser("UserProg", proc, prog, 0, 0, stackTop); - processUnref(proc); - threadChangeCurrentContext(NULL); - free(buf); } diff --git a/core/mem.h b/core/mem.h index bead0d8..6a226fc 100644 --- a/core/mem.h +++ b/core/mem.h @@ -2,7 +2,8 @@ #include "stdint.h" #include "types.h" -/** Physical Page related function +/** + * Physical Page management */ #define PAGE_SHIFT 12U @@ -20,10 +21,33 @@ struct phyMemDesc { struct phyMemDesc *next, *prev; }; +/** + * Initi Physical Page management subsystem + **/ int memSetup(paddr_t upperMem, paddr_t * firstUsed, paddr_t *lastUsed); + +/** + * Declare a physical region to be managed by the physica page subsystem + **/ int memAddBank(paddr_t bottomMem, paddr_t topMem, int isFree); + +/** + * Request @params nbPage free physical pages + **/ paddr_t allocPhyPage(uint nbPage); + +/** + * Decrement the nb of user of a given physical page + **/ int unrefPhyPage(paddr_t addr); + +/** + * Increment the nb of user of a given physical page + **/ int refPhyPage(paddr_t addr); + +/** + * Return the number of physical allocated pages + **/ unsigned long getNbAllocatedPage(void); void memGetStat(uint *free, uint *used); diff --git a/core/syscall.c b/core/syscall.c index b836c67..a760424 100644 --- a/core/syscall.c +++ b/core/syscall.c @@ -5,7 +5,10 @@ #include "stdarg.h" #include "thread.h" #include "types.h" +#include "uaccess.h" #include "uaddrspace.h" +#include "zero.h" + int syscallExecute(int syscallId, const struct cpu_state *userCtx) { @@ -55,6 +58,58 @@ int syscallExecute(int syscallId, const struct cpu_state *userCtx) threadChangeCurrentContext(NULL); break; } + case SYSCALL_ID_MMAP: { + struct uAddrSpace *as; + uaddr_t uaddr; + uaddr_t uaddr_ptr; + size_t size; + uint32_t rights; + uint32_t flags; + uaddr_t userPath; + + char path[256]; + + as = processGetAddrSpace(getCurrentThread()->process); + ret = syscallGet5args(userCtx, (unsigned int *)&uaddr_ptr, (unsigned int *)&size, + (unsigned int *)&rights, (unsigned int *)&flags, + (unsigned int *)&userPath); + + if (ret) + break; + + if (memcpyFromUser((vaddr_t)&uaddr, uaddr_ptr, sizeof(uaddr)) != sizeof(uaddr)) { + ret = -EFAULT; + break; + } + + strzcpyFromUser((vaddr_t *)path, (uaddr_t *)userPath, sizeof(path)); + + printf("Trying mmap for device %s at %lu\n", path, (vaddr_t)uaddr); + if (strcmp(path, "/dev/zero") == 0) { + ret = zeroMmap(as, &uaddr, size, rights, flags); + } + if (!ret) { + if (memcpyToUser(uaddr_ptr, (vaddr_t)&uaddr, sizeof(uaddr)) != sizeof(uaddr)) { + ret = -EFAULT; + break; + } + } + break; + } + case SYSCALL_ID_MUNMAP: { + struct uAddrSpace *as; + uaddr_t uaddr; + size_t size; + + ret = syscallGet2args(userCtx, (unsigned int *)&uaddr, (unsigned int *)&size); + + if (ret) + break; + + as = processGetAddrSpace(getCurrentThread()->process); + ret = uAddrSpaceUnmap(as, uaddr, size); + break; + } default: printf("Unknon syscall id %d\n", syscallId); ret = -ENOENT; diff --git a/core/syscall.h b/core/syscall.h index 099e51a..444789b 100644 --- a/core/syscall.h +++ b/core/syscall.h @@ -1,6 +1,6 @@ #pragma once #ifdef __KERNEL__ -#include "cpu_context.h" +#include "cpu_context.h" #endif #define SYSCALL_ID_EXIT 1 @@ -8,7 +8,9 @@ #define SYSCALL_ID_PUTC 3 #define SYSCALL_ID_READ 4 #define SYSCALL_ID_TEST 5 -#define SYSCALL_ID_BRK 6 +#define SYSCALL_ID_BRK 6 +#define SYSCALL_ID_MMAP 7 +#define SYSCALL_ID_MUNMAP 8 #ifdef __KERNEL__ int syscallExecute(int syscallId, const struct cpu_state *user_ctx); diff --git a/core/uaccess.c b/core/uaccess.c index 852cc5e..1548faf 100644 --- a/core/uaccess.c +++ b/core/uaccess.c @@ -1,10 +1,12 @@ +#include "uaccess.h" #include "assert.h" #include "errno.h" +#include "minmax.h" #include "mmuContext.h" #include "paging.h" #include "process.h" #include "thread.h" -#include "uaccess.h" +#include "types.h" static int bindtoUserContext() { @@ -42,6 +44,17 @@ static int memcpyUserMemNoCheck(vaddr_t dest, vaddr_t src, size_t size) return size; } +int memcpyToUser(uaddr_t to, vaddr_t from, size_t size) +{ + + if ((uint)to < PAGING_BASE_USER_ADDRESS) + return -EPERM; + if ((uint)to > PAGING_TOP_USER_ADDRESS - size) + return -EPERM; + + return memcpyUserMemNoCheck(to, from, size); +} + int memcpyFromUser(vaddr_t to, uaddr_t from, size_t size) { @@ -52,3 +65,37 @@ int memcpyFromUser(vaddr_t to, uaddr_t from, size_t size) return memcpyUserMemNoCheck(to, from, size); } + +static int strzcpyFromUserNoCheck(char *to, char *from, size_t size) +{ + int ret; + + ret = bindtoUserContext(); + if (ret != 0) + return ret; + + for (unsigned int i = 0; i < size; i++) { + to[i] = from[i]; + if (from[i] == '\0') + break; + } + + if (size > 0) + to[size - 1] = '\0'; + + ret = unbindUserContext(); + if (ret != 0) + return ret; + + return size; +} + +int strzcpyFromUser(vaddr_t *to, uaddr_t *from, size_t size) +{ + if ((uint)from < PAGING_BASE_USER_ADDRESS) + return -EPERM; + if ((uint)from > PAGING_TOP_USER_ADDRESS - size) + return -EPERM; + + return strzcpyFromUserNoCheck((char *)to, (char *)from, size); +} diff --git a/core/uaccess.h b/core/uaccess.h index 465e268..03d7685 100644 --- a/core/uaccess.h +++ b/core/uaccess.h @@ -1,5 +1,8 @@ #pragma once #include "types.h" +#include "stddef.h" #include "stdarg.h" int memcpyFromUser(vaddr_t to, uaddr_t from, size_t size); +int memcpyToUser(uaddr_t to, vaddr_t from, size_t size); +int strzcpyFromUser(vaddr_t *to, uaddr_t *from, size_t size); diff --git a/core/uaddrspace.c b/core/uaddrspace.c index 6f4a4ee..aefd404 100644 --- a/core/uaddrspace.c +++ b/core/uaddrspace.c @@ -1,5 +1,6 @@ #include "uaddrspace.h" #include "alloc.h" +#include "errno.h" #include "kernel.h" #include "klibc.h" #include "list.h" @@ -14,18 +15,6 @@ #include #include -struct uAddrVirtualReg { - uaddr_t addr; - size_t size; - int right; // PAGING_MEM_* - uint32_t offset; // in the mappedRessource - uint flags; - - struct mappedRessource *res; - struct uAddrVirtualReg *nextInAddrSpace, *prevInAddrSpace; - struct uAddrVirtualReg *nextInMappedRes, *prevInMappedRes; -}; - struct uAddrSpace { struct process *process; // The process that is represented by this AS struct mmu_context *ctx; // The corresponding MMU configuration @@ -33,22 +22,85 @@ struct uAddrSpace { struct uAddrVirtualReg *listVirtualReg; // List of Virtual Region used by this process uaddr_t heapStart; // Start of the Head - size_t heapSize; // Hep size -> modified by brk() + size_t heapSize; // Heap size -> modified by brk() }; -struct mappedRessourceOps { - int (*open)(struct uAddrVirtualReg *vreg); - int (*close)(struct uAddrVirtualReg *vreg); - int (*unmap)(struct uAddrVirtualReg *vregi, uaddr_t addr, size_t size); - int (*nopage)(struct uAddrVirtualReg *vregi, uaddr_t addr, - int right); // Called by the pageflt handler when the page is missing -}; +static int hasOverlap(uaddr_t addr1, size_t size1, uaddr_t addr2, size_t size2) +{ + return max(addr1, addr2) < min(addr1 + size1, addr2 + size2); +} -struct mappedRessource { - int right; // PAGING_MEM_* - struct mappedRessourceOps *ops; - struct uAddrVirtualReg *listVirtualReg; -}; +static struct uAddrVirtualReg *findVirtualRegionFromAddr(struct uAddrSpace *as, uaddr_t uaddr, size_t size) +{ + struct uAddrVirtualReg *reg; + int idx; + + list_foreach_named(as->listVirtualReg, reg, idx, prevInAddrSpace, nextInAddrSpace) + { + if (hasOverlap(reg->addr, reg->size, uaddr, size)) + return reg; + } + return NULL; +} + +static struct uAddrVirtualReg *findVirtualRegionBeforeAddr(struct uAddrSpace *as, + uaddr_t uaddr) +{ + struct uAddrVirtualReg *reg, *prev = NULL; + int idx; + + list_foreach_named(as->listVirtualReg, reg, idx, prevInAddrSpace, nextInAddrSpace) + { + if (uaddr < reg->addr) + break; + prev = reg; + } + return prev; +} + +/** + * Find a address not alreay used in the virtual region of the AS. + * This address will be equals to uaddr if possible and could take size + */ +static uaddr_t findFreeAddrInVirtualRegion(struct uAddrSpace *as, uaddr_t uaddr, size_t size) +{ + struct uAddrVirtualReg *reg, *regNext, *regOver; + + if (uaddr < PAGING_BASE_USER_ADDRESS) + uaddr = PAGING_BASE_USER_ADDRESS; + if(uaddr > PAGING_TOP_USER_ADDRESS - size) + uaddr = PAGING_TOP_USER_ADDRESS - size; + + reg = findVirtualRegionFromAddr(as, uaddr, size); + + if (!reg) + return uaddr; + + int idx; + + regOver = reg; + + //Find last region that overlap + list_foreach_named(reg->nextInAddrSpace, regNext, idx, prevInAddrSpace, nextInAddrSpace){ + if(!hasOverlap(uaddr, size, regNext->addr, regNext->size)) + break; + regOver = regNext; + } + + uaddr = regOver->addr + regOver->size; + list_foreach_named(regOver->nextInAddrSpace, regNext, idx, prevInAddrSpace, + nextInAddrSpace) + { + if (!hasOverlap(uaddr, size, regNext->addr, regNext->size) && + uaddr <= (PAGING_TOP_USER_ADDRESS - size)) { + return uaddr; + } + if (reg == regNext) // Already checked region + break; + uaddr = regNext->addr + regNext->size; + } + return (uaddr_t)NULL; +} struct uAddrSpace *uAddrSpaceCreate(struct process *proc) { @@ -76,16 +128,119 @@ int uAddrSpaceDelete(struct uAddrSpace *addr) struct uAddrVirtualReg *reg; list_collapse_named(addr->listVirtualReg, reg, nextInAddrSpace, prevInAddrSpace) { - // TODO Implement me with real ressources - assertmsg(reg->res == NULL, "Unsupported mapper ressource"); - // This is memory allocated for the heap just unmap it to free it - pr_devel("Freeing heap 0x%lx for process %s\n", reg->addr, processGetName(addr->process)); - pageUnmap(reg->addr); - free(reg); + if (reg->res == NULL) { + // This is memory allocated for the heap just unmap it to free it + pr_devel("Freeing heap 0x%lx for process %s\n", reg->addr, + processGetName(addr->process)); + pageUnmap(reg->addr); + free(reg); + } else { + if(reg->res->ops){ + if(reg->res->ops->unmap) + reg->res->ops->unmap(reg, reg->addr, reg->size); + if(reg->res->ops->close) + reg->res->ops->close(reg); + } + free(reg); + } } return mmuContextUnref(addr->ctx); } +/** + * Find the associated uAddrVirtualReg associated to the unmap space and free them; + */ +int uAddrSpaceUnmap(struct uAddrSpace *as, uaddr_t uaddr, size_t size) +{ + if (uaddr < PAGING_BASE_USER_ADDRESS || uaddr > PAGING_TOP_USER_ADDRESS - size) + return -EINVAL; + if (!IS_ALIGNED(uaddr, PAGE_SIZE) || size <= 0) + return -EINVAL; + if(!as) + return -EINVAL; + + size = ALIGN(size, PAGE_SIZE); + struct uAddrVirtualReg *reg = as->listVirtualReg; + struct uAddrVirtualReg *lastReg = reg ? reg->prevInAddrSpace : NULL; + + while (reg != NULL) { + if (reg->addr > uaddr + size) + break; + struct uAddrVirtualReg *next = reg->nextInAddrSpace; + // The Virtual Region is completly inside the unmaped space + if (reg->addr >= uaddr && (reg->addr + reg->size <= uaddr + size)) { + list_delete_named(as->listVirtualReg, reg, prevInAddrSpace, nextInAddrSpace); + list_delete_named(reg->res->listVirtualReg, reg, prevInMappedRes, nextInMappedRes); + + if (reg->res->ops && reg->res->ops->unmap) + reg->res->ops->unmap(reg, uaddr, size); + + if (reg->res->ops && reg->res->ops->close) + reg->res->ops->close(reg); + + if (reg == next)//singleton + next = NULL; + + free(reg); + // Unmaped space is inside and smaller than the VR + // VR should be splitted + } else if (reg->addr > uaddr && (reg->addr + reg->size < uaddr + size)) { + struct uAddrVirtualReg *new = + (struct uAddrVirtualReg *)zalloc(sizeof(struct uAddrSpace)); + if (!new) + return -ENOMEM; + + new->addr = uaddr + size; + new->size = reg->addr + reg->size - (uaddr + size); + new->right = reg->right; + new->offset = uaddr + size - reg->addr; + + reg->size = uaddr - reg->addr; + + list_insert_after_named(as->listVirtualReg, reg, new, prevInAddrSpace, + nextInAddrSpace); + list_insert_after_named(reg->res->listVirtualReg, reg, new, prevInMappedRes, + nextInMappedRes); + if (reg->res->ops && reg->res->ops->unmap) + reg->res->ops->unmap(reg, uaddr, size); + if (new->res->ops && new->res->ops->open) + new->res->ops->open(new); + break; + // Only affect the beginning + } else if (uaddr <= reg->addr && uaddr + size > reg->addr) { + size_t offset = uaddr + size - reg->addr; + reg->size -= offset; + reg->offset += offset; + reg->addr += offset; + if (reg->res->ops && reg->res->ops->unmap) + reg->res->ops->unmap(reg, uaddr, size); + break; + // Only affect the end + } else if (uaddr > reg->addr && uaddr + size > reg->addr + reg->size) { + size_t unmapSize = reg->addr + reg->size - uaddr; + reg->size = uaddr - reg->addr; + + if (reg->res->ops && reg->res->ops->unmap) + reg->res->ops->unmap(reg, uaddr, unmapSize); + } + reg = next; + if (reg == lastReg) + break; + } + + int needMMUSetup = as->ctx != getCurrentThread()->squattedContext; + + if (needMMUSetup) + threadChangeCurrentContext(as->ctx); + for (vaddr_t addr = uaddr; addr < uaddr + size; addr += PAGE_SIZE) { + pageUnmap(addr); + } + if (needMMUSetup) + threadChangeCurrentContext(NULL); + + return 0; +} + struct mmu_context *uAddrSpaceGetMMUContext(struct uAddrSpace *addr) { return addr->ctx; @@ -114,7 +269,7 @@ uaddr_t sysBrk(struct uAddrSpace *as, uaddr_t newHeapTop) incSize = ALIGN(newHeapTop - (as->heapStart + as->heapSize), PAGE_SIZE); if (incSize < 0){ - //TODO how to free allocated page by uAddrSpaceCheckNAlloc + //TODO how to free allocated page by uAddrSpaceHeapCheckNAlloc return as->heapStart + as->heapSize; } @@ -123,12 +278,14 @@ uaddr_t sysBrk(struct uAddrSpace *as, uaddr_t newHeapTop) return as->heapStart + as->heapSize; } -int uAddrSpaceCheckNAlloc(struct uAddrSpace *as, vaddr_t addr) +int uAddrSpaceHeapCheckNAlloc(struct uAddrSpace *as, vaddr_t addr) { struct uAddrVirtualReg *newReg; int right = PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ; - pr_devel("Checking 0x%lx inside 0x%lx and 0x%lx\n", addr, as->heapStart, as->heapStart +as->heapSize); + pr_devel("Heap check: 0x%lx inside 0x%lx and 0x%lx\n", addr, as->heapStart, + as->heapStart + as->heapSize); + if (addr < as->heapStart || addr >= as->heapStart + as->heapSize) { return -1; } @@ -159,3 +316,106 @@ free_ppage: unrefPhyPage(ppage); return -1; } + +int uAddrSpaceMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, + uint32_t flags, struct mappedRessource *res, uint32_t offset ) +{ + int ret = 0; + uaddr_t hint_uaddr = *uaddr; + + if (res == NULL || res->ops == NULL || res->ops->nopage == NULL) + return -ENOENT; + if (!IS_ALIGNED(hint_uaddr, PAGE_SIZE) || size <= 0) + return -EINVAL; + if (flags & UA_MAP_SHARED) { + if (((rights & PAGING_MEM_READ) && !(res->allowedRight & PAGING_MEM_READ)) || + ((rights & PAGING_MEM_WRITE) && !(res->allowedRight & PAGING_MEM_WRITE)) || + ((rights & PAGING_MEM_EXEC) && !(res->allowedRight & PAGING_MEM_EXEC))) + return -EPERM; + } + + size = ALIGN(size, PAGE_SIZE); + struct uAddrVirtualReg *reg = + (struct uAddrVirtualReg *)malloc(sizeof(struct uAddrVirtualReg)); + if (!reg) + return -ENOMEM; + + if (flags & UA_MAP_FIXED) { + if (hint_uaddr < PAGING_BASE_USER_ADDRESS || + hint_uaddr > PAGING_TOP_USER_ADDRESS - size) { + ret = -EINVAL; + goto free_reg; + } + ret = uAddrSpaceUnmap(as, hint_uaddr, size); + if (ret) + goto free_reg; + + } else { + hint_uaddr = findFreeAddrInVirtualRegion(as, hint_uaddr, size); + if (!hint_uaddr) { + ret = -ENOMEM; + goto free_reg; + } + } + + reg->addr = hint_uaddr; + reg->size = size; + reg->right = rights; + reg->res = res; + reg->offset = offset; + // TODO merge it with prev/next one + + // keep the AddrSpace list sorted + struct uAddrVirtualReg *prev = findVirtualRegionBeforeAddr(as, hint_uaddr); + if (prev) + list_insert_after_named(as->listVirtualReg, prev, reg, prevInAddrSpace, + nextInAddrSpace); + else + list_add_tail_named(as->listVirtualReg, reg, prevInAddrSpace, nextInAddrSpace); + + list_add_tail_named(reg->res->listVirtualReg, reg, prevInMappedRes, nextInMappedRes); + if (res->onResMapped) { + int cbret = res->onResMapped(reg); + if (cbret) { + pr_devel("Call back failed on ressource mmaped\n"); + ret = uAddrSpaceUnmap(as, reg->addr, reg->size); + } + } + + if (res->ops->open) + res->ops->open(reg); + + *uaddr = hint_uaddr; + + return ret; +free_reg: + free(reg); + return ret; +} + +/** + * Check if @param faultAddr could be managed by the uAddrVirtualReg from @param as + **/ +int uAddrSpaceSolvePageFault(struct uAddrSpace *as, vaddr_t faultAddr, int isWriteAccess) +{ + struct uAddrVirtualReg *reg; + int rights = PAGING_MEM_READ | PAGING_MEM_USER; + + reg = findVirtualRegionFromAddr(as, faultAddr, 1); + + if (reg == NULL) + return -EFAULT; + + pr_devel("Virtual Region for pageflt found\n"); + + if (isWriteAccess && !(reg->right & PAGING_MEM_WRITE)) + return -EACCES; + + if (isWriteAccess || (reg->right & PAGING_MEM_WRITE)) + rights |= PAGING_MEM_WRITE; + + if (reg->res->ops->nopage(reg, faultAddr, rights)) + return -EFAULT; + + return 0; +} diff --git a/core/uaddrspace.h b/core/uaddrspace.h index 37938de..9cf8cac 100644 --- a/core/uaddrspace.h +++ b/core/uaddrspace.h @@ -7,9 +7,47 @@ struct uAddrSpace; struct uAddrVirtualReg; -struct uAddrSpace * uAddrSpaceCreate(struct process *proc); +#define UA_MAP_SHARED (1 << 0) +#define UA_MAP_PRIVATE (1 << 1) +#define UA_MAP_FIXED (1 << 2) + +// TODO : move this struct to .c and add accessors +struct uAddrVirtualReg { + uaddr_t addr; + size_t size; + int right; // PAGING_MEM_* + uint32_t offset; // in the mappedRessource + uint flags; + + struct mappedRessource *res; + struct uAddrVirtualReg *nextInAddrSpace, *prevInAddrSpace; + struct uAddrVirtualReg *nextInMappedRes, *prevInMappedRes; +}; + +struct mappedRessourceOps { + int (*open)(struct uAddrVirtualReg *vreg); + int (*close)(struct uAddrVirtualReg *vreg); + int (*unmap)(struct uAddrVirtualReg *vreg, uaddr_t addr, size_t size); + int (*nopage)(struct uAddrVirtualReg *vreg, uaddr_t addr, + int right); // Called by the pageflt handler when the page is missing +}; + +struct mappedRessource { + int allowedRight; // PAGING_MEM_* + struct mappedRessourceOps *ops; + struct uAddrVirtualReg *listVirtualReg; + void *customData; + int (*onResMapped)(struct uAddrVirtualReg *reg); // Callabck for when the ressourced get mapped +}; + +struct uAddrSpace *uAddrSpaceCreate(struct process *proc); int uAddrSpaceDelete(struct uAddrSpace *addr); -struct mmu_context * uAddrSpaceGetMMUContext(struct uAddrSpace *addr); +struct mmu_context *uAddrSpaceGetMMUContext(struct uAddrSpace *addr); int uAddrSpaceSetHeap(struct uAddrSpace *as, uaddr_t addr, size_t size); -int uAddrSpaceCheckNAlloc(struct uAddrSpace *as, vaddr_t addr); +int uAddrSpaceHeapCheckNAlloc(struct uAddrSpace *as, vaddr_t addr); uaddr_t sysBrk(struct uAddrSpace *as, uaddr_t newHeapTop); + +int uAddrSpaceMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, + uint32_t flags, struct mappedRessource *res, uint32_t offset); +int uAddrSpaceUnmap(struct uAddrSpace *as, uaddr_t uaddr, size_t size); +int uAddrSpaceSolvePageFault(struct uAddrSpace *as, vaddr_t faultAddr, int isWriteAccess); diff --git a/custom_gdb_extension.py b/custom_gdb_extension.py index dd405b2..6c0bfd3 100644 --- a/custom_gdb_extension.py +++ b/custom_gdb_extension.py @@ -184,18 +184,19 @@ class ListDumpCmd(gdb.Command): "list_dump", gdb.COMMAND_USER ) - def _print_list(self, val): + def _print_list(self, val, next_name): """Walk through the linked list. - We will simply follow the 'next' pointers until we encounter the HEAD again """ idx = 0 head = val thread_ptr = val result = "" + while thread_ptr != 0 and (idx == 0 or thread_ptr != head): - result += gdb.execute('p *({}){}'.format(str(thread_ptr.type),thread_ptr), to_string=True) - thread_ptr = thread_ptr["next"] + result += gdb.execute('p *({}){}'.format(str(thread_ptr.type), + thread_ptr), to_string=True) + thread_ptr = thread_ptr[next_name] idx += 1 result = ("Found a Linked List with %d items:" % idx) + "\n" + result return result @@ -209,17 +210,21 @@ class ListDumpCmd(gdb.Command): # We can pass args here and use Python CLI utilities like argparse # to do argument parsing print("Args Passed: %s" % args) - if args: - ptr_val = gdb.parse_and_eval(args) - else: - ptr_val = gdb.parse_and_eval("currentThread") + thread_name = "currentThread" + next_name = "next" + if(args): + args_arr = args.split() + thread_name = args_arr[0] + if(len(args_arr) >= 2): + next_name = args_arr[1] + ptr_val = gdb.parse_and_eval(thread_name) try: - ptr_val["next"] + ptr_val[next_name] except: - print("Expected pointer argument with a next field") + print("Expected pointer argument with a %s field" % next_name) return - print(self._print_list(ptr_val)) + print(self._print_list(ptr_val, next_name)) register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True) diff --git a/disk.sfdisk b/disk.sfdisk index 2004b3d..a1ecf61 100644 --- a/disk.sfdisk +++ b/disk.sfdisk @@ -2,7 +2,6 @@ label: dos label-id: 0x9ec19bcc device: disk.img unit: sectors -sector-size: 512 disk.img1 : start= 2048, size= 32768, type=6, bootable disk.img2 : start= 34816, size= 30720, type=83 diff --git a/drivers/zero.c b/drivers/zero.c new file mode 100644 index 0000000..c416c8f --- /dev/null +++ b/drivers/zero.c @@ -0,0 +1,83 @@ +#include "zero.h" + +#include "alloc.h" +#include "kernel.h" +#include "klibc.h" + +struct zeroMappedEntry { + int refCnt; +}; + +static int zeroOpen(struct uAddrVirtualReg *vreg) +{ + struct zeroMappedEntry *ent = (struct zeroMappedEntry *)vreg->res->customData; + ent->refCnt++; + return 0; +} + +static int zeroClose(struct uAddrVirtualReg *vreg) +{ + struct zeroMappedEntry *ent = (struct zeroMappedEntry *)vreg->res->customData; + ent->refCnt--; + if (ent->refCnt == 0) { + free(vreg->res->customData); + free(vreg->res); + } + return 0; +} + +static int zeroNoPage(struct uAddrVirtualReg *vreg, uaddr_t addr, int right) +{ + (void)vreg; + + int ret = 0; + paddr_t ppage = allocPhyPage(1); + ret = pageMap(ALIGN_DOWN(addr, PAGE_SIZE), ppage, right | PAGING_MEM_USER) ; + + unrefPhyPage(ppage); + if (ret) { + return ret; + } + memset((void *)ALIGN_DOWN(addr, PAGE_SIZE), 0, PAGE_SIZE); + return ret; +} + +static struct mappedRessourceOps zeroOps = { + .open = zeroOpen, + .close = zeroClose, + .unmap = NULL, + .nopage = zeroNoPage, +}; + +int zeroOnMapped(struct uAddrVirtualReg *vreg) +{ + (void)vreg; + + printf("ZERO MAPPED !!\n"); + + return 0; +} + +int zeroMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, + uint32_t flags) +{ + int ret = 0; + struct mappedRessource *res = + (struct mappedRessource *)zalloc(sizeof(struct mappedRessource)); + struct zeroMappedEntry *cust = + (struct zeroMappedEntry *)zalloc(sizeof(struct zeroMappedEntry)); + + res->allowedRight = PAGING_MEM_READ | PAGING_MEM_WRITE | PAGING_MEM_EXEC | PAGING_MEM_USER; + res->ops = &zeroOps; + res->customData = cust; + res->onResMapped = zeroOnMapped; + + ret = uAddrSpaceMmap(as, uaddr, size, rights, flags, res, 0); + + if (ret) { + free(res); + free(cust); + } + + return ret; +} diff --git a/drivers/zero.h b/drivers/zero.h new file mode 100644 index 0000000..3fbd6fd --- /dev/null +++ b/drivers/zero.h @@ -0,0 +1,11 @@ +#pragma once + + +#include "types.h" +#include "uaddrspace.h" + +#include +#include +int zeroSetup(); + +int zeroMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, uint32_t flags); diff --git a/userspace/kernel/syscall.h b/userspace/kernel/syscall.h index 099e51a..444789b 100644 --- a/userspace/kernel/syscall.h +++ b/userspace/kernel/syscall.h @@ -1,6 +1,6 @@ #pragma once #ifdef __KERNEL__ -#include "cpu_context.h" +#include "cpu_context.h" #endif #define SYSCALL_ID_EXIT 1 @@ -8,7 +8,9 @@ #define SYSCALL_ID_PUTC 3 #define SYSCALL_ID_READ 4 #define SYSCALL_ID_TEST 5 -#define SYSCALL_ID_BRK 6 +#define SYSCALL_ID_BRK 6 +#define SYSCALL_ID_MMAP 7 +#define SYSCALL_ID_MUNMAP 8 #ifdef __KERNEL__ int syscallExecute(int syscallId, const struct cpu_state *user_ctx); diff --git a/userspace/libc.c b/userspace/libc.c index 991b192..eec4ccc 100644 --- a/userspace/libc.c +++ b/userspace/libc.c @@ -1,9 +1,12 @@ #include "libc.h" +#include "errno.h" #include "list.h" #include "minmax.h" #include "swintr.h" +#include "sys/mman.h" #include "syscall.h" +int errno = 0; int memcmp(const void *aptr, const void *bptr, size_t size) { const unsigned char *a = (const unsigned char *)aptr; @@ -725,3 +728,20 @@ void *realloc(void *ptr, size_t size) return new_ptr; } +void *mmap(void *addr, size_t len, int prot, int flags, char *path) { + + int ret = syscall5(SYSCALL_ID_MMAP, (unsigned int)&addr, len, prot, flags, (unsigned int)path); + if(!ret) + return addr; + errno = ret; + + return MAP_FAILED; +} + +int munmap(void *addr, size_t len) +{ + if (len == 0) + return -EINVAL; + + return syscall2(SYSCALL_ID_MUNMAP, (unsigned int)addr, len); +} diff --git a/userspace/libc.h b/userspace/libc.h index eb4c313..378d023 100644 --- a/userspace/libc.h +++ b/userspace/libc.h @@ -36,6 +36,8 @@ int putc(const int c); int vsnprintf(char *str, size_t size, const char *format, va_list ap) __attribute__ ((__format__ (printf, 3, 0))); int vprintf(const char *format, va_list ap) __attribute__ ((__format__ (printf, 1, 0))); int printf(const char *format, ...) __attribute__ ((__format__ (printf, 1, 2))); +void *mmap(void *addr, size_t len, int prot, int flags, char *path); +int munmap(void *addr, size_t len); int asprintf(char **strp, const char *fmt, ...) __attribute__ ((__format__ (printf, 2, 3))); int vasprintf(char **strp, const char *fmt, va_list ap) __attribute__ ((__format__ (printf, 2, 0))); diff --git a/userspace/main_user.c b/userspace/main_user.c index 420b3bc..65ce7c7 100644 --- a/userspace/main_user.c +++ b/userspace/main_user.c @@ -1,5 +1,8 @@ +#include "errno.h" #include "libc.h" #include "stdarg.h" +#include "stddef.h" +#include "sys/mman.h" #include "tiny.h" int func_help() @@ -10,6 +13,8 @@ int func_help() printf(" syscall5: a syscall with parameters over the stack \n"); printf(" alloc: test allocation\n"); printf(" tiny: a tiny C-like interpreter\n"); + printf(" mmap: test mmap \n"); + printf(" munmap: test munmap \n"); return 0; } @@ -25,29 +30,29 @@ char *initialHeap = 0; int func_alloc() { if (initialHeap == 0) { - initialHeap = sbrk(0); + initialHeap = sbrk(0); } printf("Testing allocation\n"); int allocSize = 4096 * 2; char *currentHeap = sbrk(0); if (currentHeap - initialHeap < allocSize) { - brk(initialHeap + allocSize); + brk(initialHeap + allocSize); } int *allocatedData = (int *)initialHeap; for (unsigned int i = 0; i < allocSize / sizeof(int); i++) { - allocatedData[i] = i; + allocatedData[i] = i; } printf("Success\nTesting malloc\n"); uintptr_t heap = (uintptr_t)sbrk(0); for (int i = 0; i < 12; i++) { - void *ptr = malloc(1 << i); - printf("malloc size %d: 0x%p\n", (1 << i), ptr); - if (ptr == NULL) { - printf("Malloc failed\n"); - } - memset(ptr, 0, 1 << i); - free(ptr); + void *ptr = malloc(1 << i); + printf("malloc size %d: 0x%p\n", (1 << i), ptr); + if (ptr == NULL) { + printf("Malloc failed\n"); + } + memset(ptr, 0, 1 << i); + free(ptr); } uintptr_t newHeap = (uintptr_t)sbrk(0); printf("Malloc used %dB\n", newHeap - heap); @@ -55,6 +60,64 @@ int func_alloc() return 0; } +struct mmapedArea { + void *addr; + size_t size; + struct mmapedArea *next; +}; + +static struct mmapedArea *listMmap = NULL; + +int func_mmap() +{ + struct mmapedArea *area = (struct mmapedArea *)malloc(sizeof(struct mmapedArea)); + + if (area == NULL) { + printf("Fail to allocate area\n"); + return -1; + } + char *path = "/dev/zero"; + void *mapAddr = mmap((void *)0, 4096, PROT_READ | PROT_WRITE, 0, path); + + if (mapAddr == MAP_FAILED) { + printf("mmap failed errno %d\n", errno); + return -1; + } + + printf("Zero mmaped at %p\n", mapAddr); + int data = *(int *)mapAddr; + *(int *)mapAddr = data; + + area->addr = mapAddr; + area->size = 4096; + area->next = listMmap; + listMmap = area; + + return 0; +} + +int func_munmap() +{ + if (!listMmap) { + printf("Nothing to unmap... mmap first! \n"); + return -1; + } + + struct mmapedArea *area = listMmap; + int ret = munmap(area->addr, area->size); + + if (ret) { + printf("Fail to unmap area at 0x%p (size %d) error %d\n", area->addr, area->size, ret); + return ret; + } + + printf("unmap done\n"); + listMmap = listMmap->next; + free(area); + + return ret; +} + int main(int argc, char *argv[]) { (void)argc; @@ -85,6 +148,14 @@ int main(int argc, char *argv[]) func_tiny(); continue; } + if (strcmp(buf, "mmap") == 0) { + func_mmap(); + continue; + } + if (strcmp(buf, "munmap") == 0) { + func_munmap(); + continue; + } } return 0; } diff --git a/userspace/sys/mman.h b/userspace/sys/mman.h new file mode 100644 index 0000000..40e0237 --- /dev/null +++ b/userspace/sys/mman.h @@ -0,0 +1,13 @@ +#pragma once + +/* To keep in sync with PAGING_MEM_* */ +#define PROT_EXEC (1<<3) +#define PROT_READ (1<<1) +#define PROT_WRITE (1<<2) + +#define MAP_SHARED (1 << 0) +#define MAP_PRIVATE (1 << 1) +#define MAP_FIXED (1 << 2) + + +#define MAP_FAILED (void *)-1