Refactor kernel segment bookkeeping.
This commit is contained in:
parent
68aba3d137
commit
d71179b540
|
@ -117,6 +117,7 @@ poll.o \
|
||||||
process.o \
|
process.o \
|
||||||
refcount.o \
|
refcount.o \
|
||||||
scheduler.o \
|
scheduler.o \
|
||||||
|
segment.o \
|
||||||
serialterminal.o \
|
serialterminal.o \
|
||||||
signal.o \
|
signal.o \
|
||||||
sound.o \
|
sound.o \
|
||||||
|
|
115
sortix/elf.cpp
115
sortix/elf.cpp
|
@ -28,6 +28,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sortix/mman.h>
|
#include <sortix/mman.h>
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#include <sortix/kernel/platform.h>
|
#include <sortix/kernel/platform.h>
|
||||||
#include <sortix/kernel/memorymanagement.h>
|
#include <sortix/kernel/memorymanagement.h>
|
||||||
#include <sortix/kernel/process.h>
|
#include <sortix/kernel/process.h>
|
||||||
|
#include <sortix/kernel/segment.h>
|
||||||
#include <sortix/kernel/symbol.h>
|
#include <sortix/kernel/symbol.h>
|
||||||
|
|
||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
|
@ -42,25 +44,6 @@
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
namespace ELF {
|
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
|
// 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
|
// headers may vary depending on the ELF header and that using a simple
|
||||||
// table indexation isn't enough.
|
// 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->offset + pht->filesize < filelen);
|
||||||
assert(pht->filesize <= pht->memorysize);
|
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;
|
int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE;
|
||||||
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
||||||
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
||||||
if ( pht->flags & PF_W ) { prot |= PROT_WRITE; }
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !Memory::MapRange(mapto, mapbytes, prot) )
|
assert(process == CurrentProcess());
|
||||||
// TODO: Memory leak of segment?
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Insert our newly allocated memory into the processes segment
|
if ( !Memory::MapRange(segment.addr, segment.size, prot) )
|
||||||
// list such that it can be reclaimed later.
|
{
|
||||||
if ( process->segments )
|
kthread_mutex_unlock(&process->segment_lock);
|
||||||
process->segments->prev = segment;
|
process->ResetAddressSpace();
|
||||||
segment->next = process->segments;
|
return 0;
|
||||||
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.
|
// Copy as much data as possible and memset the rest to 0.
|
||||||
uint8_t* memdest = (uint8_t*) virtualaddr;
|
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->offset + pht->filesize < filelen);
|
||||||
assert(pht->filesize <= pht->memorysize);
|
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;
|
int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE;
|
||||||
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
||||||
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
||||||
if ( pht->flags & PF_W ) { prot |= PROT_WRITE; }
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert our newly allocated memory into the processes segment
|
if ( !AddSegment(process, &segment) )
|
||||||
// list such that it can be reclaimed later.
|
{
|
||||||
if ( process->segments )
|
Memory::UnmapRange(segment.addr, segment.size);
|
||||||
process->segments->prev = segment;
|
kthread_mutex_unlock(&process->segment_lock);
|
||||||
segment->next = process->segments;
|
process->ResetAddressSpace();
|
||||||
process->segments = segment;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kthread_mutex_unlock(&process->segment_lock);
|
||||||
|
|
||||||
// Copy as much data as possible and memset the rest to 0.
|
// Copy as much data as possible and memset the rest to 0.
|
||||||
uint8_t* memdest = (uint8_t*) virtualaddr;
|
uint8_t* memdest = (uint8_t*) virtualaddr;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <sortix/kernel/clock.h>
|
#include <sortix/kernel/clock.h>
|
||||||
#include <sortix/kernel/kthread.h>
|
#include <sortix/kernel/kthread.h>
|
||||||
#include <sortix/kernel/refcount.h>
|
#include <sortix/kernel/refcount.h>
|
||||||
|
#include <sortix/kernel/segment.h>
|
||||||
#include <sortix/kernel/time.h>
|
#include <sortix/kernel/time.h>
|
||||||
#include <sortix/kernel/timer.h>
|
#include <sortix/kernel/timer.h>
|
||||||
#include <sortix/kernel/user-timer.h>
|
#include <sortix/kernel/user-timer.h>
|
||||||
|
@ -50,30 +51,6 @@ struct ioctx_struct;
|
||||||
typedef struct ioctx_struct ioctx_t;
|
typedef struct ioctx_struct ioctx_t;
|
||||||
struct Symbol;
|
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
|
class Process
|
||||||
{
|
{
|
||||||
friend void Process__OnLastThreadExit(void*);
|
friend void Process__OnLastThreadExit(void*);
|
||||||
|
@ -156,7 +133,10 @@ public:
|
||||||
kthread_mutex_t threadlock;
|
kthread_mutex_t threadlock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ProcessSegment* segments;
|
struct segment* segments;
|
||||||
|
size_t segments_used;
|
||||||
|
size_t segments_length;
|
||||||
|
kthread_mutex_t segment_lock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
kthread_mutex_t user_timers_lock;
|
kthread_mutex_t user_timers_lock;
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
|
@ -43,6 +43,7 @@
|
||||||
#define PROT_KERNEL (PROT_KEXEC | PROT_KWRITE | PROT_KREAD)
|
#define PROT_KERNEL (PROT_KEXEC | PROT_KWRITE | PROT_KREAD)
|
||||||
|
|
||||||
#define PROT_FORK (1<<6)
|
#define PROT_FORK (1<<6)
|
||||||
|
#define PROT_HEAP (1<<7)
|
||||||
|
|
||||||
#define MAP_SHARED (1<<0)
|
#define MAP_SHARED (1<<0)
|
||||||
#define MAP_PRIVATE (1<<1)
|
#define MAP_PRIVATE (1<<1)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sortix/clock.h>
|
#include <sortix/clock.h>
|
||||||
|
@ -60,53 +61,6 @@
|
||||||
|
|
||||||
namespace Sortix {
|
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()
|
Process::Process()
|
||||||
{
|
{
|
||||||
string_table = NULL;
|
string_table = NULL;
|
||||||
|
@ -140,6 +94,10 @@ Process::Process()
|
||||||
ptrlock = KTHREAD_MUTEX_INITIALIZER;
|
ptrlock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
idlock = KTHREAD_MUTEX_INITIALIZER;
|
idlock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
|
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
|
segments = NULL;
|
||||||
|
segments_used = 0;
|
||||||
|
segments_length = 0;
|
||||||
|
segment_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
mmapfrom = 0x80000000UL;
|
mmapfrom = 0x80000000UL;
|
||||||
exitstatus = -1;
|
exitstatus = -1;
|
||||||
pid = AllocatePID();
|
pid = AllocatePID();
|
||||||
|
@ -370,16 +328,17 @@ void Process::LastPrayer()
|
||||||
|
|
||||||
void Process::ResetAddressSpace()
|
void Process::ResetAddressSpace()
|
||||||
{
|
{
|
||||||
assert(Memory::GetAddressSpace() == addrspace);
|
ScopedLock lock(&segment_lock);
|
||||||
ProcessSegment* tmp = segments;
|
|
||||||
while ( tmp != NULL )
|
|
||||||
{
|
|
||||||
Memory::UnmapRange(tmp->position, tmp->size);
|
|
||||||
ProcessSegment* todelete = tmp;
|
|
||||||
tmp = tmp->next;
|
|
||||||
delete todelete;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
segments = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,34 +604,34 @@ Process* Process::Fork()
|
||||||
if ( !clone )
|
if ( !clone )
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ProcessSegment* clonesegments = NULL;
|
struct segment* clone_segments = NULL;
|
||||||
|
|
||||||
// Fork the segment list.
|
// Fork the segment list.
|
||||||
if ( segments )
|
if ( segments )
|
||||||
{
|
{
|
||||||
clonesegments = segments->Fork();
|
size_t segments_size = sizeof(struct segment) * segments_used;
|
||||||
if ( clonesegments == NULL ) { delete clone; return NULL; }
|
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.
|
// Fork address-space here and copy memory.
|
||||||
clone->addrspace = Memory::Fork();
|
clone->addrspace = Memory::Fork();
|
||||||
if ( !clone->addrspace )
|
if ( !clone->addrspace )
|
||||||
{
|
{
|
||||||
// Delete the segment list, since they are currently bogus.
|
free(clone_segments);
|
||||||
ProcessSegment* tmp = clonesegments;
|
delete clone;
|
||||||
while ( tmp != NULL )
|
return NULL;
|
||||||
{
|
|
||||||
ProcessSegment* todelete = tmp;
|
|
||||||
tmp = tmp->next;
|
|
||||||
delete todelete;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete clone; return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now it's too late to clean up here, if anything goes wrong, we simply
|
// 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.
|
// 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.
|
// Remember the relation to the child process.
|
||||||
AddChildProcess(clone);
|
AddChildProcess(clone);
|
||||||
|
@ -759,8 +718,6 @@ Process* Process::Fork()
|
||||||
|
|
||||||
void Process::ResetForExecute()
|
void Process::ResetForExecute()
|
||||||
{
|
{
|
||||||
// TODO: Delete all threads and their stacks.
|
|
||||||
|
|
||||||
string_table_length = 0;
|
string_table_length = 0;
|
||||||
symbol_table_length = 0;
|
symbol_table_length = 0;
|
||||||
delete[] string_table; string_table = NULL;
|
delete[] string_table; string_table = NULL;
|
||||||
|
@ -1145,48 +1102,60 @@ void Process::Remove(Process* process)
|
||||||
pidlist->Remove(index);
|
pidlist->Remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* sys_sbrk(intptr_t increment)
|
static void* sys_sbrk(intptr_t increment)
|
||||||
{
|
{
|
||||||
Process* process = CurrentProcess();
|
Process* process = CurrentProcess();
|
||||||
ProcessSegment* dataseg = NULL;
|
ScopedLock lock(&process->segment_lock);
|
||||||
for ( ProcessSegment* iter = process->segments; iter; iter = iter->next )
|
|
||||||
|
// 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;
|
continue;
|
||||||
if ( dataseg && iter->position < dataseg->position )
|
heap_segment = candidate;
|
||||||
continue;
|
|
||||||
dataseg = iter;
|
|
||||||
}
|
}
|
||||||
if ( !dataseg )
|
if ( !heap_segment )
|
||||||
return errno = ENOMEM, (void*) -1UL;
|
return errno = ENOMEM, (void*) -1UL;
|
||||||
addr_t currentend = dataseg->position + dataseg->size;
|
|
||||||
addr_t newend = currentend + increment;
|
assert(IsUserspaceSegment(heap_segment));
|
||||||
if ( newend < dataseg->position )
|
|
||||||
return errno = EINVAL, (void*) -1UL;
|
// Decrease the size of the heap segment if requested.
|
||||||
if ( newend < currentend )
|
if ( increment < 0 )
|
||||||
{
|
{
|
||||||
addr_t unmapfrom = Page::AlignUp(newend);
|
uintptr_t abs_amount = Page::AlignDown(- (uintptr_t) increment);
|
||||||
if ( unmapfrom < currentend )
|
if ( heap_segment->size < abs_amount )
|
||||||
{
|
abs_amount = heap_segment->size;
|
||||||
size_t unmapbytes = Page::AlignUp(currentend - unmapfrom);
|
uintptr_t new_end = heap_segment->addr + heap_segment->size - abs_amount;
|
||||||
Memory::UnmapRange(unmapfrom, unmapbytes);
|
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
|
uintptr_t abs_amount = Page::AlignUp(increment);
|
||||||
// without segments possibly colliding!
|
uintptr_t max_growth = 0 - (heap_segment->addr + heap_segment->size);
|
||||||
addr_t mapfrom = Page::AlignUp(currentend);
|
if ( max_growth < abs_amount )
|
||||||
if ( mapfrom < newend )
|
return errno = ENOMEM, (void*) -1UL;
|
||||||
{
|
struct segment growth;
|
||||||
size_t mapbytes = Page::AlignUp(newend - mapfrom);
|
growth.addr = heap_segment->addr + heap_segment->size;
|
||||||
int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE;
|
growth.size = abs_amount;
|
||||||
if ( !Memory::MapRange(mapfrom, mapbytes, prot) )
|
growth.prot = heap_segment->prot;
|
||||||
return (void*) -1UL;
|
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()
|
size_t sys_getpagesize()
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
segment.cpp
|
||||||
|
Structure representing a segment in a process.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <sortix/kernel/decl.h>
|
||||||
|
#include <sortix/kernel/memorymanagement.h>
|
||||||
|
#include <sortix/kernel/process.h>
|
||||||
|
#include <sortix/kernel/segment.h>
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in New Issue