From ee1453ad52e4ba43f5c3bd1665e7837ab7c58f39 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 21 Apr 2016 22:08:23 +0200 Subject: [PATCH] Add shared memory. --- kernel/copy.cpp | 14 +- kernel/descriptor.cpp | 26 ++ kernel/elf.cpp | 7 +- kernel/fcache.cpp | 35 ++- kernel/fs/kram.cpp | 17 +- kernel/fs/kram.h | 5 +- kernel/fs/user.cpp | 18 ++ kernel/include/sortix/kernel/descriptor.h | 4 + kernel/include/sortix/kernel/fcache.h | 12 +- kernel/include/sortix/kernel/inode.h | 7 + kernel/include/sortix/kernel/process.h | 10 +- kernel/include/sortix/kernel/refcount.h | 2 +- kernel/include/sortix/kernel/segment.h | 40 ++- kernel/include/sortix/kernel/vnode.h | 4 + kernel/inode.cpp | 15 + kernel/memorymanagement.cpp | 320 +++++++++++++++------- kernel/net/fs.cpp | 2 + kernel/process.cpp | 81 ++++-- kernel/segment.cpp | 76 +++-- kernel/vnode.cpp | 15 + regress/Makefile | 1 + regress/test-mmap-anon-shared.c | 57 ++++ 22 files changed, 584 insertions(+), 184 deletions(-) create mode 100644 regress/test-mmap-anon-shared.c diff --git a/kernel/copy.cpp b/kernel/copy.cpp index f1bdd874..b26707d8 100644 --- a/kernel/copy.cpp +++ b/kernel/copy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -55,11 +55,11 @@ static bool IsInProcessAddressSpace(Process* process) return current_address_space == process->addrspace; } -static struct segment* FindSegment(Process* process, uintptr_t addr) +static Segment* FindSegment(Process* process, uintptr_t addr) { for ( size_t i = 0; i < process->segments_used; i++ ) { - struct segment* segment = &process->segments[i]; + Segment* segment = &process->segments[i]; if ( addr < segment->addr ) continue; if ( segment->addr + segment->size <= addr ) @@ -79,7 +79,7 @@ bool CopyToUser(void* userdst_ptr, const void* ksrc_ptr, size_t count) kthread_mutex_lock(&process->segment_lock); while ( count ) { - struct segment* segment = FindSegment(process, userdst); + Segment* segment = FindSegment(process, userdst); if ( !segment || !(segment->prot & PROT_WRITE) ) { errno = EFAULT; @@ -109,7 +109,7 @@ bool CopyFromUser(void* kdst_ptr, const void* usersrc_ptr, size_t count) kthread_mutex_lock(&process->segment_lock); while ( count ) { - struct segment* segment = FindSegment(process, usersrc); + Segment* segment = FindSegment(process, usersrc); if ( !segment || !(segment->prot & PROT_READ) ) { errno = EFAULT; @@ -157,7 +157,7 @@ bool ZeroUser(void* userdst_ptr, size_t count) kthread_mutex_lock(&process->segment_lock); while ( count ) { - struct segment* segment = FindSegment(process, userdst); + Segment* segment = FindSegment(process, userdst); if ( !segment || !(segment->prot & PROT_WRITE) ) { errno = EFAULT; @@ -191,7 +191,7 @@ char* GetStringFromUser(const char* usersrc_str) while ( !done ) { uintptr_t current_at = usersrc + result_length; - struct segment* segment = FindSegment(process, current_at); + Segment* segment = FindSegment(process, current_at); if ( !segment || !(segment->prot & PROT_READ) ) { kthread_mutex_unlock(&process->segment_lock); diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index 645f8c7c..cf396294 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -888,4 +890,28 @@ int Descriptor::tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) return vnode->tcsetattr(ctx, actions, tio); } +addr_t Descriptor::mmap(ioctx_t* ctx, off_t off) +{ + if ( off & (Page::Size() - 1) ) + return errno = EINVAL, 0; + return vnode->mmap(ctx, off); +} + +void Descriptor::munmap(ioctx_t* ctx, off_t off) +{ + assert(!(off & (Page::Size() - 1))); + return vnode->munmap(ctx, off); +} + +int Descriptor::mprotect(ioctx_t* ctx, int prot) +{ + if ( !(dflags & O_READ) ) + return errno = EACCES, -1; + if ( (prot & (PROT_WRITE | PROT_KWRITE)) && !(dflags & O_WRITE) ) + return errno = EACCES, -1; + if ( (prot & (PROT_WRITE | PROT_KWRITE)) && dflags & O_APPEND ) + return errno = EACCES, -1; + return vnode->mprotect(ctx, prot); +} + } // namespace Sortix diff --git a/kernel/elf.cpp b/kernel/elf.cpp index 8bee7c5e..c0b23fb2 100644 --- a/kernel/elf.cpp +++ b/kernel/elf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -276,10 +276,7 @@ uintptr_t Load(const void* file_ptr, size_t file_size, Auxiliary* aux) uintptr_t map_end = Page::AlignUp(pheader->p_vaddr + pheader->p_memsz); size_t map_size = map_end - map_start; - struct segment segment; - segment.addr = map_start; - segment.size = map_size; - segment.prot = kprot; + Segment segment(map_start, map_size, kprot); assert(IsUserspaceSegment(&segment)); diff --git a/kernel/fcache.cpp b/kernel/fcache.cpp index d07e533c..03739e06 100644 --- a/kernel/fcache.cpp +++ b/kernel/fcache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -127,7 +127,7 @@ void BlockCache::ReleaseBlock(BlockCacheBlock* block) // put this block into a list of non-present blocks so we can reuse it // later and reallocate a physical frame for it - then we will just // reuse the block's meta information. - block->information &= ~(BCACHE_USED | BCACHE_PRESENT); + block->information = 0; return; } UnlinkBlock(block); @@ -136,7 +136,7 @@ void BlockCache::ReleaseBlock(BlockCacheBlock* block) unused_block->prev_block = block; block->next_block = unused_block; block->prev_block = NULL; - block->information &= ~BCACHE_USED; + block->information = BCACHE_PRESENT; unused_block = block; } @@ -493,4 +493,33 @@ bool FileCache::ChangeNumBlocks(size_t new_numblocks, bool exact) return true; } +addr_t FileCache::mmap(ioctx_t* /*ctx*/, off_t off) +{ + uintmax_t block_num = off / Page::Size(); + // TODO: Technically this violates POSIX that requires that you can make + if ( blocks_used <= block_num ) + return errno = EINVAL, 0; + BlockCacheBlock* block = blocks[block_num]; + assert(block); // TODO: Remove. + if ( !block ) + return errno = EINVAL, 0; + block->information |= BCACHE_MMAP; + uint8_t* block_data = kernel_block_cache->BlockData(block); + addr_t virt = 0; + int prot; + Memory::LookUp((uintptr_t) block_data, &virt, &prot); + assert(virt); + // TODO: Prevent truncate() from deallocating this memory! + return virt; +} + +void FileCache::munmap(ioctx_t* /*ctx*/, off_t /*off*/) +{ +} + +int FileCache::mprotect(ioctx_t* /*ctx*/, int /*prot*/) +{ + return 0; +} + } // namespace Sortix diff --git a/kernel/fs/kram.cpp b/kernel/fs/kram.cpp index fa6c2c0d..74035320 100644 --- a/kernel/fs/kram.cpp +++ b/kernel/fs/kram.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -183,6 +183,21 @@ int File::statvfs(ioctx_t* ctx, struct statvfs* stvfs) return common_statvfs(ctx, stvfs, dev); } +addr_t File::mmap(ioctx_t* ctx, off_t off) +{ + return fcache.mmap(ctx, off); +} + +void File::munmap(ioctx_t* ctx, off_t off) +{ + return fcache.munmap(ctx, off); +} + +int File::mprotect(ioctx_t* ctx, int prot) +{ + return fcache.mprotect(ctx, prot); +} + Dir::Dir(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode) { inode_type = INODE_TYPE_DIR; diff --git a/kernel/fs/kram.h b/kernel/fs/kram.h index 38b1005a..d5ab8a8d 100644 --- a/kernel/fs/kram.h +++ b/kernel/fs/kram.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -49,6 +49,9 @@ public: virtual ssize_t tcgetblob(ioctx_t* ctx, const char* name, void* buffer, size_t count); virtual int statvfs(ioctx_t* ctx, struct statvfs* stvfs); + virtual addr_t mmap(ioctx_t* ctx, off_t off); + virtual void munmap(ioctx_t* ctx, off_t off); + virtual int mprotect(ioctx_t* ctx, int prot); private: FileCache fcache; diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index 0af14f9e..6260d380 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -256,6 +256,9 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx); virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + virtual addr_t mmap(ioctx_t* ctx, off_t off); + virtual void munmap(ioctx_t* ctx, off_t off); + virtual int mprotect(ioctx_t* ctx, int prot); private: bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, @@ -1587,6 +1590,21 @@ int Unode::tcsetattr(ioctx_t* ctx, int actions, const struct termios* user_tio) return ret; } +addr_t Unode::mmap(ioctx_t* /*ctx*/, off_t /*off*/) +{ + return errno = ENODEV, 0; +} + +void Unode::munmap(ioctx_t* /*ctx*/, off_t /*off*/) +{ + errno = ENODEV; +} + +int Unode::mprotect(ioctx_t* /*ctx*/, int /*prot*/) +{ + return errno = ENODEV, -1; +} + bool Bootstrap(Ref* out_root, Ref* out_server, const struct stat* rootst) diff --git a/kernel/include/sortix/kernel/descriptor.h b/kernel/include/sortix/kernel/descriptor.h index f995d219..b6577eda 100644 --- a/kernel/include/sortix/kernel/descriptor.h +++ b/kernel/include/sortix/kernel/descriptor.h @@ -26,6 +26,7 @@ #include +#include #include #include @@ -110,6 +111,9 @@ public: pid_t tcgetsid(ioctx_t* ctx); int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + addr_t mmap(ioctx_t* ctx, off_t off); + void munmap(ioctx_t* ctx, off_t off); + int mprotect(ioctx_t* ctx, int prot); private: Ref open_elem(ioctx_t* ctx, const char* filename, int flags, diff --git a/kernel/include/sortix/kernel/fcache.h b/kernel/include/sortix/kernel/fcache.h index bb3c92f0..94aac1a7 100644 --- a/kernel/include/sortix/kernel/fcache.h +++ b/kernel/include/sortix/kernel/fcache.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,9 +40,10 @@ struct BlockCacheBlock; class FileCache; //class FileCacheBackend; -const uintptr_t BCACHE_PRESENT = 1 << 0; -const uintptr_t BCACHE_USED = 1 << 1; -const uintptr_t BCACHE_MODIFIED = 1 << 2; +static const uintptr_t BCACHE_PRESENT = 1 << 0; +static const uintptr_t BCACHE_USED = 1 << 1; +static const uintptr_t BCACHE_MODIFIED = 1 << 2; +static const uintptr_t BCACHE_MMAP = 1 << 3; class BlockCache { @@ -106,6 +107,9 @@ public: ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off); int truncate(ioctx_t* ctx, off_t length); off_t lseek(ioctx_t* ctx, off_t offset, int whence); + addr_t mmap(ioctx_t* ctx, off_t off); + void munmap(ioctx_t* ctx, off_t off); + int mprotect(ioctx_t* ctx, int prot); //bool ChangeBackend(FileCacheBackend* backend, bool sync_old); off_t GetFileSize(); diff --git a/kernel/include/sortix/kernel/inode.h b/kernel/include/sortix/kernel/inode.h index 9e8efe34..9c35409b 100644 --- a/kernel/include/sortix/kernel/inode.h +++ b/kernel/include/sortix/kernel/inode.h @@ -27,6 +27,7 @@ #include +#include #include struct dirent; @@ -114,6 +115,9 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx) = 0; virtual int tcsendbreak(ioctx_t* ctx, int duration) = 0; virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) = 0; + virtual addr_t mmap(ioctx_t* ctx, off_t off) = 0; + virtual void munmap(ioctx_t* ctx, off_t off) = 0; + virtual int mprotect(ioctx_t* ctx, int prot) = 0; }; @@ -207,6 +211,9 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx); virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + virtual addr_t mmap(ioctx_t* ctx, off_t off); + virtual void munmap(ioctx_t* ctx, off_t off); + virtual int mprotect(ioctx_t* ctx, int prot); }; diff --git a/kernel/include/sortix/kernel/process.h b/kernel/include/sortix/kernel/process.h index cee25a6a..cc57ae01 100644 --- a/kernel/include/sortix/kernel/process.h +++ b/kernel/include/sortix/kernel/process.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -46,11 +46,9 @@ class Descriptor; class DescriptorTable; class MountTable; class ProcessTable; -struct ProcessSegment; struct ProcessTimer; struct ioctx_struct; typedef struct ioctx_struct ioctx_t; -struct segment; class Process { @@ -139,7 +137,7 @@ public: bool threads_exiting; public: - struct segment* segments; + Segment* segments; size_t segments_used; size_t segments_length; kthread_mutex_t segment_write_lock; @@ -170,8 +168,8 @@ public: void AddChildProcess(Process* child); void ScheduleDeath(); void AbortConstruction(); - bool MapSegment(struct segment* result, void* hint, size_t size, int flags, - int prot); + bool MapSegment(struct segment_location* result, void* hint, size_t size, + int flags, int prot); public: Process* Fork(); diff --git a/kernel/include/sortix/kernel/refcount.h b/kernel/include/sortix/kernel/refcount.h index 1620c9a8..af26d0fb 100644 --- a/kernel/include/sortix/kernel/refcount.h +++ b/kernel/include/sortix/kernel/refcount.h @@ -55,7 +55,7 @@ public: Ref(const Ref& r) : obj(r.Get()) { if ( obj ) obj->Refer_Renamed(); } template Ref(const Ref& r) : obj(r.Get()) { if ( obj ) obj->Refer_Renamed(); } - ~Ref() { if ( obj ) obj->Unref_Renamed(); } + ~Ref() { if ( obj ) obj->Unref_Renamed(); obj = NULL; } Ref& operator=(const Ref r) { diff --git a/kernel/include/sortix/kernel/segment.h b/kernel/include/sortix/kernel/segment.h index 7b05cf3b..69a0fd83 100644 --- a/kernel/include/sortix/kernel/segment.h +++ b/kernel/include/sortix/kernel/segment.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,21 +23,35 @@ #include #include +#include + namespace Sortix { class Process; -struct segment +struct segment_location { uintptr_t addr; size_t size; +}; + +class Segment : public segment_location +{ +public: + Segment() { } // For operator new[]. + Segment(uintptr_t addr, size_t size, int prot) : + segment_location({addr, size}), prot(prot), desc(NULL), offset(0) { } + Segment(uintptr_t addr, size_t size, int prot, Ref desc, off_t offset) : + segment_location({addr, size}), prot(prot), desc(desc), offset(offset) { } int prot; + Ref desc; + off_t offset; }; static inline int segmentcmp(const void* a_ptr, const void* b_ptr) { - const struct segment* a = (const struct segment*) a_ptr; - const struct segment* b = (const struct segment*) b_ptr; + const Segment* a = (const Segment*) a_ptr; + const Segment* b = (const Segment*) b_ptr; return a->addr < b->addr ? -1 : b->addr < a->addr ? 1 : a->size < b->size ? -1 : @@ -45,13 +59,17 @@ static inline int segmentcmp(const void* a_ptr, const void* b_ptr) 0 ; } -bool AreSegmentsOverlapping(const struct segment* a, const struct segment* b); -bool IsUserspaceSegment(const struct segment* segment); -struct segment* FindOverlappingSegment(Process* process, const struct segment* new_segment); -bool IsSegmentOverlapping(Process* process, const struct segment* new_segment); -bool AddSegment(Process* process, const struct segment* new_segment); -bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr, - size_t size, int flags); +bool AreSegmentsOverlapping(const struct segment_location* a, + const struct segment_location* b); +bool IsUserspaceSegment(const Segment* segment); +Segment* FindOverlappingSegment(Process* process, + const struct segment_location* location); +bool IsSegmentOverlapping(Process* process, const segment_location* location); +bool AddSegment(Process* process, const Segment* new_segment); +bool PlaceSegment(struct segment_location* solution, Process* process, + void* addr_ptr, size_t size, int flags); +void UnmapSegment(Segment* segment); +void UnmapSegmentRange(Segment* segment, size_t offset, size_t size); } // namespace Sortix diff --git a/kernel/include/sortix/kernel/vnode.h b/kernel/include/sortix/kernel/vnode.h index 476aea7e..c9be19f6 100644 --- a/kernel/include/sortix/kernel/vnode.h +++ b/kernel/include/sortix/kernel/vnode.h @@ -26,6 +26,7 @@ #include +#include #include struct dirent; @@ -107,6 +108,9 @@ public: pid_t tcgetsid(ioctx_t* ctx); int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + addr_t mmap(ioctx_t* ctx, off_t off); + void munmap(ioctx_t* ctx, off_t off); + int mprotect(ioctx_t* ctx, int prot); public /*TODO: private*/: Ref inode; diff --git a/kernel/inode.cpp b/kernel/inode.cpp index 82776f7c..2a3f0677 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -443,4 +443,19 @@ int AbstractInode::tcsetattr(ioctx_t* /*ctx*/, int /*actions*/, const struct ter return errno = ENOTTY, -1; } +addr_t AbstractInode::mmap(ioctx_t* /*ctx*/, off_t /*off*/) +{ + return errno = ENODEV, 0; +} + +void AbstractInode::munmap(ioctx_t* /*ctx*/, off_t /*off*/) +{ + errno = ENODEV; +} + +int AbstractInode::mprotect(ioctx_t* /*ctx*/, int /*prot*/) +{ + return errno = ENODEV, -1; +} + } // namespace Sortix diff --git a/kernel/memorymanagement.cpp b/kernel/memorymanagement.cpp index 43525562..70c75020 100644 --- a/kernel/memorymanagement.cpp +++ b/kernel/memorymanagement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,17 +25,22 @@ #include #include +#include #include #include #include #include +#include #include #include #include #include #include #include +#include + +#include "fs/kram.h" namespace Sortix { @@ -69,54 +74,56 @@ void UnmapMemory(Process* process, uintptr_t addr, size_t size) if ( !size ) return; - struct segment unmap_segment; - unmap_segment.addr = addr; - unmap_segment.size = size; - unmap_segment.prot = 0; - while ( struct segment* conflict = FindOverlappingSegment(process, - &unmap_segment) ) + struct segment_location loc; + loc.addr = addr; + loc.size = size; + while ( Segment* conflict = FindOverlappingSegment(process, &loc) ) { // Delete the segment if covered entirely by our request. if ( addr <= conflict->addr && conflict->addr + conflict->size <= addr + size ) { uintptr_t conflict_offset = (uintptr_t) conflict - (uintptr_t) process->segments; - size_t conflict_index = conflict_offset / sizeof(struct segment); - Memory::UnmapRange(conflict->addr, conflict->size, PAGE_USAGE_USER_SPACE); - Memory::Flush(); + size_t conflict_index = conflict_offset / sizeof(Segment); + UnmapSegment(conflict); + conflict->~Segment(); if ( conflict_index + 1 == process->segments_used ) { process->segments_used--; continue; } - process->segments[conflict_index] = process->segments[--process->segments_used]; + Segment* old = &process->segments[--process->segments_used]; + Segment* dst = &process->segments[conflict_index]; + *dst = *old; + old->~Segment(); + // TODO: It's wrong to qsort the Segment class. qsort(process->segments, process->segments_used, - sizeof(struct segment), segmentcmp); + sizeof(Segment), segmentcmp); continue; } // Delete the middle of the segment if covered there by our request. if ( conflict->addr < addr && addr + size - conflict->addr <= conflict->size ) { - Memory::UnmapRange(addr, size, PAGE_USAGE_USER_SPACE); - Memory::Flush(); - struct segment right_segment; - right_segment.addr = addr + size; - right_segment.size = conflict->addr + conflict->size - (addr + size); - right_segment.prot = conflict->prot; + UnmapSegmentRange(conflict, addr - conflict->addr, size); + size_t new_addr = addr + size; + size_t new_size = conflict->addr + conflict->size - (addr + size); + off_t new_offset = conflict->offset + (new_addr - conflict->addr); + Segment right_segment(new_addr, new_size, conflict->prot, + conflict->desc, new_offset); conflict->size = addr - conflict->addr; // TODO: This shouldn't really fail as we free memory above, but // this code isn't really provably reliable. if ( !AddSegment(process, &right_segment) ) - PanicF("Unexpectedly unable to split memory mapped segment"); + Panic("Unexpectedly unable to split memory mapped segment"); continue; } // Delete the part of the segment covered partially from the left. if ( addr <= conflict->addr ) { - Memory::UnmapRange(conflict->addr, addr + size - conflict->addr, PAGE_USAGE_USER_SPACE); - Memory::Flush(); - conflict->size = conflict->addr + conflict->size - (addr + size); + UnmapSegmentRange(conflict, 0, addr + size - conflict->addr); + conflict->size += conflict->addr - (addr + size); + conflict->offset += conflict->addr - (addr + size); conflict->addr = addr + size; continue; } @@ -124,8 +131,8 @@ void UnmapMemory(Process* process, uintptr_t addr, size_t size) // Delete the part of the segment covered partially from the right. if ( conflict->addr <= addr + size ) { - Memory::UnmapRange(addr, conflict->addr + conflict->size - addr, PAGE_USAGE_USER_SPACE); - Memory::Flush(); + UnmapSegmentRange(conflict, addr - conflict->addr, + conflict->addr + conflict->size - addr); conflict->size -= conflict->addr + conflict->size - addr; continue; } @@ -145,24 +152,28 @@ bool ProtectMemory(Process* process, uintptr_t addr, size_t size, int prot) // there are no gaps in that region. This is where the operation can fail as // the AddSegment call can run out of memory. There is no harm in splitting // the segments into smaller chunks. + bool any_had_desc = false; for ( size_t offset = 0; offset < size; ) { - struct segment search_region; + struct segment_location search_region; search_region.addr = addr + offset; search_region.size = Page::Size(); - search_region.prot = prot; - struct segment* segment = FindOverlappingSegment(process, &search_region); + Segment* segment = FindOverlappingSegment(process, &search_region); if ( !segment ) return errno = EINVAL, false; + if ( segment->desc ) + any_had_desc = true; + // Split the segment into two if it begins before our search region. if ( segment->addr < search_region.addr ) { - struct segment new_segment; - new_segment.addr = search_region.addr; - new_segment.size = segment->addr + segment->size - new_segment.addr; - new_segment.prot = segment->prot; + size_t new_addr = search_region.addr; + size_t new_size = segment->size + segment->addr - new_addr; + size_t new_offset = segment->offset + segment->addr - new_addr; + Segment new_segment(new_addr, new_size, segment->prot, + segment->desc, new_offset); segment->size = search_region.addr - segment->addr; if ( !AddSegment(process, &new_segment) ) @@ -177,10 +188,11 @@ bool ProtectMemory(Process* process, uintptr_t addr, size_t size, int prot) // Split the segment into two if it ends after addr + size. if ( size < segment->addr + segment->size - addr ) { - struct segment new_segment; - new_segment.addr = addr + size; - new_segment.size = segment->addr + segment->size - new_segment.addr; - new_segment.prot = segment->prot; + size_t new_addr = addr + size; + size_t new_size = segment->size + segment->addr - new_addr; + size_t new_offset = segment->offset + segment->addr - new_addr; + Segment new_segment(new_addr, new_size, segment->prot, + segment->desc, new_offset); segment->size = addr + size - segment->addr; if ( !AddSegment(process, &new_segment) ) @@ -195,15 +207,30 @@ bool ProtectMemory(Process* process, uintptr_t addr, size_t size, int prot) offset += segment->size; } + // Verify that any backing files allow the new protection. + ioctx_t ctx; SetupUserIOCtx(&ctx); + for ( size_t offset = 0; any_had_desc && offset < size; ) + { + struct segment_location search_region; + search_region.addr = addr + offset; + search_region.size = Page::Size(); + Segment* segment = FindOverlappingSegment(process, &search_region); + assert(segment); + + if ( segment->prot != prot && + segment->desc && + segment->desc->mprotect(&ctx, prot) < 0 ) + return false; + } + // Run through all the segments in the region [addr, addr+size) and change // the permissions and update the permissions of the virtual memory itself. for ( size_t offset = 0; offset < size; ) { - struct segment search_region; + struct segment_location search_region; search_region.addr = addr + offset; search_region.size = Page::Size(); - search_region.prot = prot; - struct segment* segment = FindOverlappingSegment(process, &search_region); + Segment* segment = FindOverlappingSegment(process, &search_region); assert(segment); if ( segment->prot != prot ) @@ -234,10 +261,7 @@ bool MapMemory(Process* process, uintptr_t addr, size_t size, int prot) UnmapMemory(process, addr, size); - struct segment new_segment; - new_segment.addr = addr; - new_segment.size = size; - new_segment.prot = prot; + Segment new_segment(addr, size, prot); if ( !MapRange(new_segment.addr, new_segment.size, new_segment.prot, PAGE_USAGE_USER_SPACE) ) return false; @@ -245,8 +269,7 @@ bool MapMemory(Process* process, uintptr_t addr, size_t size, int prot) if ( !AddSegment(process, &new_segment) ) { - UnmapRange(new_segment.addr, new_segment.size, PAGE_USAGE_USER_SPACE); - Memory::Flush(); + UnmapSegment(&new_segment); return false; } @@ -291,9 +314,6 @@ void* sys_mmap(void* addr_ptr, size_t size, int prot, int flags, int fd, // Verify that MAP_PRIVATE and MAP_SHARED are not both set. if ( bool(flags & MAP_PRIVATE) == bool(flags & MAP_SHARED) ) return errno = EINVAL, MAP_FAILED; - // TODO: MAP_SHARED is not currently supported. - if ( flags & MAP_SHARED ) - return errno = EINVAL, MAP_FAILED; // Verify the fíle descriptor and the offset is suitable set if needed. if ( !(flags & MAP_ANONYMOUS) && (fd < 0 || offset < 0 || (offset & (Page::Size()-1))) ) @@ -322,82 +342,174 @@ void* sys_mmap(void* addr_ptr, size_t size, int prot, int flags, int fd, // Verify whether the backing file is usable for memory mapping. ioctx_t ctx; SetupUserIOCtx(&ctx); Ref desc; - if ( !(flags & MAP_ANONYMOUS) ) + if ( flags & MAP_ANONYMOUS ) + { + // Create an unnamed ramfs file to back this memory mapping. + if ( flags & MAP_SHARED ) + { + Ref inode(new KRAMFS::File(INODE_TYPE_FILE, S_IFREG, 0, 0, + ctx.uid, ctx.gid, 0600)); + if ( !inode ) + return MAP_FAILED; + Ref vnode(new Vnode(inode, Ref(), 0, 0)); + inode.Reset(); + if ( !vnode ) + return MAP_FAILED; + desc = Ref(new Descriptor(vnode, O_READ | O_WRITE)); + vnode.Reset(); + if ( !desc ) + return MAP_FAILED; + if ( (uintmax_t) OFF_MAX < (uintmax_t) size ) + return errno = EOVERFLOW, MAP_FAILED; + if ( desc->truncate(&ctx, size) < 0 ) + return MAP_FAILED; + offset = 0; + } + } + else { if ( !(desc = process->GetDescriptor(fd)) ) return MAP_FAILED; - // Verify that the file is seekable. - if ( desc->lseek(&ctx, 0, SEEK_CUR) < 0 ) - return errno = ENODEV, MAP_FAILED; - // Verify that we have read access to the file. - if ( desc->read(&ctx, NULL, 0) != 0 ) - return errno = EACCES, MAP_FAILED; - // Verify that we have write access to the file if needed. - if ( (prot & PROT_WRITE) && !(flags & MAP_PRIVATE) && - desc->write(&ctx, NULL, 0) != 0 ) - return errno = EACCES, MAP_FAILED; + // Verify if going through the inode mmap interface. + if ( flags & MAP_SHARED ) + { + if ( desc->mprotect(&ctx, prot) < 0 ) + return MAP_FAILED; + } + // Verify if not going through the inode mmap interface. + else if ( flags & MAP_PRIVATE ) + { + // Verify that the file is seekable. + if ( desc->lseek(&ctx, 0, SEEK_CUR) < 0 ) + return errno = ENODEV, MAP_FAILED; + // Verify that we have read access to the file. + if ( desc->read(&ctx, NULL, 0) != 0 ) + return errno = EACCES, MAP_FAILED; + // Verify that we have write access to the file if needed. + if ( (prot & PROT_WRITE) && !(flags & MAP_PRIVATE) && + desc->write(&ctx, NULL, 0) != 0 ) + return errno = EACCES, MAP_FAILED; + } } + if ( prot & PROT_READ ) + prot |= PROT_KREAD; + if ( prot & PROT_WRITE ) + prot |= PROT_KWRITE; + if ( flags & MAP_PRIVATE ) + prot |= PROT_FORK; + ScopedLock lock1(&process->segment_write_lock); ScopedLock lock2(&process->segment_lock); // Determine where to put the new segment and its protection. - struct segment new_segment; + struct segment_location location; if ( flags & MAP_FIXED ) - new_segment.addr = aligned_addr, - new_segment.size = aligned_size; - else if ( !PlaceSegment(&new_segment, process, (void*) addr, aligned_size, flags) ) - return errno = ENOMEM, MAP_FAILED; - new_segment.prot = PROT_KWRITE | PROT_FORK; - - // Allocate a memory segment with the desired properties. - if ( !Memory::MapMemory(process, new_segment.addr, new_segment.size, new_segment.prot) ) - return MAP_FAILED; - - // The pread will copy to user-space right requires this lock to be free. - lock2.Reset(); - - // Read the file contents into the newly allocated memory. - if ( !(flags & MAP_ANONYMOUS) ) { - ioctx_t kctx; SetupKernelIOCtx(&kctx); - for ( size_t so_far = 0; so_far < aligned_size; ) + location.addr = aligned_addr; + location.size = aligned_size; + } + else if ( !PlaceSegment(&location, process, (void*) addr, aligned_size, flags) ) + return errno = ENOMEM, MAP_FAILED; + + if ( flags & MAP_SHARED ) + { + assert(desc); + + Memory::UnmapMemory(process, location.addr, location.size); + + Segment new_segment(location.addr, 0, prot, desc, offset); + + while ( new_segment.size < location.size ) { - uint8_t* ptr = (uint8_t*) (new_segment.addr + so_far); - size_t left = aligned_size - so_far; - off_t pos = offset + so_far; - ssize_t num_bytes = desc->pread(&kctx, ptr, left, pos); - if ( num_bytes < 0 ) + off_t offset; + if ( __builtin_add_overflow(new_segment.offset, new_segment.size, + &offset) ) { - // TODO: How should this situation be handled? For now we'll - // just ignore the error condition. - errno = 0; - break; + errno = EOVERFLOW; + Memory::Flush(); + UnmapSegment(&new_segment); + return MAP_FAILED; } - if ( !num_bytes ) + assert(!(offset & (Page::Size() - 1))); + + addr_t addr = desc->mmap(&ctx, offset); + if ( !addr ) { - // We got an unexpected early end-of-file condition, but that's - // alright as the MapMemory call zero'd the new memory and we - // are expected to zero the remainder. - break; + Memory::Flush(); + UnmapSegment(&new_segment); + return MAP_FAILED; } - so_far += num_bytes; + uintptr_t virt = location.addr + new_segment.size; + + if ( !Memory::Map(addr, virt, prot) ) + { + desc->munmap(&ctx, offset); + Memory::Flush(); + UnmapSegment(&new_segment); + return MAP_FAILED; + } + + new_segment.size += Page::Size(); + } + Memory::Flush(); + + if ( !AddSegment(process, &new_segment) ) + { + UnmapSegment(&new_segment); + return MAP_FAILED; + } + } + else + { + int first_prot = flags & MAP_ANONYMOUS ? prot : PROT_KWRITE | PROT_FORK; + Segment new_segment(location.addr, location.size, first_prot); + + // Allocate a memory segment with the desired properties. + if ( !Memory::MapMemory(process, new_segment.addr, new_segment.size, + new_segment.prot) ) + return MAP_FAILED; + + // Read the file contents into the newly allocated memory. + if ( !(flags & MAP_ANONYMOUS) ) + { + // The pread will copy to user-space right requires this lock to be + // free. + lock2.Reset(); + + ioctx_t kctx; SetupKernelIOCtx(&kctx); + for ( size_t so_far = 0; so_far < aligned_size; ) + { + uint8_t* ptr = (uint8_t*) (new_segment.addr + so_far); + size_t left = aligned_size - so_far; + off_t pos = offset + so_far; + ssize_t num_bytes = desc->pread(&kctx, ptr, left, pos); + if ( num_bytes < 0 ) + { + // TODO: How should this situation be handled? For now we'll + // just ignore the error condition. + errno = 0; + break; + } + if ( !num_bytes ) + { + // We got an unexpected early end-of-file condition, but + // that's alright as the MapMemory call zero'd the new + // memory and we are expected to zero the remainder. + break; + } + so_far += num_bytes; + } + + // Finally switch to the desired page protections. + kthread_mutex_lock(&process->segment_lock); + Memory::ProtectMemory(CurrentProcess(), new_segment.addr, + new_segment.size, prot); + kthread_mutex_unlock(&process->segment_lock); } } - // Finally switch to the desired page protections. - kthread_mutex_lock(&process->segment_lock); - if ( prot & PROT_READ ) - prot |= PROT_KREAD; - if ( prot & PROT_WRITE ) - prot |= PROT_KWRITE; - prot |= PROT_FORK; - Memory::ProtectMemory(CurrentProcess(), new_segment.addr, new_segment.size, prot); - kthread_mutex_unlock(&process->segment_lock); - - lock1.Reset(); - - return (void*) new_segment.addr; + return (void*) location.addr; } int sys_mprotect(const void* addr_ptr, size_t size, int prot) diff --git a/kernel/net/fs.cpp b/kernel/net/fs.cpp index f16dba44..5a3a73dc 100644 --- a/kernel/net/fs.cpp +++ b/kernel/net/fs.cpp @@ -259,6 +259,7 @@ ssize_t StreamSocket::recv(ioctx_t* ctx, uint8_t* buf, size_t count, ScopedLock lock(&socket_lock); if ( !is_connected ) return errno = ENOTCONN, -1; + lock.Reset(); return incoming.read(ctx, buf, count); } @@ -268,6 +269,7 @@ ssize_t StreamSocket::send(ioctx_t* ctx, const uint8_t* buf, size_t count, ScopedLock lock(&socket_lock); if ( !is_connected ) return errno = ENOTCONN, -1; + lock.Reset(); return outgoing.write(ctx, buf, count); } diff --git a/kernel/process.cpp b/kernel/process.cpp index dc7f074d..8f361188 100644 --- a/kernel/process.cpp +++ b/kernel/process.cpp @@ -366,12 +366,14 @@ void Process::ResetAddressSpace() assert(Memory::GetAddressSpace() == addrspace); for ( size_t i = 0; i < segments_used; i++ ) - Memory::UnmapRange(segments[i].addr, segments[i].size, PAGE_USAGE_USER_SPACE); - - Memory::Flush(); + { + UnmapSegment(&segments[i]); + //segments[i].~Segment(); + segments[i].desc.Reset(); + } segments_used = segments_length = 0; - free(segments); + delete[] segments; segments = NULL; } @@ -624,25 +626,68 @@ Process* Process::Fork() return NULL; } - struct segment* clone_segments = NULL; + Segment* clone_segments = NULL; // Fork the segment list. + kthread_mutex_lock(&segment_lock); + bool segment_failure = false; + size_t segment_failure_i = 0; + size_t segment_failure_o = 0; if ( segments ) { - size_t segments_size = sizeof(struct segment) * segments_used; - if ( !(clone_segments = (struct segment*) malloc(segments_size)) ) + clone_segments = new Segment[segments_used]; + if ( !clone_segments ) { + kthread_mutex_unlock(&segment_lock); delete clone; return NULL; } - memcpy(clone_segments, segments, segments_size); + + for ( size_t i = 0; i < segments_used; i++ ) + clone_segments[i] = segments[i]; + + ioctx_t ctx; SetupUserIOCtx(&ctx); + for ( size_t i = 0; i < segments_used; i++ ) + { + for ( size_t o = 0; + clone_segments[i].desc && o < clone_segments[i].size; + o += Page::Size() ) + { + off_t offset = clone_segments[i].offset + o; + if ( !clone_segments[i].desc->mmap(&ctx, offset) ) + { + segment_failure = true; + segment_failure_i = i; + segment_failure_o = o; + break; + } + } + if ( segment_failure ) + break; + } } // Fork address-space here and copy memory. - clone->addrspace = Memory::Fork(); + clone->addrspace = !segment_failure? Memory::Fork() : 0; + kthread_mutex_unlock(&segment_lock); if ( !clone->addrspace ) { - free(clone_segments); + ioctx_t ctx; SetupUserIOCtx(&ctx); + for ( size_t i = 0; i < segments_used; i++ ) + { + if ( segment_failure && i <= segment_failure_i ) + break; + for ( size_t o = 0; + clone_segments[i].desc && o < clone_segments[i].size; + o += Page::Size() ) + { + if ( segment_failure && o <= segment_failure_o ) + break; + off_t offset = clone_segments[i].offset + o; + clone_segments[i].desc->munmap(&ctx, offset); + } + } + delete[] clone_segments; delete clone; return NULL; } @@ -744,8 +789,8 @@ void Process::ResetForExecute() ResetAddressSpace(); } -bool Process::MapSegment(struct segment* result, void* hint, size_t size, - int flags, int prot) +bool Process::MapSegment(struct segment_location* result, void* hint, + size_t size, int flags, int prot) { // process->segment_write_lock is held at this point. // process->segment_lock is held at this point. @@ -755,7 +800,7 @@ bool Process::MapSegment(struct segment* result, void* hint, size_t size, if ( !PlaceSegment(result, this, hint, size, flags) ) return false; - if ( !Memory::MapMemory(this, result->addr, result->size, result->prot = prot) ) + if ( !Memory::MapMemory(this, result->addr, result->size, prot) ) { // The caller is expected to self-destruct in this case, so the // segment just created is not removed. @@ -849,11 +894,11 @@ int Process::Execute(const char* programname, const uint8_t* program, for ( int i = 0; i < envc; i++ ) arg_size += strlen(envp[i]) + 1; - struct segment arg_segment; - struct segment stack_segment; - struct segment raw_tls_segment; - struct segment tls_segment; - struct segment auxcode_segment; + struct segment_location arg_segment; + struct segment_location stack_segment; + struct segment_location raw_tls_segment; + struct segment_location tls_segment; + struct segment_location auxcode_segment; kthread_mutex_lock(&segment_write_lock); kthread_mutex_lock(&segment_lock); diff --git a/kernel/segment.cpp b/kernel/segment.cpp index c0582dc3..5808aeb0 100644 --- a/kernel/segment.cpp +++ b/kernel/segment.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,12 +36,13 @@ namespace Sortix { -bool AreSegmentsOverlapping(const struct segment* a, const struct segment* b) +bool AreSegmentsOverlapping(const struct segment_location* a, + const struct segment_location* b) { return a->addr < b->addr + b->size && b->addr < a->addr + a->size; } -bool IsUserspaceSegment(const struct segment* segment) +bool IsUserspaceSegment(const Segment* segment) { uintptr_t userspace_addr; size_t userspace_size; @@ -53,29 +55,31 @@ bool IsUserspaceSegment(const struct segment* segment) return true; } -struct segment* FindOverlappingSegment(Process* process, const struct segment* new_segment) +Segment* FindOverlappingSegment(Process* process, + const struct segment_location* location) { // process->segment_lock is held at this point. // TODO: Speed up using binary search. for ( size_t i = 0; i < process->segments_used; i++ ) { - struct segment* segment = &process->segments[i]; - if ( AreSegmentsOverlapping(segment, new_segment) ) + Segment* segment = &process->segments[i]; + if ( AreSegmentsOverlapping(segment, location) ) return segment; } return NULL; } -bool IsSegmentOverlapping(Process* process, const struct segment* new_segment) +bool IsSegmentOverlapping(Process* process, + const struct segment_location* location) { // process->segment_lock is held at this point. - return FindOverlappingSegment(process, new_segment) != NULL; + return FindOverlappingSegment(process, location) != NULL; } -bool AddSegment(Process* process, const struct segment* new_segment) +bool AddSegment(Process* process, const Segment* new_segment) { // process->segment_lock is held at this point. @@ -86,11 +90,12 @@ bool AddSegment(Process* process, const struct segment* new_segment) { size_t new_length = process->segments_length ? process->segments_length * 2 : 8; - size_t new_size = new_length * sizeof(struct segment); - struct segment* new_segments = - (struct segment*) realloc(process->segments, new_size); + Segment* new_segments = new Segment[new_length]; if ( !new_segments ) return false; + for ( size_t i = 0; i < process->segments_used; i++ ) + new_segments[i] = process->segments[i]; + delete[] process->segments; process->segments = new_segments; process->segments_length = new_length; } @@ -99,7 +104,8 @@ bool AddSegment(Process* process, const struct segment* new_segment) process->segments[process->segments_used++] = *new_segment; // Sort the segment list after address. - qsort(process->segments, process->segments_used, sizeof(struct segment), + // TODO: It's wrong to qsort the Segment class. + qsort(process->segments, process->segments_used, sizeof(Segment), segmentcmp); return true; @@ -107,7 +113,7 @@ bool AddSegment(Process* process, const struct segment* new_segment) class segment_gaps { - typedef yielder_iterator my_iterator; + typedef yielder_iterator my_iterator; public: segment_gaps(finished_yielder) : process(0) { } @@ -121,7 +127,7 @@ public: Memory::GetUserVirtualArea(&userspace_addr, &userspace_size); } - bool yield(struct segment* result) + bool yield(Segment* result) { // process->segment_lock is held at this point. @@ -200,8 +206,8 @@ private: }; -bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr, - size_t size, int flags) +bool PlaceSegment(struct segment_location* solution, Process* process, + void* addr_ptr, size_t size, int flags) { // process->segment_lock is held at this point. @@ -212,9 +218,9 @@ bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr, size = Page::AlignUp(size); bool found_any = false; size_t best_distance = 0; - struct segment best; + struct segment_location best; - for ( struct segment gap : segment_gaps(process) ) + for ( Segment gap : segment_gaps(process) ) { if ( gap.size < size ) continue; @@ -222,20 +228,17 @@ bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr, { solution->addr = addr; solution->size = size; - solution->prot = 0; return true; } - struct segment attempt; + struct segment_location attempt; size_t distance; attempt.addr = gap.addr; attempt.size = size; - attempt.prot = 0; distance = addr < attempt.addr ? attempt.addr - addr : addr - attempt.addr; if ( !found_any|| distance < best_distance ) found_any = true, best_distance = distance, best = attempt; attempt.addr = gap.addr + gap.size - size; attempt.size = size; - attempt.prot = 0; distance = addr < attempt.addr ? attempt.addr - addr : addr - attempt.addr; if ( !found_any|| distance < best_distance ) found_any = true, best_distance = distance, best = attempt; @@ -244,4 +247,31 @@ bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr, return *solution = best, found_any; } +void UnmapSegment(Segment* segment) +{ + UnmapSegmentRange(segment, 0, segment->size); +} + +void UnmapSegmentRange(Segment* segment, size_t offset, size_t size) +{ + assert(offset <= segment->size); + assert(size <= segment->size - offset); + if ( !size ) + return; + if ( segment->desc ) + { + for ( size_t i = 0; i < size; i += Page::Size() ) + Memory::Unmap(segment->addr + offset + i); + Memory::Flush(); + ioctx_t ctx; SetupUserIOCtx(&ctx); + for ( size_t i = 0; i < size; i += Page::Size() ) + segment->desc->munmap(&ctx, segment->offset + i); + } + else + { + Memory::UnmapRange(segment->addr, segment->size, PAGE_USAGE_USER_SPACE); + Memory::Flush(); + } +} + } // namespace Sortix diff --git a/kernel/vnode.cpp b/kernel/vnode.cpp index 24042f2d..e0f63b86 100644 --- a/kernel/vnode.cpp +++ b/kernel/vnode.cpp @@ -452,4 +452,19 @@ int Vnode::tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) return inode->tcsetattr(ctx, actions, tio); } +addr_t Vnode::mmap(ioctx_t* ctx, off_t off) +{ + return inode->mmap(ctx, off); +} + +void Vnode::munmap(ioctx_t* ctx, off_t off) +{ + return inode->munmap(ctx, off); +} + +int Vnode::mprotect(ioctx_t* ctx, int prot) +{ + return inode->mprotect(ctx, prot); +} + } // namespace Sortix diff --git a/regress/Makefile b/regress/Makefile index c73dfd98..8a896ed3 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -16,6 +16,7 @@ regress \ TESTS:=\ test-fmemopen \ +test-mmap-anon-shared \ test-pthread-argv \ test-pthread-basic \ test-pthread-main-join \ diff --git a/regress/test-mmap-anon-shared.c b/regress/test-mmap-anon-shared.c new file mode 100644 index 00000000..effb00f2 --- /dev/null +++ b/regress/test-mmap-anon-shared.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * test-mmap-anon-shared.c + * Tests whether anonymous shared memory works. + */ + +#include +#include + +#include + +#include "test.h" + +int main(void) +{ + const char* magic = "Tests whether anonymous shared memory works"; + size_t pagesize = getpagesize(); + test_assert(strlen(magic) < pagesize); + + void* shared = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ( shared == MAP_FAILED ) + test_error(errno, "mmap(MAP_SHARED | MAP_ANONYMOUS)"); + + pid_t child = fork(); + if ( child < 0 ) + test_error(errno, "fork"); + + if ( child == 0 ) + { + strlcpy((char*) shared, magic, pagesize); + _exit(0); + } + + int status; + waitpid(child, &status, 0); + + test_assert(strncmp((const char*) shared, magic, pagesize) == 0); + + if ( munmap(shared, pagesize) < 0 ) + test_error(errno, "munmap"); + + return 0; +}