From 78cf247cd4c8f421de27e59df768be73590d8025 Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Wed, 31 Jan 2024 23:49:07 +0100 Subject: [PATCH] Add the ability to map/unmap a ressource --- core/syscall.c | 3 +- core/uaddrspace.c | 242 ++++++++++++++++++++++++++++++++++++++++-- core/uaddrspace.h | 8 +- drivers/zero.c | 32 ++++-- userspace/main_user.c | 1 + 5 files changed, 264 insertions(+), 22 deletions(-) diff --git a/core/syscall.c b/core/syscall.c index 5ebfc55..aeb8184 100644 --- a/core/syscall.c +++ b/core/syscall.c @@ -74,8 +74,9 @@ int syscallExecute(int syscallId, const struct cpu_state *userCtx) strzcpyFromUser((vaddr_t *)path, (uaddr_t *)userPath, sizeof(path)); printf("Trying mmap for device %s\n", path); if(strcmp(path, "/dev/zero") == 0){ - zeroMmap(as, &uaddr, size, rights,flags); + ret = zeroMmap(as, &uaddr, size, rights,flags); } + printf("ret %d\n", ret); (void)as; break; } diff --git a/core/uaddrspace.c b/core/uaddrspace.c index f621541..4eebbd6 100644 --- a/core/uaddrspace.c +++ b/core/uaddrspace.c @@ -24,6 +24,82 @@ struct uAddrSpace { size_t heapSize; // Heap size -> modified by brk() }; +static int hasOverlap(uaddr_t addr1, size_t size1, uaddr_t addr2, size_t size2) +{ + return max(addr1, addr2) < min(addr1 + size1, addr2 + size2); +} + +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) { @@ -61,6 +137,91 @@ int uAddrSpaceDelete(struct uAddrSpace *addr) 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 (!ALIGN(uaddr, PAGE_SIZE) || size <= 0) + 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->close) + reg->res->ops->close(reg); + + 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; @@ -137,14 +298,77 @@ free_ppage: return -1; } -int uAddrSpaceMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, uint32_t flags, - struct mappedRessource *res) +int uAddrSpaceMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, + uint32_t flags, struct mappedRessource *res, uint32_t offset ) { - (void)as; - (void)uaddr; - (void)rights; - (void)size; - (void)flags; - (void)res; - return 0; + 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; + } + + 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; } diff --git a/core/uaddrspace.h b/core/uaddrspace.h index 8fa3e53..77f49e6 100644 --- a/core/uaddrspace.h +++ b/core/uaddrspace.h @@ -7,6 +7,8 @@ struct uAddrSpace; struct uAddrVirtualReg; +#define UA_MAP_SHARED (1 << 0) +#define UA_MAP_FIXED (1 << 1) // TODO : move this struct to .c and add accessors struct uAddrVirtualReg { uaddr_t addr; @@ -33,6 +35,7 @@ struct mappedRessource { struct mappedRessourceOps *ops; struct uAddrVirtualReg *listVirtualReg; void *customData; + int (*onResMapped)(struct uAddrVirtualReg *reg); // Callabck for when the ressourced get mapped }; @@ -43,5 +46,6 @@ int uAddrSpaceSetHeap(struct uAddrSpace *as, uaddr_t addr, size_t size); int uAddrSpaceCheckNAlloc(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); +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); diff --git a/drivers/zero.c b/drivers/zero.c index 0580a65..9610658 100644 --- a/drivers/zero.c +++ b/drivers/zero.c @@ -1,5 +1,6 @@ -#include "zero.h" #include "alloc.h" +#include "klibc.h" +#include "zero.h" struct zeroMappedEntry { int refCnt; @@ -30,16 +31,27 @@ static struct mappedRessourceOps zeroOps = { .nopage = zeroNoPage, }; -int zeroMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, uint32_t flags) +int zeroOnMapped(struct uAddrVirtualReg *vreg) { - struct mappedRessource *res = (struct mappedRessource *)zalloc(sizeof(struct mappedRessource)); - struct zeroMappedEntry *cust = (struct zeroMappedEntry *)zalloc(sizeof(struct zeroMappedEntry)); + (void)vreg; - res->allowedRight = PAGING_MEM_READ | PAGING_MEM_WRITE | PAGING_MEM_EXEC | PAGING_MEM_USER; - res->ops = &zeroOps; - res->customData = cust; + printf("ZERO MAPPED !!\n"); - uAddrSpaceMmap(as, uaddr, size, rights, flags, res); - - return 0; + return 0; +} + +int zeroMmap(struct uAddrSpace *as, uaddr_t *uaddr, size_t size, uint32_t rights, + uint32_t flags) +{ + 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; + + return uAddrSpaceMmap(as, uaddr, size, rights, flags, res, 0); } diff --git a/userspace/main_user.c b/userspace/main_user.c index 3b7c1fb..cfdc52c 100644 --- a/userspace/main_user.c +++ b/userspace/main_user.c @@ -10,6 +10,7 @@ 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"); return 0; }