diff --git a/src/mmap.c b/src/mmap.c index ebbd741..ebe91e0 100644 --- a/src/mmap.c +++ b/src/mmap.c @@ -159,3 +159,101 @@ bool mmapReserve(struct Thread* thread, uint64 addr, uint64 length, uint32 prot, slot->fd = anonymous ? -1 : fd; slot->fileOffset = anonymous ? 0 : fileOffset; + *outAddr = start; + return true; +} + +bool mmapRelease(struct Thread* thread, uint64 addr, uint64 length) +{ + if (length == 0 || addr % frameSize != 0) + return false; + + uint64 unmapStart = addr; + uint64 unmapEnd = addr + pageAlignUp(length); + if (unmapEnd < unmapStart) + return false; + + /* Up-front check: refuse the WHOLE call if releasing this range would + need to split a Vma in the middle (touching neither end) and no free + slot exists to hold the resulting upper half. A thread's own Vmas + never overlap each other, so at most one can ever need splitting. */ + for (int i = 0; i < maxVmasPerThread; i++) { + Vma* v = &thread->vmas[i]; + if (v->used && v->start < unmapStart && v->end > unmapEnd && !findFreeVmaSlot(thread)) + return false; + } + + for (int i = 0; i < maxVmasPerThread; i++) { + Vma* v = &thread->vmas[i]; + if (!v->used || !rangesOverlap(v->start, v->end, unmapStart, unmapEnd)) + continue; + + uint64 freeFrom = v->start > unmapStart ? v->start : unmapStart; + uint64 freeTo = v->end < unmapEnd ? v->end : unmapEnd; + pagingUnmapUserRange(freeFrom, freeTo - freeFrom); + + if (v->start >= unmapStart && v->end <= unmapEnd) { + v->used = false; + } else if (v->start < unmapStart && v->end > unmapEnd) { + Vma* upper = findFreeVmaSlot(thread); + *upper = *v; + upper->start = unmapEnd; + v->end = unmapStart; + } else if (v->start < unmapStart) { + v->end = unmapStart; + } else { + uint64 trimmed = unmapEnd - v->start; + if (!v->anonymous) + v->fileOffset += trimmed; + v->start = unmapEnd; + } + } + return true; +} + +bool mmapFaultHandler(struct Thread* thread, uint64 faultAddr, bool forWrite) +{ + Vma* v = mmapFind(thread, faultAddr); + if (!v) + return false; + if (forWrite && !(v->prot & mmapProtWrite)) + return false; + if (!forWrite && !(v->prot & mmapProtRead)) + return false; + + uint64 pageAddr = pageAlignDown(faultAddr); + if (pagingResolve(pageAddr) != 0) + return true; + + uint64 frame = pmmAlloc(); + if (frame == 0) + return false; + memset((void*)frame, 0, frameSize); + + if (!v->anonymous) { + FileDescriptor* fdEntry = &thread->fds[v->fd]; + if (fdEntry->kind == fdFree) { + pmmFree(frame); + return false; + } + + uint64 savedOffset = fdEntry->offset; + fdEntry->offset = v->fileOffset + (pageAddr - v->start); + vfsRead(v->fd, (void*)frame, frameSize); + fdEntry->offset = savedOffset; + } + + uint64 flags = pageUser | ((v->prot & mmapProtWrite) ? pageWrite : 0); + if (!(v->prot & mmapProtExec)) + flags |= pageNoExecute; + if (!pagingMap(pageAddr, frame, flags)) { + pmmFree(frame); + return false; + } + return true; +} + +void mmapReleaseAll(struct Thread* thread) +{ + memset(thread->vmas, 0, sizeof(thread->vmas)); +}