diff --git a/sortix/Makefile b/sortix/Makefile index 8645d9cb..8c1df59f 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -117,6 +117,7 @@ poll.o \ process.o \ refcount.o \ scheduler.o \ +segment.o \ serialterminal.o \ signal.o \ sound.o \ diff --git a/sortix/elf.cpp b/sortix/elf.cpp index 236e0b85..81e8733e 100644 --- a/sortix/elf.cpp +++ b/sortix/elf.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include "elf.h" @@ -42,25 +44,6 @@ namespace Sortix { namespace ELF { -static int ToProgramSectionType(int flags) -{ - switch ( flags & (PF_X | PF_R | PF_W) ) - { - case 0: - return SEG_NONE; - case PF_X: - case PF_X | PF_R: - case PF_X | PF_W: - case PF_X | PF_R | PF_W: - return SEG_TEXT; - case PF_R: - case PF_W: - case PF_R | PF_W: - default: - return SEG_DATA; - } -} - // TODO: This code doesn't respect that the size of program headers and section // headers may vary depending on the ELF header and that using a simple // table indexation isn't enough. @@ -114,34 +97,47 @@ addr_t Construct32(Process* process, const uint8_t* file, size_t filelen) assert(pht->offset + pht->filesize < filelen); assert(pht->filesize <= pht->memorysize); - ProcessSegment* segment = new ProcessSegment; - if ( segment == NULL ) - return 0; - segment->position = mapto; - segment->size = Page::AlignUp(mapbytes); - segment->type = ToProgramSectionType(pht->flags); - int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE; if ( pht->flags & PF_X ) { prot |= PROT_EXEC; } if ( pht->flags & PF_R ) { prot |= PROT_READ; } if ( pht->flags & PF_W ) { prot |= PROT_WRITE; } - if ( segment->Intersects(process->segments) ) + if ( (pht->flags & (PF_X | PF_R | PF_W)) == (PF_R | PF_W) ) + prot |= PROT_HEAP; + + struct segment segment; + segment.addr = mapto; + segment.size = Page::AlignUp(mapbytes); + segment.prot = prot; + + kthread_mutex_lock(&process->segment_lock); + + if ( !IsUserspaceSegment(&segment) || + IsSegmentOverlapping(process, &segment) ) { - delete segment; + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); return 0; } - if ( !Memory::MapRange(mapto, mapbytes, prot) ) - // TODO: Memory leak of segment? - return 0; + assert(process == CurrentProcess()); - // Insert our newly allocated memory into the processes segment - // list such that it can be reclaimed later. - if ( process->segments ) - process->segments->prev = segment; - segment->next = process->segments; - process->segments = segment; + if ( !Memory::MapRange(segment.addr, segment.size, prot) ) + { + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); + return 0; + } + + if ( !AddSegment(process, &segment) ) + { + Memory::UnmapRange(segment.addr, segment.size); + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); + return 0; + } + + kthread_mutex_unlock(&process->segment_lock); // Copy as much data as possible and memset the rest to 0. uint8_t* memdest = (uint8_t*) virtualaddr; @@ -269,36 +265,47 @@ addr_t Construct64(Process* process, const uint8_t* file, size_t filelen) assert(pht->offset + pht->filesize < filelen); assert(pht->filesize <= pht->memorysize); - ProcessSegment* segment = new ProcessSegment; - if ( segment == NULL ) - return 0; - segment->position = mapto; - segment->size = Page::AlignUp(mapbytes); - segment->type = ToProgramSectionType(pht->flags); - int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE; if ( pht->flags & PF_X ) { prot |= PROT_EXEC; } if ( pht->flags & PF_R ) { prot |= PROT_READ; } if ( pht->flags & PF_W ) { prot |= PROT_WRITE; } - if ( segment->Intersects(process->segments) ) + if ( (pht->flags & (PF_X | PF_R | PF_W)) == (PF_R | PF_W) ) + prot |= PROT_HEAP; + + struct segment segment; + segment.addr = mapto; + segment.size = Page::AlignUp(mapbytes); + segment.prot = prot; + + kthread_mutex_lock(&process->segment_lock); + + if ( !IsUserspaceSegment(&segment) || + IsSegmentOverlapping(process, &segment) ) { - delete segment; + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); return 0; } - if ( !Memory::MapRange(mapto, mapbytes, prot) ) + assert(process == CurrentProcess()); + + if ( !Memory::MapRange(segment.addr, segment.size, prot) ) { - // TODO: Memory leak of segment? + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); return 0; } - // Insert our newly allocated memory into the processes segment - // list such that it can be reclaimed later. - if ( process->segments ) - process->segments->prev = segment; - segment->next = process->segments; - process->segments = segment; + if ( !AddSegment(process, &segment) ) + { + Memory::UnmapRange(segment.addr, segment.size); + kthread_mutex_unlock(&process->segment_lock); + process->ResetAddressSpace(); + return 0; + } + + kthread_mutex_unlock(&process->segment_lock); // Copy as much data as possible and memset the rest to 0. uint8_t* memdest = (uint8_t*) virtualaddr; diff --git a/sortix/include/sortix/kernel/process.h b/sortix/include/sortix/kernel/process.h index 78d6c334..f0855e64 100644 --- a/sortix/include/sortix/kernel/process.h +++ b/sortix/include/sortix/kernel/process.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -50,30 +51,6 @@ struct ioctx_struct; typedef struct ioctx_struct ioctx_t; struct Symbol; -const int SEG_NONE = 0; -const int SEG_TEXT = 1; -const int SEG_DATA = 2; -const int SEG_STACK = 3; -const int SEG_OTHER = 4; - -struct ProcessSegment -{ -public: - ProcessSegment() : prev(NULL), next(NULL) { } - -public: - ProcessSegment* prev; - ProcessSegment* next; - addr_t position; - size_t size; - int type; - -public: - bool Intersects(ProcessSegment* segments); - ProcessSegment* Fork(); - -}; - class Process { friend void Process__OnLastThreadExit(void*); @@ -156,7 +133,10 @@ public: kthread_mutex_t threadlock; public: - ProcessSegment* segments; + struct segment* segments; + size_t segments_used; + size_t segments_length; + kthread_mutex_t segment_lock; public: kthread_mutex_t user_timers_lock; diff --git a/sortix/include/sortix/kernel/segment.h b/sortix/include/sortix/kernel/segment.h new file mode 100644 index 00000000..81e82459 --- /dev/null +++ b/sortix/include/sortix/kernel/segment.h @@ -0,0 +1,60 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/segment.h + Structure representing a segment in a process. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_SEGMENT_H +#define INCLUDE_SORTIX_KERNEL_SEGMENT_H + +namespace Sortix { + +class Process; + +struct segment +{ + uintptr_t addr; + size_t size; + int prot; +}; + +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; + return a->addr < b->addr ? -1 : + b->addr < a->addr ? 1 : + a->size < b->size ? -1 : + b->size < a->size ? 1 : + 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); + +} // namespace Sortix + +#endif diff --git a/sortix/include/sortix/mman.h b/sortix/include/sortix/mman.h index 412b0058..2cb20eae 100644 --- a/sortix/include/sortix/mman.h +++ b/sortix/include/sortix/mman.h @@ -43,6 +43,7 @@ #define PROT_KERNEL (PROT_KEXEC | PROT_KWRITE | PROT_KREAD) #define PROT_FORK (1<<6) +#define PROT_HEAP (1<<7) #define MAP_SHARED (1<<0) #define MAP_PRIVATE (1<<1) diff --git a/sortix/process.cpp b/sortix/process.cpp index 71d22f8e..ef1f2ab2 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -60,53 +61,6 @@ namespace Sortix { -bool ProcessSegment::Intersects(ProcessSegment* segments) -{ - for ( ProcessSegment* tmp = segments; tmp != NULL; tmp = tmp->next ) - { - if ( tmp->position < position + size && - position < tmp->position + tmp->size ) - { - return true; - } - } - - if ( next ) { return next->Intersects(segments); } - - return false; -} - -ProcessSegment* ProcessSegment::Fork() -{ - ProcessSegment* nextclone = NULL; - if ( next ) - { - nextclone = next->Fork(); - if ( nextclone == NULL ) { return NULL; } - } - - ProcessSegment* clone = new ProcessSegment(); - if ( clone == NULL ) - { - while ( nextclone != NULL ) - { - ProcessSegment* todelete = nextclone; - nextclone = nextclone->next; - delete todelete; - } - - return NULL; - } - - if ( nextclone ) - nextclone->prev = clone; - clone->next = nextclone; - clone->position = position; - clone->size = size; - - return clone; -} - Process::Process() { string_table = NULL; @@ -140,6 +94,10 @@ Process::Process() ptrlock = KTHREAD_MUTEX_INITIALIZER; idlock = KTHREAD_MUTEX_INITIALIZER; user_timers_lock = KTHREAD_MUTEX_INITIALIZER; + segments = NULL; + segments_used = 0; + segments_length = 0; + segment_lock = KTHREAD_MUTEX_INITIALIZER; mmapfrom = 0x80000000UL; exitstatus = -1; pid = AllocatePID(); @@ -370,16 +328,17 @@ void Process::LastPrayer() void Process::ResetAddressSpace() { - assert(Memory::GetAddressSpace() == addrspace); - ProcessSegment* tmp = segments; - while ( tmp != NULL ) - { - Memory::UnmapRange(tmp->position, tmp->size); - ProcessSegment* todelete = tmp; - tmp = tmp->next; - delete todelete; - } + ScopedLock lock(&segment_lock); + assert(Memory::GetAddressSpace() == addrspace); + + for ( size_t i = 0; i < segments_used; i++ ) + Memory::UnmapRange(segments[i].addr, segments[i].size); + + Memory::Flush(); + + segments_used = segments_length = 0; + free(segments); segments = NULL; } @@ -645,34 +604,34 @@ Process* Process::Fork() if ( !clone ) return NULL; - ProcessSegment* clonesegments = NULL; + struct segment* clone_segments = NULL; // Fork the segment list. if ( segments ) { - clonesegments = segments->Fork(); - if ( clonesegments == NULL ) { delete clone; return NULL; } + size_t segments_size = sizeof(struct segment) * segments_used; + if ( !(clone_segments = (struct segment*) malloc(segments_size)) ) + { + delete clone; + return NULL; + } + memcpy(clone_segments, segments, segments_size); } // Fork address-space here and copy memory. clone->addrspace = Memory::Fork(); if ( !clone->addrspace ) { - // Delete the segment list, since they are currently bogus. - ProcessSegment* tmp = clonesegments; - while ( tmp != NULL ) - { - ProcessSegment* todelete = tmp; - tmp = tmp->next; - delete todelete; - } - - delete clone; return NULL; + free(clone_segments); + delete clone; + return NULL; } // Now it's too late to clean up here, if anything goes wrong, we simply // ask the process to commit suicide before it goes live. - clone->segments = clonesegments; + clone->segments = clone_segments; + clone->segments_used = segments_used; + clone->segments_length = segments_used; // Remember the relation to the child process. AddChildProcess(clone); @@ -759,8 +718,6 @@ Process* Process::Fork() void Process::ResetForExecute() { - // TODO: Delete all threads and their stacks. - string_table_length = 0; symbol_table_length = 0; delete[] string_table; string_table = NULL; @@ -1145,48 +1102,60 @@ void Process::Remove(Process* process) pidlist->Remove(index); } -void* sys_sbrk(intptr_t increment) +static void* sys_sbrk(intptr_t increment) { Process* process = CurrentProcess(); - ProcessSegment* dataseg = NULL; - for ( ProcessSegment* iter = process->segments; iter; iter = iter->next ) + ScopedLock lock(&process->segment_lock); + + // Locate the heap segment. + struct segment* heap_segment = NULL; + for ( size_t i = process->segments_used; !heap_segment && i != 0; i-- ) { - if ( !iter->type == SEG_DATA ) + struct segment* candidate = &process->segments[i-1]; + if ( !(candidate->prot & PROT_HEAP) ) continue; - if ( dataseg && iter->position < dataseg->position ) - continue; - dataseg = iter; + heap_segment = candidate; } - if ( !dataseg ) + if ( !heap_segment ) return errno = ENOMEM, (void*) -1UL; - addr_t currentend = dataseg->position + dataseg->size; - addr_t newend = currentend + increment; - if ( newend < dataseg->position ) - return errno = EINVAL, (void*) -1UL; - if ( newend < currentend ) + + assert(IsUserspaceSegment(heap_segment)); + + // Decrease the size of the heap segment if requested. + if ( increment < 0 ) { - addr_t unmapfrom = Page::AlignUp(newend); - if ( unmapfrom < currentend ) - { - size_t unmapbytes = Page::AlignUp(currentend - unmapfrom); - Memory::UnmapRange(unmapfrom, unmapbytes); - } + uintptr_t abs_amount = Page::AlignDown(- (uintptr_t) increment); + if ( heap_segment->size < abs_amount ) + abs_amount = heap_segment->size; + uintptr_t new_end = heap_segment->addr + heap_segment->size - abs_amount; + Memory::UnmapRange(new_end, abs_amount); + heap_segment->size -= abs_amount; + // TODO: How do we handle that the heap shrinks to 0 bytes? } - else if ( currentend < newend ) + + // Increase the size of the heap if requested. + if ( 0 < increment ) { - // TODO: HACK: Make a safer way of expanding the data segment - // without segments possibly colliding! - addr_t mapfrom = Page::AlignUp(currentend); - if ( mapfrom < newend ) - { - size_t mapbytes = Page::AlignUp(newend - mapfrom); - int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; - if ( !Memory::MapRange(mapfrom, mapbytes, prot) ) - return (void*) -1UL; - } + uintptr_t abs_amount = Page::AlignUp(increment); + uintptr_t max_growth = 0 - (heap_segment->addr + heap_segment->size); + if ( max_growth < abs_amount ) + return errno = ENOMEM, (void*) -1UL; + struct segment growth; + growth.addr = heap_segment->addr + heap_segment->size; + growth.size = abs_amount; + growth.prot = heap_segment->prot; + if ( !IsUserspaceSegment(&growth) ) + return errno = ENOMEM, (void*) -1UL; + if ( FindOverlappingSegment(process, &growth) ) + return errno = ENOMEM, (void*) -1UL; + if ( !Memory::MapRange(growth.addr, growth.size, growth.prot) ) + return errno = ENOMEM, (void*) -1UL; + heap_segment->size += growth.size; } - dataseg->size += increment; - return (void*) newend; + + assert(IsUserspaceSegment(heap_segment)); + + return (void*) (heap_segment->addr + heap_segment->size); } size_t sys_getpagesize() diff --git a/sortix/segment.cpp b/sortix/segment.cpp new file mode 100644 index 00000000..2b91b218 --- /dev/null +++ b/sortix/segment.cpp @@ -0,0 +1,108 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + segment.cpp + Structure representing a segment in a process. + +*******************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace Sortix { + +bool AreSegmentsOverlapping(const struct segment* a, const struct segment* b) +{ + return a->addr < b->addr + b->size && b->addr < a->addr + a->size; +} + +bool IsUserspaceSegment(const struct segment* segment) +{ + uintptr_t userspace_addr; + size_t userspace_size; + Memory::GetUserVirtualArea(&userspace_addr, &userspace_size); + if ( segment->addr < userspace_addr ) + return false; + uintptr_t userspace_end = userspace_addr + userspace_size; + if ( userspace_end - segment->addr < segment->size ) + return false; + return true; +} + +struct segment* FindOverlappingSegment(Process* process, const struct segment* new_segment) +{ + // 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) ) + return segment; + } + + return NULL; +} + +bool IsSegmentOverlapping(Process* process, const struct segment* new_segment) +{ + // process->segment_lock is held at this point. + + return FindOverlappingSegment(process, new_segment) != NULL; +} + +bool AddSegment(Process* process, const struct segment* new_segment) +{ + // process->segment_lock is held at this point. + + // assert(!IsSegmentOverlapping(new_segment)); + + // Check if we need to expand the segment list. + if ( process->segments_used == process->segments_length ) + { + 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); + if ( !new_segments ) + return false; + process->segments = new_segments; + process->segments_length = new_length; + } + + // Add the new segment to the segment list. + process->segments[process->segments_used++] = *new_segment; + + // Sort the segment list after address. + qsort(process->segments, process->segments_used, sizeof(struct segment), + segmentcmp); + + return true; +} + +} // namespace Sortix